Dashboard: Add new visualization/row/library panel/pasted panel is now a dropdown menu (#65361)

* Empty Dashboard state has its own CTA items and its own separate box to choose a library panel to create

* show empty dashboard screen if no panels

* add feature flag for empty dashboard redesign

* only show empty dashboard redesign if FF

* add-new-panel button is a dropdown with options: visualization, row, import, paste

* fix onPasteCopiedPanel types

* do not create new panel to new dashboard if emptyDashboardPage FF true

* ToolbarButton does not allow rendering of Dropdown's overlay - switch to Button

* remove panel-add icon's default size of xl

* separate components for add new panel from dash navigation bar
This commit is contained in:
Polina Boneva
2023-03-30 13:50:35 +03:00
committed by GitHub
parent 1c7921770c
commit b9fb23502c
8 changed files with 252 additions and 61 deletions

View File

@@ -0,0 +1,61 @@
import { css, cx } from '@emotion/css';
import React, { useState } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { Dropdown, Button, useTheme2, Icon } from '@grafana/ui';
import { t } from 'app/core/internationalization';
import { DashboardModel } from 'app/features/dashboard/state';
import { AddPanelMenu } from './AddPanelMenu';
interface Props {
dashboard: DashboardModel;
}
export const AddPanelButton = ({ dashboard }: Props) => {
const styles = getStyles(useTheme2());
const [isMenuOpen, setIsMenuOpen] = useState(false);
return (
<Dropdown
overlay={() => <AddPanelMenu dashboard={dashboard} />}
placement="bottom"
offset={[0, 6]}
onVisibleChange={setIsMenuOpen}
>
<Button
tooltip={t('dashboard.toolbar.add-panel', 'Add panel')}
icon="panel-add"
size="lg"
fill="outline"
className={cx(styles.button, styles.buttonIcon, styles.buttonText)}
>
Add
<Icon name={isMenuOpen ? 'angle-up' : 'angle-down'} size="lg" />
</Button>
</Dropdown>
);
};
function getStyles(theme: GrafanaTheme2) {
return {
button: css({
label: 'add-panel-button',
padding: theme.spacing(0.5, 0.5, 0.5, 0.75),
height: theme.spacing((theme.components.height.sm + theme.components.height.md) / 2),
borderRadius: theme.shape.radius.default,
}),
buttonIcon: css({
svg: {
margin: 0,
},
}),
buttonText: css({
label: 'add-panel-button-text',
fontSize: theme.typography.body.fontSize,
span: {
marginLeft: theme.spacing(0.67),
},
}),
};
}

View File

@@ -0,0 +1,63 @@
import React, { useMemo } from 'react';
import { locationService, reportInteraction } from '@grafana/runtime';
import { Menu } from '@grafana/ui';
import { DashboardModel } from 'app/features/dashboard/state';
import {
getCopiedPanelPlugin,
onAddLibraryPanel,
onCreateNewPanel,
onCreateNewRow,
onPasteCopiedPanel,
} from 'app/features/dashboard/utils/dashboard';
interface Props {
dashboard: DashboardModel;
}
export const AddPanelMenu = ({ dashboard }: Props) => {
const copiedPanelPlugin = useMemo(() => getCopiedPanelPlugin(), []);
return (
<Menu>
<Menu.Item
key="add-visualisation"
label="Visualization"
ariaLabel="Add new panel"
onClick={() => {
reportInteraction('Create new panel');
const id = onCreateNewPanel(dashboard);
locationService.partial({ editPanel: id });
}}
/>
<Menu.Item
key="add-row"
label="Row"
ariaLabel="Add new row"
onClick={() => {
reportInteraction('Create new row');
onCreateNewRow(dashboard);
}}
/>
<Menu.Item
key="add-panel-lib"
label="Import from library"
ariaLabel="Add new panel from panel library"
onClick={() => {
reportInteraction('Add a panel from the panel library');
onAddLibraryPanel(dashboard);
}}
/>
<Menu.Item
key="add-panel-clipboard"
label="Paste panel"
ariaLabel="Add new panel from clipboard"
onClick={() => {
reportInteraction('Paste panel from clipboard');
onPasteCopiedPanel(dashboard, copiedPanelPlugin);
}}
disabled={!copiedPanelPlugin}
/>
</Menu>
);
};

View File

@@ -217,7 +217,7 @@ const AddPanelWidgetHandle = ({ children, onBack, onCancel, styles }: AddPanelWi
)}
{!onBack && (
<div className={styles.backButton}>
<Icon name="panel-add" size="md" />
<Icon name="panel-add" size="xl" />
</div>
)}
{children && <span>{children}</span>}

View File

@@ -25,17 +25,17 @@ import { useAppNotification } from 'app/core/copy/appNotification';
import { appEvents } from 'app/core/core';
import { useBusEvent } from 'app/core/hooks/useBusEvent';
import { t, Trans } from 'app/core/internationalization';
import { setStarred } from 'app/core/reducers/navBarTree';
import { AddPanelButton } from 'app/features/dashboard/components/AddPanelButton/AddPanelButton';
import { SaveDashboardDrawer } from 'app/features/dashboard/components/SaveDashboard/SaveDashboardDrawer';
import { ShareModal } from 'app/features/dashboard/components/ShareModal';
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
import { DashboardModel } from 'app/features/dashboard/state';
import { playlistSrv } from 'app/features/playlist/PlaylistSrv';
import { updateTimeZoneForSession } from 'app/features/profile/state/reducers';
import { KioskMode } from 'app/types';
import { DashboardMetaChangedEvent, ShowModalReactEvent } from 'app/types/events';
import { setStarred } from '../../../../core/reducers/navBarTree';
import { getDashboardSrv } from '../../services/DashboardSrv';
import { DashboardModel } from '../../state';
import { DashNavButton } from './DashNavButton';
import { DashNavTimeControls } from './DashNavTimeControls';
@@ -305,14 +305,19 @@ export const DashNav = React.memo<Props>((props) => {
}
if (canEdit && !isFullscreen) {
buttons.push(
<ToolbarButton
tooltip={t('dashboard.toolbar.add-panel', 'Add panel')}
icon="panel-add"
onClick={onAddPanel}
key="button-panel-add"
/>
);
if (config.featureToggles.emptyDashboardPage) {
buttons.push(<AddPanelButton dashboard={dashboard} key="panel-add-dropdown" />);
} else {
buttons.push(
<ToolbarButton
tooltip={t('dashboard.toolbar.add-panel', 'Add panel')}
icon="panel-add"
iconSize="xl"
onClick={onAddPanel}
key="button-panel-add"
/>
);
}
}
if (canSave && !isFullscreen) {