From 18e8d1e28d61570abbde68ee4a7bc6318e406826 Mon Sep 17 00:00:00 2001 From: juanicabanas Date: Wed, 18 Jan 2023 10:54:19 -0300 Subject: [PATCH] PublicDashboards: Time range settings (#61585) --- .../src/selectors/pages.ts | 1 + pkg/api/dashboard.go | 3 - pkg/api/dtos/dashboard.go | 1 - .../dashboard/api/publicDashboardApi.ts | 5 +- .../SharePublicDashboard/Configuration.tsx | 8 +- .../SharePublicDashboard.test.tsx | 100 +++++++++++++++--- .../SharePublicDashboard.tsx | 14 +-- .../SharePublicDashboardUtils.ts | 1 + .../containers/PublicDashboardPage.test.tsx | 59 ++++++++--- .../containers/PublicDashboardPage.tsx | 8 +- .../PublicDashboardDataSource.test.ts | 10 +- .../services/PublicDashboardDataSource.ts | 15 ++- .../dashboard/services/TimeSrv.test.ts | 33 +++++- .../features/dashboard/services/TimeSrv.ts | 8 +- public/app/types/dashboard.ts | 1 - public/test/specs/helpers.ts | 3 +- 16 files changed, 213 insertions(+), 57 deletions(-) diff --git a/packages/grafana-e2e-selectors/src/selectors/pages.ts b/packages/grafana-e2e-selectors/src/selectors/pages.ts index 9e20696760a..cf6aa83e452 100644 --- a/packages/grafana-e2e-selectors/src/selectors/pages.ts +++ b/packages/grafana-e2e-selectors/src/selectors/pages.ts @@ -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: { diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 3ec3f9871f1..a2e051d23ef 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -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 diff --git a/pkg/api/dtos/dashboard.go b/pkg/api/dtos/dashboard.go index 0d13051f5be..3918374cf7f 100644 --- a/pkg/api/dtos/dashboard.go +++ b/pkg/api/dtos/dashboard.go @@ -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"` diff --git a/public/app/features/dashboard/api/publicDashboardApi.ts b/public/app/features/dashboard/api/publicDashboardApi.ts index 4249547a967..4c3a67f3071 100644 --- a/public/app/features/dashboard/api/publicDashboardApi.ts +++ b/public/app/features/dashboard/api/publicDashboardApi.ts @@ -38,7 +38,7 @@ export const publicDashboardApi = createApi({ tagTypes: ['PublicDashboard', 'AuditTablePublicDashboard'], refetchOnMountOrArgChange: true, endpoints: (builder) => ({ - getPublicDashboard: builder.query({ + getPublicDashboard: builder.query({ 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, }); diff --git a/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/Configuration.tsx b/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/Configuration.tsx index 6263dd1d234..e6447e1e93e 100644 --- a/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/Configuration.tsx +++ b/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/Configuration.tsx @@ -34,9 +34,15 @@ export const Configuration = ({
- + {}} /> + + + + ({ @@ -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(); diff --git a/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.tsx b/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.tsx index b1bdff1ab72..30ce9afe4e7 100644 --- a/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.tsx +++ b/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.tsx @@ -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) => { {publicDashboard && hasWritePermissions && ( ({ useDispatch: () => jest.fn(), })); +interface ScenarioContext { + mount: () => void; + rerender: ({ + propOverrides, + newState, + }: { + propOverrides?: Partial; + newState?: Partial; + }) => void; + setup: (fn: () => void) => void; +} + const renderWithProvider = ({ props, initialState, @@ -68,17 +81,7 @@ const renderWithProvider = ({ ); }; -interface ScenarioContext { - mount: () => void; - rerender: ({ - propOverrides, - newState, - }: { - propOverrides?: Partial; - newState?: Partial; - }) => void; - setup: (fn: () => void) => void; -} +const selectors = e2eSelectors.components; const getTestDashboard = (overrides?: Partial, metaOverrides?: Partial): DashboardModel => { const data: Dashboard = Object.assign( @@ -89,6 +92,7 @@ const getTestDashboard = (overrides?: Partial, 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(); + }); }); }); diff --git a/public/app/features/dashboard/containers/PublicDashboardPage.tsx b/public/app/features/dashboard/containers/PublicDashboardPage.tsx index 761fdf47abe..531f779caa0 100644 --- a/public/app/features/dashboard/containers/PublicDashboardPage.tsx +++ b/public/app/features/dashboard/containers/PublicDashboardPage.tsx @@ -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 ; diff --git a/public/app/features/dashboard/services/PublicDashboardDataSource.test.ts b/public/app/features/dashboard/services/PublicDashboardDataSource.test.ts index 8b274aaae03..15fc9b3312d 100644 --- a/public/app/features/dashboard/services/PublicDashboardDataSource.test.ts +++ b/public/app/features/dashboard/services/PublicDashboardDataSource.test.ts @@ -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); diff --git a/public/app/features/dashboard/services/PublicDashboardDataSource.ts b/public/app/features/dashboard/services/PublicDashboardDataSource.ts index 9f1696de4b3..a49a696c1b4 100644 --- a/public/app/features/dashboard/services/PublicDashboardDataSource.ts +++ b/public/app/features/dashboard/services/PublicDashboardDataSource.ts @@ -80,7 +80,14 @@ export class PublicDashboardDataSource extends DataSourceApi): Observable { - 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({ diff --git a/public/app/features/dashboard/services/TimeSrv.test.ts b/public/app/features/dashboard/services/TimeSrv.test.ts index c9d4b6b341e..751251a452f 100644 --- a/public/app/features/dashboard/services/TimeSrv.test.ts +++ b/public/app/features/dashboard/services/TimeSrv.test.ts @@ -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'); diff --git a/public/app/features/dashboard/services/TimeSrv.ts b/public/app/features/dashboard/services/TimeSrv.ts index ff3f1b6c2b3..42a83a9510c 100644 --- a/public/app/features/dashboard/services/TimeSrv.ts +++ b/public/app/features/dashboard/services/TimeSrv.ts @@ -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 diff --git a/public/app/types/dashboard.ts b/public/app/types/dashboard.ts index cd5bf15fb71..0f15ecc67b8 100644 --- a/public/app/types/dashboard.ts +++ b/public/app/types/dashboard.ts @@ -44,7 +44,6 @@ export interface DashboardMeta { publicDashboardAccessToken?: string; publicDashboardUid?: string; publicDashboardEnabled?: boolean; - hasPublicDashboard?: boolean; dashboardNotFound?: boolean; } diff --git a/public/test/specs/helpers.ts b/public/test/specs/helpers.ts index 0bddf53b9cd..156ae47e6f3 100644 --- a/public/test/specs/helpers.ts +++ b/public/test/specs/helpers.ts @@ -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() {