mirror of https://github.com/grafana/grafana.git
				
				
				
			PublicDashboards: Time range settings (#61585)
This commit is contained in:
		
							parent
							
								
									1454b1b40a
								
							
						
					
					
						commit
						18e8d1e28d
					
				|  | @ -194,6 +194,7 @@ export const Pages = { | |||
|       CopyUrlButton: 'data-testid public dashboard copy url button', | ||||
|       TemplateVariablesWarningAlert: 'data-testid public dashboard disabled template variables alert', | ||||
|       UnsupportedDatasourcesWarningAlert: 'data-testid public dashboard unsupported datasources', | ||||
|       EnableTimeRangeSwitch: 'data-testid public dashboard on off switch for time range', | ||||
|     }, | ||||
|   }, | ||||
|   Explore: { | ||||
|  |  | |||
|  | @ -101,7 +101,6 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) response.Response { | |||
| 	} | ||||
| 
 | ||||
| 	var ( | ||||
| 		hasPublicDashboard     = false | ||||
| 		publicDashboardEnabled = false | ||||
| 		err                    error | ||||
| 	) | ||||
|  | @ -115,7 +114,6 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) response.Response { | |||
| 		} | ||||
| 
 | ||||
| 		if publicDashboard != nil { | ||||
| 			hasPublicDashboard = true | ||||
| 			publicDashboardEnabled = publicDashboard.IsEnabled | ||||
| 		} | ||||
| 	} | ||||
|  | @ -187,7 +185,6 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) response.Response { | |||
| 		FolderTitle:            "General", | ||||
| 		AnnotationsPermissions: annotationPermissions, | ||||
| 		PublicDashboardEnabled: publicDashboardEnabled, | ||||
| 		HasPublicDashboard:     hasPublicDashboard, | ||||
| 	} | ||||
| 
 | ||||
| 	// lookup folder title
 | ||||
