mirror of
https://github.com/grafana/grafana.git
synced 2025-02-10 23:55:47 -06:00
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
Block a user