DashboardScene: Support custom actions in the nav toolbar (#79139)

* DashbaordScene: Support custom nav toolbar actions

* Move custom actions to scenes feature dir

* Review

* betterrerererererer
This commit is contained in:
Dominik Prokop
2023-12-11 00:26:32 -08:00
committed by GitHub
parent 5b4da3a7dc
commit 096a3b148f
5 changed files with 71 additions and 18 deletions
@@ -6,9 +6,11 @@ import { AppChromeUpdate } from 'app/core/components/AppChrome/AppChromeUpdate';
import { NavToolbarSeparator } from 'app/core/components/AppChrome/NavToolbar/NavToolbarSeparator';
import { t } from 'app/core/internationalization';
import { DashNavButton } from 'app/features/dashboard/components/DashNav/DashNavButton';
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
import { ShareModal } from '../sharing/ShareModal';
import { DashboardInteractions } from '../utils/interactions';
import { dynamicDashNavActions } from '../utils/registerDynamicDashNavAction';
import { DashboardScene } from './DashboardScene';
@@ -19,6 +21,8 @@ interface Props {
export const NavToolbarActions = React.memo<Props>(({ dashboard }) => {
const { actions = [], isEditing, viewPanelScene, isDirty, uid, meta, editview } = dashboard.useState();
const toolbarActions = (actions ?? []).map((action) => <action.Component key={action.state.key} model={action} />);
const rightToolbarActions: JSX.Element[] = [];
const _legacyDashboardModel = getDashboardSrv().getCurrent();
if (uid && !editview) {
if (meta.canStar) {
@@ -61,10 +65,31 @@ export const NavToolbarActions = React.memo<Props>(({ dashboard }) => {
onClick={() => locationService.push(`/d/${uid}`)}
/>
);
if (dynamicDashNavActions.left.length > 0) {
dynamicDashNavActions.left.map((action, index) => {
const Component = action.component;
const element = <Component dashboard={_legacyDashboardModel} />;
typeof action.index === 'number'
? toolbarActions.splice(action.index, 0, element)
: toolbarActions.push(element);
});
}
}
toolbarActions.push(<NavToolbarSeparator leftActionsSeparator key="separator" />);
if (dynamicDashNavActions.right.length > 0) {
dynamicDashNavActions.right.map((action, index) => {
const Component = action.component;
const element = <Component dashboard={_legacyDashboardModel} key={`button-custom-${index}`} />;
typeof action.index === 'number'
? rightToolbarActions.splice(action.index, 0, element)
: rightToolbarActions.push(element);
});
toolbarActions.push(...rightToolbarActions);
}
if (viewPanelScene) {
toolbarActions.push(
<Button
@@ -7,6 +7,7 @@ import {
SceneQueryRunner,
SceneTimeRange,
VizPanel,
SceneTimePicker,
} from '@grafana/scenes';
import { DashboardCursorSync } from '@grafana/schema';
@@ -18,7 +19,7 @@ import { DashboardModelCompatibilityWrapper } from './DashboardModelCompatibilit
describe('DashboardModelCompatibilityWrapper', () => {
it('Provide basic prop and function of compatability', () => {
const { wrapper } = setup();
const { wrapper, scene } = setup();
expect(wrapper.uid).toBe('dash-1');
expect(wrapper.title).toBe('hello');
@@ -30,6 +31,14 @@ describe('DashboardModelCompatibilityWrapper', () => {
expect(wrapper.timezone).toBe('America/New_York');
expect(wrapper.weekStart).toBe('friday');
expect(wrapper.timepicker.refresh_intervals).toEqual(['1s']);
expect(wrapper.timepicker.hidden).toEqual(true);
(scene.state.controls![0] as DashboardControls).setState({
timeControls: [new SceneTimePicker({})],
});
const wrapper2 = new DashboardModelCompatibilityWrapper(scene);
expect(wrapper2.timepicker.hidden).toEqual(false);
});
it('Shared tooltip functions', () => {
@@ -63,6 +63,7 @@ export class DashboardModelCompatibilityWrapper {
public get timepicker() {
return {
refresh_intervals: dashboardSceneGraph.getRefreshPicker(this._scene)?.state.intervals,
hidden: !Boolean(dashboardSceneGraph.getTimePicker(this._scene)),
};
}
@@ -0,0 +1,21 @@
import React from 'react';
import { DashboardModel } from '../../dashboard/state';
interface ComponentProps {
dashboard: DashboardModel;
}
export interface DynamicDashNavButtonModel {
show: (props: ComponentProps) => boolean;
component: React.FC<Partial<ComponentProps>>;
index?: number | 'end';
}
export const dynamicDashNavActions: { left: DynamicDashNavButtonModel[]; right: DynamicDashNavButtonModel[] } = {
left: [],
right: [],
};
export function registerDynamicDashNavAction(side: 'left' | 'right', action: DynamicDashNavButtonModel) {
dynamicDashNavActions[side].push(action);
}
@@ -1,5 +1,5 @@
import { css } from '@emotion/css';
import React, { FC, ReactNode } from 'react';
import React, { ReactNode } from 'react';
import { connect, ConnectedProps } from 'react-redux';
import { useLocation } from 'react-router-dom';
@@ -33,6 +33,12 @@ import { updateTimeZoneForSession } from 'app/features/profile/state/reducers';
import { KioskMode } from 'app/types';
import { DashboardMetaChangedEvent, ShowModalReactEvent } from 'app/types/events';
import {
DynamicDashNavButtonModel,
dynamicDashNavActions,
registerDynamicDashNavAction,
} from '../../../dashboard-scene/utils/registerDynamicDashNavAction';
import { DashNavButton } from './DashNavButton';
import { DashNavTimeControls } from './DashNavTimeControls';
import { ShareButton } from './ShareButton';
@@ -56,21 +62,12 @@ export interface OwnProps {
onAddPanel: () => void;
}
interface DashNavButtonModel {
show: (props: Props) => boolean;
component: FC<Partial<Props>>;
index?: number | 'end';
export function addCustomLeftAction(content: DynamicDashNavButtonModel) {
registerDynamicDashNavAction('left', content);
}
const customLeftActions: DashNavButtonModel[] = [];
const customRightActions: DashNavButtonModel[] = [];
export function addCustomLeftAction(content: DashNavButtonModel) {
customLeftActions.push(content);
}
export function addCustomRightAction(content: DashNavButtonModel) {
customRightActions.push(content);
export function addCustomRightAction(content: DynamicDashNavButtonModel) {
registerDynamicDashNavAction('right', content);
}
type Props = OwnProps & ConnectedProps<typeof connector>;
@@ -152,7 +149,7 @@ export const DashNav = React.memo<Props>((props) => {
forceUpdate();
};
const addCustomContent = (actions: DashNavButtonModel[], buttons: ReactNode[]) => {
const addCustomContent = (actions: DynamicDashNavButtonModel[], buttons: ReactNode[]) => {
actions.map((action, index) => {
const Component = action.component;
const element = <Component {...props} key={`button-custom-${index}`} />;
@@ -213,7 +210,7 @@ export const DashNav = React.memo<Props>((props) => {
);
}
addCustomContent(customLeftActions, buttons);
addCustomContent(dynamicDashNavActions.left, buttons);
return buttons;
};
@@ -336,7 +333,7 @@ export const DashNav = React.memo<Props>((props) => {
);
}
addCustomContent(customRightActions, buttons);
addCustomContent(dynamicDashNavActions.right, buttons);
buttons.push(renderTimeControls());