|  |  | |||
|  | @ -32,7 +32,6 @@ type DashboardMeta struct { | |||
| 	Provisioned                bool                  `json:"provisioned"` | ||||
| 	ProvisionedExternalId      string                `json:"provisionedExternalId"` | ||||
| 	AnnotationsPermissions     *AnnotationPermission `json:"annotationsPermissions"` | ||||
| 	HasPublicDashboard         bool                  `json:"hasPublicDashboard"` | ||||
| 	PublicDashboardAccessToken string                `json:"publicDashboardAccessToken"` | ||||
| 	PublicDashboardUID         string                `json:"publicDashboardUid"` | ||||
| 	PublicDashboardEnabled     bool                  `json:"publicDashboardEnabled"` | ||||
|  |  | |||
|  | @ -38,7 +38,7 @@ export const publicDashboardApi = createApi({ | |||
|   tagTypes: ['PublicDashboard', 'AuditTablePublicDashboard'], | ||||
|   refetchOnMountOrArgChange: true, | ||||
|   endpoints: (builder) => ({ | ||||
|     getPublicDashboard: builder.query<PublicDashboard, string>({ | ||||
|     getPublicDashboard: builder.query<PublicDashboard | undefined, string>({ | ||||
|       query: (dashboardUid) => ({ | ||||
|         url: `/uid/${dashboardUid}/public-dashboards`, | ||||
|         manageError: getConfigError, | ||||
|  | @ -67,7 +67,6 @@ export const publicDashboardApi = createApi({ | |||
| 
 | ||||
|         // Update runtime meta flag
 | ||||
|         dashboard.updateMeta({ | ||||
|           hasPublicDashboard: true, | ||||
|           publicDashboardUid: data.uid, | ||||
|           publicDashboardEnabled: data.isEnabled, | ||||
|         }); | ||||
|  | @ -87,7 +86,6 @@ export const publicDashboardApi = createApi({ | |||
| 
 | ||||
|         // Update runtime meta flag
 | ||||
|         dashboard.updateMeta({ | ||||
|           hasPublicDashboard: true, | ||||
|           publicDashboardUid: data.uid, | ||||
|           publicDashboardEnabled: data.isEnabled, | ||||
|         }); | ||||
|  | @ -110,7 +108,6 @@ export const publicDashboardApi = createApi({ | |||
|         dispatch(notifyApp(createSuccessNotification('Public dashboard deleted!'))); | ||||
| 
 | ||||
|         dashboard?.updateMeta({ | ||||
|           hasPublicDashboard: false, | ||||
|           publicDashboardUid: uid, | ||||
|           publicDashboardEnabled: false, | ||||
|         }); | ||||
|  |  | |||
|  | @ -34,9 +34,15 @@ export const Configuration = ({ | |||
|       <FieldSet disabled={disabled} className={styles.dashboardConfig}> | ||||
|         <VerticalGroup spacing="md"> | ||||
|           <Layout orientation={isDesktop ? 0 : 1} spacing="xs" justify="space-between"> | ||||
|             <Label description="The public dashboard uses the default time settings of the dashboard">Time range</Label> | ||||
|             <Label description="The public dashboard uses the default time settings of the dashboard"> | ||||
|               Default time range | ||||
|             </Label> | ||||
|             <TimeRangeInput value={timeRange} disabled onChange={() => {}} /> | ||||
|           </Layout> | ||||
|           <Layout orientation={isDesktop ? 0 : 1} spacing="xs" justify="space-between"> | ||||
|             <Label description="Allow viewers to change time range">Time range picker enabled</Label> | ||||
|             <Switch {...register('isTimeRangeEnabled')} data-testid={selectors.EnableTimeRangeSwitch} /> | ||||
|           </Layout> | ||||
|           <Layout orientation={isDesktop ? 0 : 1} spacing="xs" justify="space-between"> | ||||
|             <Label description="Show annotations on public dashboard">Show annotations</Label> | ||||
|             <Switch | ||||
|  |  | |||
|  | @ -19,6 +19,8 @@ import { configureStore } from 'app/store/configureStore'; | |||
| 
 | ||||
| import { ShareModal } from '../ShareModal'; | ||||
| 
 | ||||
| import { PublicDashboard } from './SharePublicDashboardUtils'; | ||||
| 
 | ||||
| const server = setupServer(); | ||||
| 
 | ||||
| jest.mock('@grafana/runtime', () => ({ | ||||
|  | @ -95,6 +97,29 @@ afterEach(() => { | |||
| }); | ||||
| 
 | ||||
| describe('SharePublic', () => { | ||||
|   const pubdashResponse: PublicDashboard = { | ||||
|     isEnabled: true, | ||||
|     annotationsEnabled: true, | ||||
|     timeSelectionEnabled: true, | ||||
|     uid: 'a-uid', | ||||
|     dashboardUid: '', | ||||
|     accessToken: 'an-access-token', | ||||
|   }; | ||||
| 
 | ||||
|   beforeEach(() => { | ||||
|     server.use( | ||||
|       rest.get('/api/dashboards/uid/:dashboardUid/public-dashboards', (req, res, ctx) => { | ||||
|         return res( | ||||
|           ctx.status(200), | ||||
|           ctx.json({ | ||||
|             ...pubdashResponse, | ||||
|             dashboardUid: req.params.dashboardUid, | ||||
|           }) | ||||
|         ); | ||||
|       }) | ||||
|     ); | ||||
|   }); | ||||
| 
 | ||||
|   it('does not render share panel when public dashboards feature is disabled', async () => { | ||||
|     config.featureToggles.publicDashboards = false; | ||||
|     await renderSharePublicDashboard({ panel: mockPanel, dashboard: mockDashboard, onDismiss: () => {} }, false); | ||||
|  | @ -150,21 +175,20 @@ describe('SharePublic', () => { | |||
|     expect(screen.getByText('2022-08-30 00:00:00 to 2022-09-04 01:59:59')).toBeInTheDocument(); | ||||
|   }); | ||||
|   it('when modal is opened, then loader spinner appears and inputs are disabled', async () => { | ||||
|     mockDashboard.meta.hasPublicDashboard = true; | ||||
|     await renderSharePublicDashboard({ panel: mockPanel, dashboard: mockDashboard, onDismiss: () => {} }); | ||||
| 
 | ||||
|     screen.getAllByTestId('Spinner'); | ||||
| 
 | ||||
|     expect(screen.getByText('Save public dashboard')).toBeInTheDocument(); | ||||
|     expect(screen.getByText('Create public dashboard')).toBeInTheDocument(); | ||||
|     expect(screen.getByTestId(selectors.WillBePublicCheckbox)).toBeDisabled(); | ||||
|     expect(screen.getByTestId(selectors.LimitedDSCheckbox)).toBeDisabled(); | ||||
|     expect(screen.getByTestId(selectors.CostIncreaseCheckbox)).toBeDisabled(); | ||||
|     expect(screen.getByTestId(selectors.EnableTimeRangeSwitch)).toBeDisabled(); | ||||
|     expect(screen.getByTestId(selectors.EnableSwitch)).toBeDisabled(); | ||||
|     expect(screen.getByTestId(selectors.SaveConfigButton)).toBeDisabled(); | ||||
|     expect(screen.queryByTestId(selectors.DeleteButton)).not.toBeInTheDocument(); | ||||
|   }); | ||||
|   it('when fetch errors happen, then all inputs remain disabled', async () => { | ||||
|     mockDashboard.meta.hasPublicDashboard = true; | ||||
|     server.use( | ||||
|       rest.get('/api/dashboards/uid/:dashboardUid/public-dashboards', (req, res, ctx) => { | ||||
|         return res(ctx.status(500)); | ||||
|  | @ -178,17 +202,29 @@ describe('SharePublic', () => { | |||
|     expect(screen.getByTestId(selectors.LimitedDSCheckbox)).toBeDisabled(); | ||||
|     expect(screen.getByTestId(selectors.CostIncreaseCheckbox)).toBeDisabled(); | ||||
|     expect(screen.getByTestId(selectors.EnableSwitch)).toBeDisabled(); | ||||
|     expect(screen.getByTestId(selectors.EnableTimeRangeSwitch)).toBeDisabled(); | ||||
|     expect(screen.getByTestId(selectors.EnableAnnotationsSwitch)).toBeDisabled(); | ||||
|     expect(screen.getByText('Save public dashboard')).toBeInTheDocument(); | ||||
|     expect(screen.getByText('Create public dashboard')).toBeInTheDocument(); | ||||
|     expect(screen.getByTestId(selectors.SaveConfigButton)).toBeDisabled(); | ||||
|     expect(screen.queryByTestId(selectors.DeleteButton)).not.toBeInTheDocument(); | ||||
|   }); | ||||
|   // test checking if current version of dashboard in state is persisted to db
 | ||||
| }); | ||||
| 
 | ||||
| describe('SharePublic - New config setup', () => { | ||||
|   beforeEach(() => { | ||||
|     mockDashboard.meta.hasPublicDashboard = false; | ||||
|     server.use( | ||||
|       rest.get('/api/dashboards/uid/:dashboardUid/public-dashboards', (req, res, ctx) => { | ||||
|         return res( | ||||
|           ctx.status(404), | ||||
|           ctx.json({ | ||||
|             message: 'Public dashboard not found', | ||||
|             messageId: 'publicdashboards.notFound', | ||||
|             statusCode: 404, | ||||
|             traceID: '', | ||||
|           }) | ||||
|         ); | ||||
|       }) | ||||
|     ); | ||||
|   }); | ||||
|   it('when modal is opened, then save button is disabled', async () => { | ||||
|     await renderSharePublicDashboard({ panel: mockPanel, dashboard: mockDashboard, onDismiss: () => {} }); | ||||
|  | @ -213,13 +249,14 @@ describe('SharePublic - New config setup', () => { | |||
|   }); | ||||
|   it('when fetch is done, then no loader spinner appears, inputs are enabled and save button is disabled', async () => { | ||||
|     await renderSharePublicDashboard({ panel: mockPanel, dashboard: mockDashboard, onDismiss: () => {} }); | ||||
|     expect(screen.queryByTestId('Spinner')).not.toBeInTheDocument(); | ||||
|     await waitForElementToBeRemoved(screen.getAllByTestId('Spinner')); | ||||
| 
 | ||||
|     expect(screen.getByTestId(selectors.WillBePublicCheckbox)).toBeEnabled(); | ||||
|     expect(screen.getByTestId(selectors.LimitedDSCheckbox)).toBeEnabled(); | ||||
|     expect(screen.getByTestId(selectors.CostIncreaseCheckbox)).toBeEnabled(); | ||||
|     expect(screen.getByTestId(selectors.EnableSwitch)).toBeEnabled(); | ||||
|     expect(screen.getByTestId(selectors.EnableAnnotationsSwitch)).toBeEnabled(); | ||||
|     expect(screen.getByTestId(selectors.EnableTimeRangeSwitch)).toBeEnabled(); | ||||
|     expect(screen.queryByTestId(selectors.DeleteButton)).not.toBeInTheDocument(); | ||||
| 
 | ||||
|     expect(screen.getByText('Create public dashboard')).toBeInTheDocument(); | ||||
|  | @ -227,7 +264,7 @@ describe('SharePublic - New config setup', () => { | |||
|   }); | ||||
|   it('when checkboxes are filled, then save button remains disabled', async () => { | ||||
|     await renderSharePublicDashboard({ panel: mockPanel, dashboard: mockDashboard, onDismiss: () => {} }); | ||||
|     expect(screen.queryByTestId('Spinner')).not.toBeInTheDocument(); | ||||
|     await waitForElementToBeRemoved(screen.getAllByTestId('Spinner')); | ||||
| 
 | ||||
|     fireEvent.click(screen.getByTestId(selectors.WillBePublicCheckbox)); | ||||
|     fireEvent.click(screen.getByTestId(selectors.LimitedDSCheckbox)); | ||||
|  | @ -238,7 +275,7 @@ describe('SharePublic - New config setup', () => { | |||
|   }); | ||||
|   it('when checkboxes and switch are filled, then save button is enabled', async () => { | ||||
|     await renderSharePublicDashboard({ panel: mockPanel, dashboard: mockDashboard, onDismiss: () => {} }); | ||||
|     expect(screen.queryByTestId('Spinner')).not.toBeInTheDocument(); | ||||
|     await waitForElementToBeRemoved(screen.getAllByTestId('Spinner')); | ||||
| 
 | ||||
|     fireEvent.click(screen.getByTestId(selectors.WillBePublicCheckbox)); | ||||
|     fireEvent.click(screen.getByTestId(selectors.LimitedDSCheckbox)); | ||||
|  | @ -254,18 +291,23 @@ describe('SharePublic - New config setup', () => { | |||
| }); | ||||
| 
 | ||||
| describe('SharePublic - Already persisted', () => { | ||||
|   const pubdashResponse: PublicDashboard = { | ||||
|     isEnabled: true, | ||||
|     annotationsEnabled: true, | ||||
|     timeSelectionEnabled: true, | ||||
|     uid: 'a-uid', | ||||
|     dashboardUid: '', | ||||
|     accessToken: 'an-access-token', | ||||
|   }; | ||||
| 
 | ||||
|   beforeEach(() => { | ||||
|     mockDashboard.meta.hasPublicDashboard = true; | ||||
|     server.use( | ||||
|       rest.get('/api/dashboards/uid/:dashboardUid/public-dashboards', (req, res, ctx) => { | ||||
|         return res( | ||||
|           ctx.status(200), | ||||
|           ctx.json({ | ||||
|             isEnabled: true, | ||||
|             annotationsEnabled: true, | ||||
|             uid: 'a-uid', | ||||
|             ...pubdashResponse, | ||||
|             dashboardUid: req.params.dashboardUid, | ||||
|             accessToken: 'an-access-token', | ||||
|           }) | ||||
|         ); | ||||
|       }) | ||||
|  | @ -294,6 +336,35 @@ describe('SharePublic - Already persisted', () => { | |||
|     expect(screen.getByTestId(selectors.EnableAnnotationsSwitch)).toBeEnabled(); | ||||
|     expect(screen.getByTestId(selectors.EnableAnnotationsSwitch)).toBeChecked(); | ||||
|   }); | ||||
|   it('when modal is opened, then time range switch is enabled and checked when its checked in the db', async () => { | ||||
|     await renderSharePublicDashboard({ panel: mockPanel, dashboard: mockDashboard, onDismiss: () => {} }); | ||||
|     await waitForElementToBeRemoved(screen.getAllByTestId('Spinner')); | ||||
| 
 | ||||
|     const enableTimeRangeSwitch = screen.getByTestId(selectors.EnableTimeRangeSwitch); | ||||
|     expect(enableTimeRangeSwitch).toBeEnabled(); | ||||
|     expect(enableTimeRangeSwitch).toBeChecked(); | ||||
|   }); | ||||
| 
 | ||||
|   it('when modal is opened, then time range switch is enabled and not checked when its not checked in the db', async () => { | ||||
|     server.use( | ||||
|       rest.get('/api/dashboards/uid/:dashboardUid/public-dashboards', (req, res, ctx) => { | ||||
|         return res( | ||||
|           ctx.status(200), | ||||
|           ctx.json({ | ||||
|             ...pubdashResponse, | ||||
|             timeSelectionEnabled: false, | ||||
|           }) | ||||
|         ); | ||||
|       }) | ||||
|     ); | ||||
| 
 | ||||
|     await renderSharePublicDashboard({ panel: mockPanel, dashboard: mockDashboard, onDismiss: () => {} }); | ||||
|     await waitForElementToBeRemoved(screen.getAllByTestId('Spinner')); | ||||
| 
 | ||||
|     const enableTimeRangeSwitch = screen.getByTestId(selectors.EnableTimeRangeSwitch); | ||||
|     expect(enableTimeRangeSwitch).toBeEnabled(); | ||||
|     expect(enableTimeRangeSwitch).not.toBeChecked(); | ||||
|   }); | ||||
|   it('when fetch is done, then loader spinner is gone, inputs are disabled and save button is enabled', async () => { | ||||
|     await renderSharePublicDashboard({ panel: mockPanel, dashboard: mockDashboard, onDismiss: () => {} }); | ||||
|     await waitForElementToBeRemoved(screen.getAllByTestId('Spinner')); | ||||
|  | @ -303,6 +374,7 @@ describe('SharePublic - Already persisted', () => { | |||
|     expect(screen.getByTestId(selectors.CostIncreaseCheckbox)).toBeDisabled(); | ||||
| 
 | ||||
|     expect(screen.getByTestId(selectors.EnableSwitch)).toBeEnabled(); | ||||
|     expect(screen.getByTestId(selectors.EnableTimeRangeSwitch)).toBeEnabled(); | ||||
|     expect(screen.getByText('Save public dashboard')).toBeInTheDocument(); | ||||
|     expect(screen.getByTestId(selectors.SaveConfigButton)).toBeEnabled(); | ||||
|     expect(screen.getByTestId(selectors.DeleteButton)).toBeEnabled(); | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ type SharePublicDashboardAcknowledgmentInputs = { | |||
| export type SharePublicDashboardInputs = { | ||||
|   isAnnotationsEnabled: boolean; | ||||
|   enabledSwitch: boolean; | ||||
|   isTimeRangeEnabled: boolean; | ||||
| } & SharePublicDashboardAcknowledgmentInputs; | ||||
| 
 | ||||
| export const SharePublicDashboard = (props: Props) => { | ||||
|  | @ -64,17 +65,13 @@ export const SharePublicDashboard = (props: Props) => { | |||
| 
 | ||||
|   const dashboardVariables = props.dashboard.getVariables(); | ||||
|   const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard; | ||||
|   const { hasPublicDashboard } = props.dashboard.meta; | ||||
| 
 | ||||
|   const { | ||||
|     isLoading: isGetLoading, | ||||
|     data: publicDashboard, | ||||
|     isError: isGetError, | ||||
|     isFetching, | ||||
|   } = useGetPublicDashboardQuery(props.dashboard.uid, { | ||||
|     // if we don't have a public dashboard, don't try to load public dashboard
 | ||||
|     skip: !hasPublicDashboard, | ||||
|   }); | ||||
|   } = useGetPublicDashboardQuery(props.dashboard.uid); | ||||
| 
 | ||||
|   const { | ||||
|     reset, | ||||
|  | @ -88,6 +85,7 @@ export const SharePublicDashboard = (props: Props) => { | |||
|       dataSourcesAcknowledgment: false, | ||||
|       usageAcknowledgment: false, | ||||
|       isAnnotationsEnabled: false, | ||||
|       isTimeRangeEnabled: false, | ||||
|       enabledSwitch: false, | ||||
|     }, | ||||
|   }); | ||||
|  | @ -110,6 +108,7 @@ export const SharePublicDashboard = (props: Props) => { | |||
|       dataSourcesAcknowledgment: isPublicDashboardPersisted, | ||||
|       usageAcknowledgment: isPublicDashboardPersisted, | ||||
|       isAnnotationsEnabled: publicDashboard?.annotationsEnabled, | ||||
|       isTimeRangeEnabled: publicDashboard?.timeSelectionEnabled, | ||||
|       enabledSwitch: publicDashboard?.isEnabled, | ||||
|     }); | ||||
|   }, [publicDashboard, reset]); | ||||
|  | @ -151,11 +150,12 @@ export const SharePublicDashboard = (props: Props) => { | |||
|         ...publicDashboard!, | ||||
|         isEnabled: values.enabledSwitch, | ||||
|         annotationsEnabled: values.isAnnotationsEnabled, | ||||
|         timeSelectionEnabled: values.isTimeRangeEnabled, | ||||
|       }, | ||||
|     }; | ||||
| 
 | ||||
|     // create or update based on whether we have existing uid
 | ||||
|     hasPublicDashboard ? updatePublicDashboard(req) : createPublicDashboard(req); | ||||
|     !!publicDashboard ? updatePublicDashboard(req) : createPublicDashboard(req); | ||||
|   }; | ||||
| 
 | ||||
|   const onDismissDelete = () => { | ||||
|  | @ -260,7 +260,7 @@ export const SharePublicDashboard = (props: Props) => { | |||
|             <HorizontalGroup> | ||||
|               <Layout orientation={isDesktop ? 0 : 1}> | ||||
|                 <Button type="submit" disabled={isSaveDisabled} data-testid={selectors.SaveConfigButton}> | ||||
|                   {hasPublicDashboard ? 'Save public dashboard' : 'Create public dashboard'} | ||||
|                   {!!publicDashboard ? 'Save public dashboard' : 'Create public dashboard'} | ||||
|                 </Button> | ||||
|                 {publicDashboard && hasWritePermissions && ( | ||||
|                   <DeletePublicDashboardButton | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ export interface PublicDashboard { | |||
|   uid: string; | ||||
|   dashboardUid: string; | ||||
|   timeSettings?: object; | ||||
|   timeSelectionEnabled: boolean; | ||||
| } | ||||
| 
 | ||||
| export interface DashboardResponse { | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ import { useEffectOnce } from 'react-use'; | |||
| import { AutoSizerProps } from 'react-virtualized-auto-sizer'; | ||||
| import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock'; | ||||
| 
 | ||||
| import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src'; | ||||
| import { locationService } from '@grafana/runtime'; | ||||
| import { Dashboard, DashboardCursorSync } from '@grafana/schema/src'; | ||||
| import { GrafanaContext } from 'app/core/context/GrafanaContext'; | ||||
|  | @ -47,6 +48,18 @@ jest.mock('app/types', () => ({ | |||
|   useDispatch: () => jest.fn(), | ||||
| })); | ||||
| 
 | ||||
| interface ScenarioContext { | ||||
|   mount: () => void; | ||||
|   rerender: ({ | ||||
|     propOverrides, | ||||
|     newState, | ||||
|   }: { | ||||
|     propOverrides?: Partial<Props>; | ||||
|     newState?: Partial<appTypes.StoreState>; | ||||
|   }) => void; | ||||
|   setup: (fn: () => void) => void; | ||||
| } | ||||
| 
 | ||||
| const renderWithProvider = ({ | ||||
|   props, | ||||
|   initialState, | ||||
|  | @ -68,17 +81,7 @@ const renderWithProvider = ({ | |||
|   ); | ||||
| }; | ||||
| 
 | ||||
| interface ScenarioContext { | ||||
|   mount: () => void; | ||||
|   rerender: ({ | ||||
|     propOverrides, | ||||
|     newState, | ||||
|   }: { | ||||
|     propOverrides?: Partial<Props>; | ||||
|     newState?: Partial<appTypes.StoreState>; | ||||
|   }) => void; | ||||
|   setup: (fn: () => void) => void; | ||||
| } | ||||
| const selectors = e2eSelectors.components; | ||||
| 
 | ||||
| const getTestDashboard = (overrides?: Partial<Dashboard>, metaOverrides?: Partial<DashboardMeta>): DashboardModel => { | ||||
|   const data: Dashboard = Object.assign( | ||||
|  | @ -89,6 +92,7 @@ const getTestDashboard = (overrides?: Partial<Dashboard>, metaOverrides?: Partia | |||
|       graphTooltip: DashboardCursorSync.Off, | ||||
|       schemaVersion: 1, | ||||
|       style: 'dark', | ||||
|       timepicker: { hidden: true }, | ||||
|       panels: [ | ||||
|         { | ||||
|           id: 1, | ||||
|  | @ -181,7 +185,7 @@ describe('PublicDashboardPage', () => { | |||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   dashboardPageScenario('Given a simple dashboard', (ctx) => { | ||||
|   dashboardPageScenario('Given a simple public dashboard', (ctx) => { | ||||
|     ctx.setup(() => { | ||||
|       ctx.mount(); | ||||
|       ctx.rerender({ | ||||
|  | @ -203,5 +207,36 @@ describe('PublicDashboardPage', () => { | |||
|     it('Should update title', () => { | ||||
|       expect(document.title).toBe('My dashboard - Grafana'); | ||||
|     }); | ||||
| 
 | ||||
|     it('Should not render neither time range nor refresh picker buttons', () => { | ||||
|       expect(screen.queryByTestId(selectors.TimePicker.openButton)).not.toBeInTheDocument(); | ||||
|       expect(screen.queryByTestId(selectors.RefreshPicker.runButtonV2)).not.toBeInTheDocument(); | ||||
|       expect(screen.queryByTestId(selectors.RefreshPicker.intervalButtonV2)).not.toBeInTheDocument(); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   dashboardPageScenario('Given a public dashboard with time range enabled', (ctx) => { | ||||
|     ctx.setup(() => { | ||||
|       ctx.mount(); | ||||
|       ctx.rerender({ | ||||
|         newState: { | ||||
|           dashboard: { | ||||
|             getModel: () => | ||||
|               getTestDashboard({ | ||||
|                 timepicker: { hidden: false, collapse: false, enable: true, refresh_intervals: [], time_options: [] }, | ||||
|               }), | ||||
|             initError: null, | ||||
|             initPhase: DashboardInitPhase.Completed, | ||||
|             permissions: [], | ||||
|           }, | ||||
|         }, | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     it('Should render time range and refresh picker buttons', () => { | ||||
|       expect(screen.getByTestId(selectors.TimePicker.openButton)).toBeInTheDocument(); | ||||
|       expect(screen.getByTestId(selectors.RefreshPicker.runButtonV2)).toBeInTheDocument(); | ||||
|       expect(screen.getByTestId(selectors.RefreshPicker.intervalButtonV2)).toBeInTheDocument(); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|  |  | |||
|  | @ -73,7 +73,11 @@ const PublicDashboardPage = (props: Props) => { | |||
|       const prevUrlParams = prevProps?.queryParams; | ||||
|       const urlParams = props.queryParams; | ||||
| 
 | ||||
|       if (urlParams?.from !== prevUrlParams?.from || urlParams?.to !== prevUrlParams?.to) { | ||||
|       const updateTimeRangeFromUrl = | ||||
|         (urlParams?.from !== prevUrlParams?.from || urlParams?.to !== prevUrlParams?.to) && | ||||
|         !dashboard?.timepicker.hidden; | ||||
| 
 | ||||
|       if (updateTimeRangeFromUrl) { | ||||
|         getTimeSrv().updateTimeRangeFromUrl(); | ||||
|       } | ||||
| 
 | ||||
|  | @ -81,7 +85,7 @@ const PublicDashboardPage = (props: Props) => { | |||
|         getTimeSrv().setAutoRefresh(urlParams.refresh); | ||||
|       } | ||||
|     } | ||||
|   }, [prevProps, location.search, props.queryParams]); | ||||
|   }, [prevProps, location.search, props.queryParams, dashboard?.timepicker.hidden]); | ||||
| 
 | ||||
|   if (!dashboard) { | ||||
|     return <DashboardLoading initPhase={dashboardState.initPhase} />; | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import { of } from 'rxjs'; | ||||
| 
 | ||||
| import { DataQueryRequest, DataSourceInstanceSettings, DataSourceRef, TimeRange } from '@grafana/data'; | ||||
| import { DataQueryRequest, DataSourceInstanceSettings, DataSourceRef, dateTime, TimeRange } from '@grafana/data'; | ||||
| import { BackendSrvRequest, BackendSrv, DataSourceWithBackend } from '@grafana/runtime'; | ||||
| import { GrafanaQueryType } from 'app/plugins/datasource/grafana/types'; | ||||
| import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSource'; | ||||
|  | @ -115,6 +115,14 @@ describe('PublicDashboardDatasource', () => { | |||
|       intervalMs: 5000, | ||||
|       targets: [{ refId: 'A' }, { refId: 'B', datasource: { type: 'sample' } }], | ||||
|       panelId, | ||||
|       range: { | ||||
|         from: dateTime('2022-01-01T15:55:00Z'), | ||||
|         to: dateTime('2022-07-12T15:55:00Z'), | ||||
|         raw: { | ||||
|           from: 'now-15m', | ||||
|           to: 'now', | ||||
|         }, | ||||
|       }, | ||||
|       publicDashboardAccessToken, | ||||
|     } as DataQueryRequest); | ||||
| 
 | ||||
|  |  | |||
|  | @ -80,7 +80,14 @@ export class PublicDashboardDataSource extends DataSourceApi<DataQuery, DataSour | |||
|    * Ideally final -- any other implementation may not work as expected | ||||
|    */ | ||||
|   query(request: DataQueryRequest<DataQuery>): Observable<DataQueryResponse> { | ||||
|     const { intervalMs, maxDataPoints, requestId, publicDashboardAccessToken, panelId } = request; | ||||
|     const { | ||||
|       intervalMs, | ||||
|       maxDataPoints, | ||||
|       requestId, | ||||
|       publicDashboardAccessToken, | ||||
|       panelId, | ||||
|       range: { from: fromRange, to: toRange }, | ||||
|     } = request; | ||||
|     let queries: DataQuery[]; | ||||
| 
 | ||||
|     // Return early if no queries exist
 | ||||
|  | @ -100,7 +107,11 @@ export class PublicDashboardDataSource extends DataSourceApi<DataQuery, DataSour | |||
| 
 | ||||
|     // Its a datasource query
 | ||||
|     else { | ||||
|       const body = { intervalMs, maxDataPoints }; | ||||
|       const body = { | ||||
|         intervalMs, | ||||
|         maxDataPoints, | ||||
|         timeRange: { from: fromRange.valueOf().toString(), to: toRange.valueOf().toString() }, | ||||
|       }; | ||||
| 
 | ||||
|       return getBackendSrv() | ||||
|         .fetch<BackendDataSourceResponse>({ | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ import * as H from 'history'; | |||
| import { ContextSrvStub } from 'test/specs/helpers'; | ||||
| 
 | ||||
| import { dateTime, isDateTime } from '@grafana/data'; | ||||
| import { HistoryWrapper, locationService, setLocationService } from '@grafana/runtime'; | ||||
| import { config, HistoryWrapper, locationService, setLocationService } from '@grafana/runtime'; | ||||
| 
 | ||||
| import { TimeSrv } from './TimeSrv'; | ||||
| 
 | ||||
|  | @ -85,6 +85,37 @@ describe('timeSrv', () => { | |||
|       expect(timeSrv.refresh).toBe(false); | ||||
|     }); | ||||
| 
 | ||||
|     describe('public dashboard', () => { | ||||
|       beforeEach(() => { | ||||
|         _dashboard = { | ||||
|           time: { from: 'now-6h', to: 'now' }, | ||||
|           getTimezone: jest.fn(() => 'browser'), | ||||
|           refresh: false, | ||||
|           timeRangeUpdated: jest.fn(() => {}), | ||||
|         }; | ||||
| 
 | ||||
|         locationService.push('/d/id?from=now-24h&to=now'); | ||||
|         config.isPublicDashboardView = true; | ||||
|         timeSrv = new TimeSrv(new ContextSrvStub()); | ||||
|       }); | ||||
| 
 | ||||
|       it("should ignore from and to if it's a public dashboard and time picker is hidden", () => { | ||||
|         timeSrv.init({ ..._dashboard, timepicker: { hidden: true } }); | ||||
|         const time = timeSrv.timeRange(); | ||||
| 
 | ||||
|         expect(time.raw.from).toBe('now-6h'); | ||||
|         expect(time.raw.to).toBe('now'); | ||||
|       }); | ||||
| 
 | ||||
|       it("should not ignore from and to if it's a public dashboard but time picker is not hidden", () => { | ||||
|         timeSrv.init({ ..._dashboard, timepicker: { hidden: false } }); | ||||
|         const time = timeSrv.timeRange(); | ||||
| 
 | ||||
|         expect(time.raw.from).toBe('now-24h'); | ||||
|         expect(time.raw.to).toBe('now'); | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     it('should handle formatted dates without time', () => { | ||||
|       locationService.push('/d/id?from=20140410&to=20140520'); | ||||
| 
 | ||||
|  |  | |||
|  | @ -148,8 +148,7 @@ export class TimeSrv { | |||
|   } | ||||
| 
 | ||||
|   private initTimeFromUrl() { | ||||
|     // If we are in a public dashboard ignore the time range in the url
 | ||||
|     if (config.isPublicDashboardView) { | ||||
|     if (config.isPublicDashboardView && this.timeModel?.timepicker?.hidden) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|  | @ -280,11 +279,6 @@ export class TimeSrv { | |||
|   } | ||||
| 
 | ||||
|   setTime(time: RawTimeRange, updateUrl = true) { | ||||
|     // If we are in a public dashboard ignore time range changes
 | ||||
|     if (config.isPublicDashboardView) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     extend(this.time, time); | ||||
| 
 | ||||
|     // disable refresh if zoom in or zoom out
 | ||||
|  |  | |||
|  | @ -44,7 +44,6 @@ export interface DashboardMeta { | |||
|   publicDashboardAccessToken?: string; | ||||
|   publicDashboardUid?: string; | ||||
|   publicDashboardEnabled?: boolean; | ||||
|   hasPublicDashboard?: boolean; | ||||
|   dashboardNotFound?: boolean; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ import { each, template } from 'lodash'; | |||
| import { RawTimeRange, PanelPluginMeta, dateMath } from '@grafana/data'; | ||||
| import { GrafanaRootScope } from 'app/angular/GrafanaCtrl'; | ||||
| import config from 'app/core/config'; | ||||
| import { ContextSrv } from 'app/core/services/context_srv'; | ||||
| import { PanelModel } from 'app/features/dashboard/state/PanelModel'; | ||||
| 
 | ||||
| import { angularMocks, sinon } from '../lib/common'; | ||||
|  | @ -133,7 +134,7 @@ export class TimeSrvStub { | |||
|   } | ||||
| } | ||||
| 
 | ||||
| export class ContextSrvStub { | ||||
| export class ContextSrvStub extends ContextSrv { | ||||
|   isGrafanaVisible = jest.fn(); | ||||
| 
 | ||||
|   getValidInterval() { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue