PublicDashboards: Time range settings (#61585)

This commit is contained in:
juanicabanas 2023-01-18 10:54:19 -03:00 committed by GitHub
parent 1454b1b40a
commit 18e8d1e28d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 213 additions and 57 deletions

View File

@ -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: {

View File

@ -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

View File

@ -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"`

View File

@ -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,
});

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -13,6 +13,7 @@ export interface PublicDashboard {
uid: string;
dashboardUid: string;
timeSettings?: object;
timeSelectionEnabled: boolean;
}
export interface DashboardResponse {

View File

@ -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();
});
});
});

View File

@ -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} />;

View File

@ -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);

View File

@ -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>({

View File

@ -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');

View File

@ -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

View File

@ -44,7 +44,6 @@ export interface DashboardMeta {
publicDashboardAccessToken?: string;
publicDashboardUid?: string;
publicDashboardEnabled?: boolean;
hasPublicDashboard?: boolean;
dashboardNotFound?: boolean;
}

View File

@ -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() {