Dashboard-Scene: Show empty state after removing last panel (#83114)

* Show empty state after removing last panel

* betterer

* Refactor isEmpty state update in DashboardScene.tsx

* don't need viewPanelScene check

* track isEmpty through a behavior

* Fix test

* Add test for empty state

* minor fix

* Refactor isEmpty check

* Don't use const

* clean up
pull/83353/head
Haris Rozajac 1 year ago committed by GitHub
parent 240480ac9b
commit e4276a4ede
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 7
      public/app/features/dashboard-scene/pages/DashboardScenePage.test.tsx
  2. 15
      public/app/features/dashboard-scene/scene/DashboardScene.tsx
  3. 14
      public/app/features/dashboard-scene/scene/DashboardSceneRenderer.tsx
  4. 4
      public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx
  5. 2
      public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts
  6. 17
      public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts

@ -194,6 +194,13 @@ describe('DashboardScenePage', () => {
expect(screen.queryByTitle('Panel A')).not.toBeInTheDocument();
expect(await screen.findByTitle('Panel B')).toBeInTheDocument();
});
it('Shows empty state when dashboard is empty', async () => {
loadDashboardMock.mockResolvedValue({ dashboard: { panels: [] }, meta: {} });
setup();
expect(await screen.findByText('Start your new dashboard by adding a visualization')).toBeInTheDocument();
});
});
interface VizOptions {

@ -94,6 +94,7 @@ export interface DashboardSceneState extends SceneObjectState {
editPanel?: PanelEditor;
/** Scene object that handles the current drawer or modal */
overlay?: SceneObject;
isEmpty?: boolean;
}
export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
@ -522,20 +523,6 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
locationService.partial({ editview: 'settings' });
};
public isEmpty = (): boolean => {
const { body, viewPanelScene } = this.state;
if (!!viewPanelScene) {
return !!viewPanelScene.state.body;
}
if (body instanceof SceneFlexLayout || body instanceof SceneGridLayout) {
return body.state.children.length === 0;
}
throw new Error('Invalid body type');
};
/**
* Called by the SceneQueryRunner to privide contextural parameters (tracking) props for the request
*/

@ -14,7 +14,7 @@ import { DashboardScene } from './DashboardScene';
import { NavToolbarActions } from './NavToolbarActions';
export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardScene>) {
const { controls, overlay, editview, editPanel } = model.useState();
const { controls, overlay, editview, editPanel, isEmpty } = model.useState();
const styles = useStyles2(getStyles);
const location = useLocation();
const navIndex = useSelector((state) => state.navIndex);
@ -34,12 +34,9 @@ export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardS
const emptyState = <DashboardEmpty dashboard={model} canCreate={!!model.state.meta.canEdit} />;
const withPanels = (
<>
{controls && <controls.Component model={controls} />}
<div className={cx(styles.body)}>
<bodyToRender.Component model={bodyToRender} />
</div>
</>
<div className={cx(styles.body)}>
<bodyToRender.Component model={bodyToRender} />
</div>
);
return (
@ -49,7 +46,8 @@ export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardS
<CustomScrollbar autoHeightMin={'100%'}>
<div className={styles.canvasContent}>
<NavToolbarActions dashboard={model} />
{model.isEmpty() ? emptyState : withPanels}
{controls && <controls.Component model={controls} />}
{isEmpty ? emptyState : withPanels}
</div>
</CustomScrollbar>
)}

@ -417,9 +417,7 @@ export function removePanel(dashboard: DashboardScene, panel: VizPanel, ask: boo
const layout = dashboard.state.body;
if (layout instanceof SceneGridLayout || SceneFlexLayout) {
layout.setState({
children: panels,
});
layout.setState({ children: panels });
}
}

@ -140,7 +140,7 @@ describe('transformSaveModelToScene', () => {
const scene = createDashboardSceneFromDashboardModel(oldModel);
expect(scene.state.$behaviors).toHaveLength(5);
expect(scene.state.$behaviors).toHaveLength(6);
expect(scene.state.$behaviors![0]).toBeInstanceOf(behaviors.CursorSync);
expect((scene.state.$behaviors![0] as behaviors.CursorSync).state.sync).toEqual(DashboardCursorSync.Crosshair);
});

@ -28,6 +28,7 @@ import {
UserActionEvent,
GroupByVariable,
AdHocFiltersVariable,
SceneFlexLayout,
} from '@grafana/scenes';
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
import { trackDashboardLoaded } from 'app/features/dashboard/utils/tracking';
@ -266,6 +267,7 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel)
registerDashboardMacro,
registerDashboardSceneTracking(oldModel),
registerPanelInteractionsReporter,
trackIfIsEmpty,
],
$data:
layers.length > 0
@ -535,6 +537,21 @@ function registerPanelInteractionsReporter(scene: DashboardScene) {
});
}
export function trackIfIsEmpty(parent: DashboardScene) {
updateIsEmpty(parent);
parent.state.body.subscribeToState(() => {
updateIsEmpty(parent);
});
}
function updateIsEmpty(parent: DashboardScene) {
const { body } = parent.state;
if (body instanceof SceneFlexLayout || body instanceof SceneGridLayout) {
parent.setState({ isEmpty: body.state.children.length === 0 });
}
}
const convertSnapshotData = (snapshotData: DataFrameDTO[]): DataFrameJSON[] => {
return snapshotData.map((data) => {
return {

Loading…
Cancel
Save