mirror of https://github.com/grafana/grafana.git
				
				
				
			DashboardScene: Show dashboard not found view (#89342)
* DashboardScene: Show dashboard not found view * Test fix * Use correct selector
This commit is contained in:
		
							parent
							
								
									8eabef1f91
								
							
						
					
					
						commit
						d46df10d30
					
				|  | @ -603,4 +603,7 @@ export const Components = { | |||
|     headerOrderSwitch: 'data-testid header-order-switch', | ||||
|     headerPreviewSwitch: 'data-testid header-preview-switch', | ||||
|   }, | ||||
|   EntityNotFound: { | ||||
|     container: 'data-testid entity-not-found', | ||||
|   }, | ||||
| }; | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ import { css } from '@emotion/css'; | |||
| import React from 'react'; | ||||
| 
 | ||||
| import { GrafanaTheme2 } from '@grafana/data'; | ||||
| import { selectors } from '@grafana/e2e-selectors'; | ||||
| import { EmptyState, TextLink, useStyles2 } from '@grafana/ui'; | ||||
| 
 | ||||
| export interface Props { | ||||
|  | @ -15,7 +16,7 @@ export function EntityNotFound({ entity = 'Page' }: Props) { | |||
|   const styles = useStyles2(getStyles); | ||||
| 
 | ||||
|   return ( | ||||
|     <div className={styles.container}> | ||||
|     <div className={styles.container} data-testid={selectors.components.EntityNotFound.container}> | ||||
|       <EmptyState message={`${entity} not found`} variant="not-found"> | ||||
|         We're looking but can't seem to find this {entity.toLowerCase()}. Try returning{' '} | ||||
|         <TextLink href="/">home</TextLink> or seeking help on the{' '} | ||||
|  |  | |||
|  | @ -392,6 +392,10 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> { | |||
|   public getPageNav(location: H.Location, navIndex: NavIndex) { | ||||
|     const { meta, viewPanelScene, editPanel } = this.state; | ||||
| 
 | ||||
|     if (meta.dashboardNotFound) { | ||||
|       return { text: 'Not found' }; | ||||
|     } | ||||
| 
 | ||||
|     let pageNav: NavModelItem = { | ||||
|       text: this.state.title, | ||||
|       url: getDashboardUrl({ | ||||
|  |  | |||
|  | @ -0,0 +1,65 @@ | |||
| import { render, screen } from '@testing-library/react'; | ||||
| import React from 'react'; | ||||
| import { Provider } from 'react-redux'; | ||||
| import { Router } from 'react-router'; | ||||
| import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock'; | ||||
| 
 | ||||
| import { selectors } from '@grafana/e2e-selectors'; | ||||
| import { locationService } from '@grafana/runtime'; | ||||
| import { GrafanaContext } from 'app/core/context/GrafanaContext'; | ||||
| import { configureStore } from 'app/store/configureStore'; | ||||
| 
 | ||||
| import { transformSaveModelToScene } from '../serialization/transformSaveModelToScene'; | ||||
| 
 | ||||
| describe('DashboardSceneRenderer', () => { | ||||
|   it('should render Not Found notice when dashboard is not found', async () => { | ||||
|     const scene = transformSaveModelToScene({ | ||||
|       meta: { | ||||
|         isSnapshot: true, | ||||
|         dashboardNotFound: true, | ||||
|         canStar: false, | ||||
|         canDelete: false, | ||||
|         canSave: false, | ||||
|         canEdit: false, | ||||
|         canShare: false, | ||||
|       }, | ||||
|       dashboard: { | ||||
|         title: 'Not found', | ||||
|         uid: 'uid', | ||||
|         schemaVersion: 0, | ||||
|         // Disabling build in annotations to avoid mocking Grafana data source
 | ||||
|         annotations: { | ||||
|           list: [ | ||||
|             { | ||||
|               builtIn: 1, | ||||
|               datasource: { | ||||
|                 type: 'grafana', | ||||
|                 uid: '-- Grafana --', | ||||
|               }, | ||||
|               enable: false, | ||||
|               hide: true, | ||||
|               iconColor: 'rgba(0, 211, 255, 1)', | ||||
|               name: 'Annotations & Alerts', | ||||
|               type: 'dashboard', | ||||
|             }, | ||||
|           ], | ||||
|         }, | ||||
|       }, | ||||
|     }); | ||||
| 
 | ||||
|     const store = configureStore({}); | ||||
|     const context = getGrafanaContextMock(); | ||||
| 
 | ||||
|     render( | ||||
|       <GrafanaContext.Provider value={context}> | ||||
|         <Provider store={store}> | ||||
|           <Router history={locationService.getHistory()}> | ||||
|             <scene.Component model={scene} /> | ||||
|           </Router> | ||||
|         </Provider> | ||||
|       </GrafanaContext.Provider> | ||||
|     ); | ||||
| 
 | ||||
|     expect(await screen.findByTestId(selectors.components.EntityNotFound.container)).toBeInTheDocument(); | ||||
|   }); | ||||
| }); | ||||
|  | @ -7,6 +7,7 @@ import { selectors } from '@grafana/e2e-selectors'; | |||
| import { SceneComponentProps } from '@grafana/scenes'; | ||||
| import { CustomScrollbar, useStyles2 } from '@grafana/ui'; | ||||
| import { Page } from 'app/core/components/Page/Page'; | ||||
| import { EntityNotFound } from 'app/core/components/PageNotFound/EntityNotFound'; | ||||
| import { getNavModel } from 'app/core/selectors/navModel'; | ||||
| import DashboardEmpty from 'app/features/dashboard/dashgrid/DashboardEmpty'; | ||||
| import { useSelector } from 'app/types'; | ||||
|  | @ -35,14 +36,26 @@ export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardS | |||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   const emptyState = <DashboardEmpty dashboard={model} canCreate={!!model.state.meta.canEdit} />; | ||||
|   const emptyState = ( | ||||
|     <DashboardEmpty dashboard={model} canCreate={!!model.state.meta.canEdit} key="dashboard-empty-state" /> | ||||
|   ); | ||||
| 
 | ||||
|   const withPanels = ( | ||||
|     <div className={cx(styles.body, !hasControls && styles.bodyWithoutControls)}> | ||||
|     <div className={cx(styles.body, !hasControls && styles.bodyWithoutControls)} key="dashboard-panels"> | ||||
|       <bodyToRender.Component model={bodyToRender} /> | ||||
|     </div> | ||||
|   ); | ||||
| 
 | ||||
|   const notFound = meta.dashboardNotFound && <EntityNotFound entity="Dashboard" key="dashboard-not-found" />; | ||||
| 
 | ||||
|   let body = [withPanels]; | ||||
| 
 | ||||
|   if (notFound) { | ||||
|     body = [notFound]; | ||||
|   } else if (isEmpty) { | ||||
|     body = [emptyState, withPanels]; | ||||
|   } | ||||
| 
 | ||||
|   return ( | ||||
|     <Page navModel={navModel} pageNav={pageNav} layout={PageLayoutType.Custom}> | ||||
|       {editPanel && <editPanel.Component model={editPanel} />} | ||||
|  | @ -55,7 +68,7 @@ export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardS | |||
|             scopes && isScopesExpanded && styles.pageContainerWithScopesExpanded | ||||
|           )} | ||||
|         > | ||||
|           {scopes && <scopes.Component model={scopes} />} | ||||
|           {scopes && !meta.dashboardNotFound && <scopes.Component model={scopes} />} | ||||
|           <NavToolbarActions dashboard={model} /> | ||||
|           {controls && hasControls && ( | ||||
|             <div | ||||
|  | @ -71,10 +84,7 @@ export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardS | |||
|             className={styles.scrollbarContainer} | ||||
|             testId={selectors.pages.Dashboard.DashNav.scrollContainer} | ||||
|           > | ||||
|             <div className={cx(styles.canvasContent, isHomePage && styles.homePagePadding)}> | ||||
|               <>{isEmpty && emptyState}</> | ||||
|               {withPanels} | ||||
|             </div> | ||||
|             <div className={cx(styles.canvasContent, isHomePage && styles.homePagePadding)}>{body}</div> | ||||
|           </CustomScrollbar> | ||||
|         </div> | ||||
|       )} | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ import { selectors } from '@grafana/e2e-selectors'; | |||
| import { config, locationService } from '@grafana/runtime'; | ||||
| import { SceneGridLayout, SceneQueryRunner, SceneTimeRange, UrlSyncContextProvider, VizPanel } from '@grafana/scenes'; | ||||
| import { playlistSrv } from 'app/features/playlist/PlaylistSrv'; | ||||
| import { DashboardMeta } from 'app/types'; | ||||
| 
 | ||||
| import { buildPanelEditScene } from '../panel-edit/PanelEditor'; | ||||
| 
 | ||||
|  | @ -163,9 +164,27 @@ describe('NavToolbarActions', () => { | |||
|       expect(newShareButton).toBeInTheDocument(); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('Snapshot', () => { | ||||
|     it('should show link button when is a snapshot', () => { | ||||
|       setup({ | ||||
|         isSnapshot: true, | ||||
|       }); | ||||
| 
 | ||||
|       expect(screen.queryByTestId('button-snapshot')).toBeInTheDocument(); | ||||
|     }); | ||||
|     it('should not show link button when is not found dashboard', () => { | ||||
|       setup({ | ||||
|         isSnapshot: true, | ||||
|         dashboardNotFound: true, | ||||
|       }); | ||||
| 
 | ||||
|       expect(screen.queryByTestId('button-snapshot')).not.toBeInTheDocument(); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| 
 | ||||
| function setup() { | ||||
| function setup(meta?: DashboardMeta) { | ||||
|   const dashboard = new DashboardScene({ | ||||
|     $timeRange: new SceneTimeRange({ from: 'now-6h', to: 'now' }), | ||||
|     meta: { | ||||
|  | @ -177,6 +196,7 @@ function setup() { | |||
|       canStar: true, | ||||
|       canAdmin: true, | ||||
|       canDelete: true, | ||||
|       ...meta, | ||||
|     }, | ||||
|     title: 'hello', | ||||
|     uid: 'dash-1', | ||||
|  |  | |||
|  | @ -139,7 +139,7 @@ export function ToolbarActions({ dashboard }: Props) { | |||
| 
 | ||||
|   toolbarActions.push({ | ||||
|     group: 'icon-actions', | ||||
|     condition: meta.isSnapshot && !isEditing, | ||||
|     condition: meta.isSnapshot && !meta.dashboardNotFound && !isEditing, | ||||
|     render: () => ( | ||||
|       <GoToSnapshotOriginButton | ||||
|         key="go-to-snapshot-origin" | ||||
|  |  | |||
|  | @ -92,7 +92,9 @@ export class DashboardLoaderSrv { | |||
|           return result; | ||||
|         }) | ||||
|         .catch(() => { | ||||
|           return this._dashboardLoadFailed('Not found', true); | ||||
|           const dash = this._dashboardLoadFailed('Not found', true); | ||||
|           dash.dashboard.uid = uid; | ||||
|           return dash; | ||||
|         }); | ||||
|     } else { | ||||
|       throw new Error('Dashboard uid or slug required'); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue