feat: menu header is controlled from menuList rather than boolean for each menu (#3288)

* feat: create alerts method is added

* feat: method is updated for onClick

* chore: create alerts is udpated

* chore: header menu list is updated

* chore: headerMenuList is made optional

* chore: default props is updated
This commit is contained in:
Palash Gupta 2023-08-10 11:44:36 +05:30 committed by GitHub
parent 900752b6e2
commit 3b22698e35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 70 additions and 127 deletions

View File

@ -53,9 +53,7 @@ function WidgetGraphComponent({
setLayout, setLayout,
onDragSelect, onDragSelect,
onClickHandler, onClickHandler,
allowClone = true, headerMenuList,
allowDelete = true,
allowEdit = true,
}: WidgetGraphComponentProps): JSX.Element { }: WidgetGraphComponentProps): JSX.Element {
const [deleteModal, setDeleteModal] = useState(false); const [deleteModal, setDeleteModal] = useState(false);
const [modal, setModal] = useState<boolean>(false); const [modal, setModal] = useState<boolean>(false);
@ -281,9 +279,7 @@ function WidgetGraphComponent({
onClone={onCloneHandler} onClone={onCloneHandler}
queryResponse={queryResponse} queryResponse={queryResponse}
errorMessage={errorMessage} errorMessage={errorMessage}
allowClone={allowClone} headerMenuList={headerMenuList}
allowDelete={allowDelete}
allowEdit={allowEdit}
/> />
</div> </div>
)} )}
@ -313,9 +309,6 @@ WidgetGraphComponent.defaultProps = {
setLayout: undefined, setLayout: undefined,
onDragSelect: undefined, onDragSelect: undefined,
onClickHandler: undefined, onClickHandler: undefined,
allowDelete: true,
allowClone: true,
allowEdit: true,
}; };
const mapDispatchToProps = ( const mapDispatchToProps = (

View File

@ -15,6 +15,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { getSelectedDashboardVariable } from 'utils/dashboard/selectedDashboard'; import { getSelectedDashboardVariable } from 'utils/dashboard/selectedDashboard';
import EmptyWidget from '../EmptyWidget'; import EmptyWidget from '../EmptyWidget';
import { MenuItemKeys } from '../WidgetHeader/contants';
import { GridCardGraphProps } from './types'; import { GridCardGraphProps } from './types';
import WidgetGraphComponent from './WidgetGraphComponent'; import WidgetGraphComponent from './WidgetGraphComponent';
@ -26,9 +27,7 @@ function GridCardGraph({
setLayout, setLayout,
onDragSelect, onDragSelect,
onClickHandler, onClickHandler,
allowDelete, headerMenuList = [MenuItemKeys.View],
allowClone,
allowEdit,
isQueryEnabled, isQueryEnabled,
}: GridCardGraphProps): JSX.Element { }: GridCardGraphProps): JSX.Element {
const { isAddWidget } = useSelector<AppState, DashboardReducer>( const { isAddWidget } = useSelector<AppState, DashboardReducer>(
@ -121,9 +120,7 @@ function GridCardGraph({
yAxisUnit={yAxisUnit} yAxisUnit={yAxisUnit}
layout={layout} layout={layout}
setLayout={setLayout} setLayout={setLayout}
allowClone={allowClone} headerMenuList={headerMenuList}
allowDelete={allowDelete}
allowEdit={allowEdit}
/> />
)} )}
</span> </span>
@ -145,9 +142,7 @@ function GridCardGraph({
yAxisUnit={yAxisUnit} yAxisUnit={yAxisUnit}
layout={layout} layout={layout}
setLayout={setLayout} setLayout={setLayout}
allowClone={allowClone} headerMenuList={headerMenuList}
allowDelete={allowDelete}
allowEdit={allowEdit}
onClickHandler={onClickHandler} onClickHandler={onClickHandler}
/> />
) : ( ) : (
@ -170,9 +165,7 @@ function GridCardGraph({
name={name} name={name}
yAxisUnit={yAxisUnit} yAxisUnit={yAxisUnit}
onDragSelect={onDragSelect} onDragSelect={onDragSelect}
allowClone={allowClone} headerMenuList={headerMenuList}
allowDelete={allowDelete}
allowEdit={allowEdit}
onClickHandler={onClickHandler} onClickHandler={onClickHandler}
/> />
)} )}
@ -185,10 +178,8 @@ function GridCardGraph({
GridCardGraph.defaultProps = { GridCardGraph.defaultProps = {
onDragSelect: undefined, onDragSelect: undefined,
onClickHandler: undefined, onClickHandler: undefined,
allowDelete: true,
allowClone: true,
allowEdit: true,
isQueryEnabled: true, isQueryEnabled: true,
headerMenuList: [MenuItemKeys.View],
}; };
export default memo(GridCardGraph); export default memo(GridCardGraph);

View File

@ -10,6 +10,7 @@ import { Widgets } from 'types/api/dashboard/getAll';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { LayoutProps } from '..'; import { LayoutProps } from '..';
import { MenuItemKeys } from '../WidgetHeader/contants';
import { LegendEntryProps } from './FullView/types'; import { LegendEntryProps } from './FullView/types';
export interface GraphVisibilityLegendEntryProps { export interface GraphVisibilityLegendEntryProps {
@ -38,25 +39,19 @@ export interface WidgetGraphComponentProps extends DispatchProps {
setLayout?: Dispatch<SetStateAction<LayoutProps[]>>; setLayout?: Dispatch<SetStateAction<LayoutProps[]>>;
onDragSelect?: (start: number, end: number) => void; onDragSelect?: (start: number, end: number) => void;
onClickHandler?: GraphOnClickHandler; onClickHandler?: GraphOnClickHandler;
allowDelete?: boolean; headerMenuList: MenuItemKeys[];
allowClone?: boolean;
allowEdit?: boolean;
} }
export interface GridCardGraphProps { export interface GridCardGraphProps {
widget: Widgets; widget: Widgets;
name: string; name: string;
yAxisUnit: string | undefined; yAxisUnit: string | undefined;
// eslint-disable-next-line react/require-default-props
layout?: Layout[]; layout?: Layout[];
// eslint-disable-next-line react/require-default-props
setLayout?: Dispatch<SetStateAction<LayoutProps[]>>; setLayout?: Dispatch<SetStateAction<LayoutProps[]>>;
onDragSelect?: (start: number, end: number) => void; onDragSelect?: (start: number, end: number) => void;
onClickHandler?: GraphOnClickHandler; onClickHandler?: GraphOnClickHandler;
allowDelete?: boolean; headerMenuList?: WidgetGraphComponentProps['headerMenuList'];
allowClone?: boolean; isQueryEnabled: boolean;
allowEdit?: boolean;
isQueryEnabled?: boolean;
} }
export interface GetGraphVisibilityStateOnLegendClickProps { export interface GetGraphVisibilityStateOnLegendClickProps {

View File

@ -3,6 +3,7 @@ export enum MenuItemKeys {
Edit = 'edit', Edit = 'edit',
Delete = 'delete', Delete = 'delete',
Clone = 'clone', Clone = 'clone',
CreateAlerts = 'createAlerts',
} }
export const MENUITEM_KEYS_VS_LABELS = { export const MENUITEM_KEYS_VS_LABELS = {
@ -10,4 +11,5 @@ export const MENUITEM_KEYS_VS_LABELS = {
[MenuItemKeys.Edit]: 'Edit', [MenuItemKeys.Edit]: 'Edit',
[MenuItemKeys.Delete]: 'Delete', [MenuItemKeys.Delete]: 'Delete',
[MenuItemKeys.Clone]: 'Clone', [MenuItemKeys.Clone]: 'Clone',
[MenuItemKeys.CreateAlerts]: 'Create Alerts',
}; };

View File

@ -7,9 +7,9 @@ import {
FullscreenOutlined, FullscreenOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { Dropdown, MenuProps, Tooltip, Typography } from 'antd'; import { Dropdown, MenuProps, Tooltip, Typography } from 'antd';
import { MenuItemType } from 'antd/es/menu/hooks/useItems';
import Spinner from 'components/Spinner'; import Spinner from 'components/Spinner';
import { queryParamNamesMap } from 'constants/queryBuilderQueryNames'; import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
import ROUTES from 'constants/routes';
import useComponentPermission from 'hooks/useComponentPermission'; import useComponentPermission from 'hooks/useComponentPermission';
import history from 'lib/history'; import history from 'lib/history';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
@ -33,7 +33,7 @@ import {
HeaderContainer, HeaderContainer,
HeaderContentContainer, HeaderContentContainer,
} from './styles'; } from './styles';
import { KeyMethodMappingProps, MenuItem, TWidgetOptions } from './types'; import { MenuItem } from './types';
import { generateMenuList, isTWidgetOptions } from './utils'; import { generateMenuList, isTWidgetOptions } from './utils';
interface IWidgetHeaderProps { interface IWidgetHeaderProps {
@ -47,10 +47,9 @@ interface IWidgetHeaderProps {
SuccessResponse<MetricRangePayloadProps> | ErrorResponse SuccessResponse<MetricRangePayloadProps> | ErrorResponse
>; >;
errorMessage: string | undefined; errorMessage: string | undefined;
allowDelete?: boolean; headerMenuList?: MenuItemKeys[];
allowClone?: boolean;
allowEdit?: boolean;
} }
function WidgetHeader({ function WidgetHeader({
title, title,
widget, widget,
@ -60,9 +59,7 @@ function WidgetHeader({
parentHover, parentHover,
queryResponse, queryResponse,
errorMessage, errorMessage,
allowClone = true, headerMenuList,
allowDelete = true,
allowEdit = true,
}: IWidgetHeaderProps): JSX.Element { }: IWidgetHeaderProps): JSX.Element {
const [localHover, setLocalHover] = useState(false); const [localHover, setLocalHover] = useState(false);
const [isOpen, setIsOpen] = useState<boolean>(false); const [isOpen, setIsOpen] = useState<boolean>(false);
@ -78,32 +75,30 @@ function WidgetHeader({
); );
}, [widget.id, widget.panelTypes, widget.query]); }, [widget.id, widget.panelTypes, widget.query]);
const keyMethodMapping: KeyMethodMappingProps<TWidgetOptions> = useMemo( const onCreateAlertsHandler = useCallback(() => {
history.push(
`${ROUTES.ALERTS_NEW}?${
queryParamNamesMap.compositeQuery
}=${encodeURIComponent(JSON.stringify(widget.query))}`,
);
}, [widget]);
const keyMethodMapping = useMemo(
() => ({ () => ({
view: { [MenuItemKeys.View]: onView,
key: MenuItemKeys.View, [MenuItemKeys.Edit]: onEditHandler,
method: onView, [MenuItemKeys.Delete]: onDelete,
}, [MenuItemKeys.Clone]: onClone,
edit: { [MenuItemKeys.CreateAlerts]: onCreateAlertsHandler,
key: MenuItemKeys.Edit,
method: onEditHandler,
},
delete: {
key: MenuItemKeys.Delete,
method: onDelete,
},
clone: {
key: MenuItemKeys.Clone,
method: onClone,
},
}), }),
[onDelete, onEditHandler, onView, onClone], [onDelete, onEditHandler, onView, onClone, onCreateAlertsHandler],
); );
const onMenuItemSelectHandler: MenuProps['onClick'] = useCallback( const onMenuItemSelectHandler: MenuProps['onClick'] = useCallback(
({ key }: { key: string }): void => { ({ key }: { key: string }): void => {
if (isTWidgetOptions(key)) { if (isTWidgetOptions(key)) {
const functionToCall = keyMethodMapping[key]?.method; const functionToCall = keyMethodMapping[key];
if (functionToCall) { if (functionToCall) {
functionToCall(); functionToCall();
setIsOpen(false); setIsOpen(false);
@ -125,46 +120,43 @@ function WidgetHeader({
key: MenuItemKeys.View, key: MenuItemKeys.View,
icon: <FullscreenOutlined />, icon: <FullscreenOutlined />,
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.View], label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.View],
isVisible: true, isVisible: headerMenuList?.includes(MenuItemKeys.View) || false,
disabled: queryResponse.isLoading, disabled: queryResponse.isLoading,
}, },
{ {
key: MenuItemKeys.Edit, key: MenuItemKeys.Edit,
icon: <EditFilled />, icon: <EditFilled />,
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Edit], label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Edit],
isVisible: allowEdit, isVisible: headerMenuList?.includes(MenuItemKeys.Edit) || false,
disabled: !editWidget, disabled: !editWidget,
}, },
{ {
key: MenuItemKeys.Clone, key: MenuItemKeys.Clone,
icon: <CopyOutlined />, icon: <CopyOutlined />,
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Clone], label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Clone],
isVisible: allowClone, isVisible: headerMenuList?.includes(MenuItemKeys.Clone) || false,
disabled: !editWidget, disabled: !editWidget,
}, },
{ {
key: MenuItemKeys.Delete, key: MenuItemKeys.Delete,
icon: <DeleteOutlined />, icon: <DeleteOutlined />,
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Delete], label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Delete],
isVisible: allowDelete, isVisible: headerMenuList?.includes(MenuItemKeys.Delete) || false,
disabled: !deleteWidget, disabled: !deleteWidget,
danger: true, danger: true,
}, },
{
key: MenuItemKeys.CreateAlerts,
icon: <DeleteOutlined />,
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.CreateAlerts],
isVisible: headerMenuList?.includes(MenuItemKeys.CreateAlerts) || false,
disabled: false,
},
], ],
[ [queryResponse.isLoading, headerMenuList, editWidget, deleteWidget],
allowEdit,
allowClone,
allowDelete,
queryResponse.isLoading,
deleteWidget,
editWidget,
],
); );
const menuList: MenuItemType[] = useMemo( const updatedMenuList = useMemo(() => generateMenuList(actions), [actions]);
(): MenuItemType[] => generateMenuList(actions, keyMethodMapping),
[actions, keyMethodMapping],
);
const onClickHandler = useCallback(() => { const onClickHandler = useCallback(() => {
setIsOpen((open) => !open); setIsOpen((open) => !open);
@ -172,10 +164,10 @@ function WidgetHeader({
const menu = useMemo( const menu = useMemo(
() => ({ () => ({
items: menuList, items: updatedMenuList,
onClick: onMenuItemSelectHandler, onClick: onMenuItemSelectHandler,
}), }),
[menuList, onMenuItemSelectHandler], [updatedMenuList, onMenuItemSelectHandler],
); );
return ( return (
@ -219,9 +211,7 @@ function WidgetHeader({
WidgetHeader.defaultProps = { WidgetHeader.defaultProps = {
onDelete: undefined, onDelete: undefined,
onClone: undefined, onClone: undefined,
allowDelete: true, headerMenuList: [MenuItemKeys.View],
allowClone: true,
allowEdit: true,
}; };
export default WidgetHeader; export default WidgetHeader;

View File

@ -3,23 +3,10 @@ import { ReactNode } from 'react';
import { MenuItemKeys } from './contants'; import { MenuItemKeys } from './contants';
export interface MenuItem { export interface MenuItem {
key: TWidgetOptions; key: MenuItemKeys;
icon: ReactNode; icon: ReactNode;
label: string; label: string;
isVisible: boolean; isVisible: boolean;
disabled: boolean; disabled: boolean;
danger?: boolean; danger?: boolean;
} }
export type TWidgetOptions =
| MenuItemKeys.View
| MenuItemKeys.Edit
| MenuItemKeys.Delete
| MenuItemKeys.Clone;
export type KeyMethodMappingProps<T extends TWidgetOptions> = {
[K in T]: {
key: TWidgetOptions;
method?: VoidFunction;
};
};

View File

@ -1,24 +1,22 @@
import { MenuItemType } from 'antd/es/menu/hooks/useItems'; import { MenuItemType } from 'antd/es/menu/hooks/useItems';
import { MenuItemKeys } from './contants'; import { MenuItemKeys } from './contants';
import { KeyMethodMappingProps, MenuItem, TWidgetOptions } from './types'; import { MenuItem } from './types';
export const generateMenuList = ( export const generateMenuList = (actions: MenuItem[]): MenuItemType[] =>
actions: MenuItem[],
keyMethodMapping: KeyMethodMappingProps<TWidgetOptions>,
): MenuItemType[] =>
actions actions
.filter((action: MenuItem) => action.isVisible) .filter((action: MenuItem) => action.isVisible)
.map(({ key, icon: Icon, label, disabled, ...rest }) => ({ .map(({ key, icon: Icon, label, disabled, ...rest }) => ({
key: keyMethodMapping[key].key, key,
icon: Icon, icon: Icon,
label, label,
disabled, disabled,
...rest, ...rest,
})); }));
export const isTWidgetOptions = (value: string): value is TWidgetOptions => export const isTWidgetOptions = (value: string): value is MenuItemKeys =>
value === MenuItemKeys.View || value === MenuItemKeys.View ||
value === MenuItemKeys.Edit || value === MenuItemKeys.Edit ||
value === MenuItemKeys.Delete || value === MenuItemKeys.Delete ||
value === MenuItemKeys.Clone; value === MenuItemKeys.Clone ||
value === MenuItemKeys.CreateAlerts;

View File

@ -0,0 +1,8 @@
import { MenuItemKeys } from 'container/GridGraphLayout/WidgetHeader/contants';
export const headerMenuList = [
MenuItemKeys.View,
MenuItemKeys.Clone,
MenuItemKeys.Delete,
MenuItemKeys.Edit,
];

View File

@ -29,6 +29,7 @@ import { Dashboard, Widgets } from 'types/api/dashboard/getAll';
import AppReducer from 'types/reducer/app'; import AppReducer from 'types/reducer/app';
import DashboardReducer from 'types/reducer/dashboards'; import DashboardReducer from 'types/reducer/dashboards';
import { headerMenuList } from './config';
import Graph from './Graph'; import Graph from './Graph';
import GraphLayoutContainer from './GraphLayout'; import GraphLayoutContainer from './GraphLayout';
import { UpdateDashboard } from './utils'; import { UpdateDashboard } from './utils';
@ -49,6 +50,7 @@ export const getPreLayouts = (
yAxisUnit={widget?.yAxisUnit} yAxisUnit={widget?.yAxisUnit}
layout={layout} layout={layout}
setLayout={setLayout} setLayout={setLayout}
headerMenuList={headerMenuList}
/> />
); );
}, },
@ -233,6 +235,7 @@ function GridGraph(props: Props): JSX.Element {
layout={layout} layout={layout}
setLayout={setLayout} setLayout={setLayout}
onDragSelect={onDragSelect} onDragSelect={onDragSelect}
headerMenuList={headerMenuList}
/> />
), ),
}; };

View File

@ -117,9 +117,6 @@ function DBCall(): JSX.Element {
'database_call_rps', 'database_call_rps',
); );
}} }}
allowClone={false}
allowDelete={false}
allowEdit={false}
/> />
</GraphContainer> </GraphContainer>
</Card> </Card>
@ -153,9 +150,6 @@ function DBCall(): JSX.Element {
'database_call_avg_duration', 'database_call_avg_duration',
); );
}} }}
allowClone={false}
allowDelete={false}
allowEdit={false}
/> />
</GraphContainer> </GraphContainer>
</Card> </Card>

View File

@ -156,9 +156,6 @@ function External(): JSX.Element {
'external_call_error_percentage', 'external_call_error_percentage',
); );
}} }}
allowClone={false}
allowDelete={false}
allowEdit={false}
/> />
</GraphContainer> </GraphContainer>
</Card> </Card>
@ -194,9 +191,6 @@ function External(): JSX.Element {
'external_call_duration', 'external_call_duration',
); );
}} }}
allowClone={false}
allowDelete={false}
allowEdit={false}
/> />
</GraphContainer> </GraphContainer>
</Card> </Card>
@ -233,9 +227,6 @@ function External(): JSX.Element {
'external_call_rps_by_address', 'external_call_rps_by_address',
); );
}} }}
allowClone={false}
allowDelete={false}
allowEdit={false}
/> />
</GraphContainer> </GraphContainer>
</Card> </Card>
@ -271,9 +262,6 @@ function External(): JSX.Element {
'external_call_duration_by_address', 'external_call_duration_by_address',
); );
}} }}
allowClone={false}
allowDelete={false}
allowEdit={false}
/> />
</GraphContainer> </GraphContainer>
</Card> </Card>

View File

@ -75,9 +75,6 @@ function ServiceOverview({
widget={latencyWidget} widget={latencyWidget}
yAxisUnit="ns" yAxisUnit="ns"
onClickHandler={handleGraphClick('Service')} onClickHandler={handleGraphClick('Service')}
allowClone={false}
allowDelete={false}
allowEdit={false}
isQueryEnabled={isQueryEnabled} isQueryEnabled={isQueryEnabled}
/> />
</GraphContainer> </GraphContainer>

View File

@ -39,9 +39,6 @@ function TopLevelOperation({
onClickHandler={handleGraphClick(opName)} onClickHandler={handleGraphClick(opName)}
yAxisUnit={yAxisUnit} yAxisUnit={yAxisUnit}
onDragSelect={onDragSelect} onDragSelect={onDragSelect}
allowClone={false}
allowDelete={false}
allowEdit={false}
/> />
)} )}
</GraphContainer> </GraphContainer>