mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
PublicDashboards: Time range setting added (#60487)
Time range added for public dashboard: - Enable/Disable switch added in public dashboard configuration. - Time range picker shown in public dashboard for viewer user
This commit is contained in:
@@ -70,6 +70,7 @@ export const publicDashboardApi = createApi({
|
||||
hasPublicDashboard: true,
|
||||
publicDashboardUid: data.uid,
|
||||
publicDashboardEnabled: data.isEnabled,
|
||||
publicDashboardTimeSelectionEnabled: data.timeSelectionEnabled,
|
||||
});
|
||||
},
|
||||
invalidatesTags: (result, error, { payload }) => [{ type: 'PublicDashboard', id: payload.dashboardUid }],
|
||||
@@ -90,6 +91,7 @@ export const publicDashboardApi = createApi({
|
||||
hasPublicDashboard: true,
|
||||
publicDashboardUid: data.uid,
|
||||
publicDashboardEnabled: data.isEnabled,
|
||||
publicDashboardTimeSelectionEnabled: data.timeSelectionEnabled,
|
||||
});
|
||||
},
|
||||
invalidatesTags: (result, error, { payload }) => [{ type: 'PublicDashboard', id: payload.dashboardUid }],
|
||||
|
||||
@@ -36,6 +36,7 @@ describe('Public dashboard title tag', () => {
|
||||
isFullscreen={false}
|
||||
onAddPanel={() => {}}
|
||||
title="test"
|
||||
isPublic={false}
|
||||
/>
|
||||
</Router>
|
||||
</GrafanaContext.Provider>
|
||||
|
||||
@@ -58,6 +58,7 @@ export interface OwnProps {
|
||||
title: string;
|
||||
shareModalActiveTab?: string;
|
||||
onAddPanel: () => void;
|
||||
isPublic: boolean;
|
||||
}
|
||||
|
||||
interface DashNavButtonModel {
|
||||
@@ -198,7 +199,7 @@ export const DashNav = React.memo<Props>((props) => {
|
||||
const { canStar, canShare, isStarred } = dashboard.meta;
|
||||
const buttons: ReactNode[] = [];
|
||||
|
||||
if (kioskMode || isPlaylistRunning()) {
|
||||
if (kioskMode || isPlaylistRunning() || props.isPublic) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -321,6 +322,10 @@ export const DashNav = React.memo<Props>((props) => {
|
||||
return [renderPlaylistControls(), renderTimeControls()];
|
||||
}
|
||||
|
||||
if (props.isPublic && !!props.dashboard.meta.publicDashboardTimeSelectionEnabled) {
|
||||
return [renderTimeControls()];
|
||||
}
|
||||
|
||||
if (kioskMode === KioskMode.TV) {
|
||||
return [renderTimeControls(), tvButton];
|
||||
}
|
||||
@@ -418,8 +423,8 @@ export const DashNav = React.memo<Props>((props) => {
|
||||
|
||||
return (
|
||||
<PageToolbar
|
||||
pageIcon={isFullscreen ? undefined : 'apps'}
|
||||
title={title}
|
||||
pageIcon={isFullscreen || props.isPublic ? undefined : 'apps'}
|
||||
title={props.isPublic ? undefined : title}
|
||||
parent={folderTitle}
|
||||
titleHref={titleHref}
|
||||
parentHref={parentHref}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -17,6 +17,8 @@ import { configureStore } from 'app/store/configureStore';
|
||||
|
||||
import { ShareModal } from '../ShareModal';
|
||||
|
||||
import { PublicDashboard } from './SharePublicDashboardUtils';
|
||||
|
||||
const server = setupServer();
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
@@ -158,6 +160,7 @@ 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.SaveConfigButton)).toBeDisabled();
|
||||
expect(screen.queryByTestId(selectors.DeleteButton)).not.toBeInTheDocument();
|
||||
});
|
||||
@@ -176,6 +179,7 @@ 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.getByTestId(selectors.SaveConfigButton)).toBeDisabled();
|
||||
@@ -218,6 +222,7 @@ describe('SharePublic - New config setup', () => {
|
||||
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();
|
||||
@@ -252,6 +257,15 @@ 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(
|
||||
@@ -259,11 +273,8 @@ describe('SharePublic - Already persisted', () => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
isEnabled: true,
|
||||
annotationsEnabled: true,
|
||||
uid: 'a-uid',
|
||||
...pubdashResponse,
|
||||
dashboardUid: req.params.dashboardUid,
|
||||
accessToken: 'an-access-token',
|
||||
})
|
||||
);
|
||||
})
|
||||
@@ -292,6 +303,33 @@ 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'));
|
||||
|
||||
expect(screen.getByTestId(selectors.EnableTimeRangeSwitch)).toBeEnabled();
|
||||
expect(screen.getByTestId(selectors.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'));
|
||||
|
||||
expect(screen.getByTestId(selectors.EnableTimeRangeSwitch)).toBeEnabled();
|
||||
expect(screen.getByTestId(selectors.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'));
|
||||
@@ -301,6 +339,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();
|
||||
|
||||
@@ -53,6 +53,7 @@ type SharePublicDashboardAcknowledgmentInputs = {
|
||||
|
||||
export type SharePublicDashboardInputs = {
|
||||
isAnnotationsEnabled: boolean;
|
||||
isTimeRangeEnabled: boolean;
|
||||
enabledSwitch: boolean;
|
||||
} & SharePublicDashboardAcknowledgmentInputs;
|
||||
|
||||
@@ -88,6 +89,7 @@ export const SharePublicDashboard = (props: Props) => {
|
||||
dataSourcesAcknowledgment: false,
|
||||
usageAcknowledgment: false,
|
||||
isAnnotationsEnabled: false,
|
||||
isTimeRangeEnabled: false,
|
||||
enabledSwitch: false,
|
||||
},
|
||||
});
|
||||
@@ -110,6 +112,7 @@ export const SharePublicDashboard = (props: Props) => {
|
||||
dataSourcesAcknowledgment: isPublicDashboardPersisted,
|
||||
usageAcknowledgment: isPublicDashboardPersisted,
|
||||
isAnnotationsEnabled: publicDashboard?.annotationsEnabled,
|
||||
isTimeRangeEnabled: publicDashboard?.timeSelectionEnabled,
|
||||
enabledSwitch: publicDashboard?.isEnabled,
|
||||
});
|
||||
}, [publicDashboard, reset]);
|
||||
@@ -151,6 +154,7 @@ export const SharePublicDashboard = (props: Props) => {
|
||||
...publicDashboard!,
|
||||
isEnabled: values.enabledSwitch,
|
||||
annotationsEnabled: values.isAnnotationsEnabled,
|
||||
timeSelectionEnabled: values.isTimeRangeEnabled,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ export interface PublicDashboard {
|
||||
uid: string;
|
||||
dashboardUid: string;
|
||||
timeSettings?: object;
|
||||
timeSelectionEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface DashboardResponse {
|
||||
|
||||
@@ -336,4 +336,19 @@ describe('DashboardPage', () => {
|
||||
expect(screen.queryAllByLabelText(selectors.pages.Dashboard.SubMenu.submenu)).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
dashboardPageScenario('When dashboard is public and timeSelection is enabled', (ctx) => {
|
||||
ctx.setup(() => {
|
||||
locationService.partial({ kiosk: false });
|
||||
ctx.mount({
|
||||
queryParams: {},
|
||||
dashboard: getTestDashboard(null, { publicDashboardTimeSelectionEnabled: true }),
|
||||
});
|
||||
ctx.rerender({ dashboard: ctx.dashboard, isPublic: true });
|
||||
});
|
||||
|
||||
it('should render page toolbar because timeSelection is enabled, but not submenu', () => {
|
||||
expect(screen.queryAllByTestId(selectors.pages.Dashboard.DashNav.navV2)).toHaveLength(1);
|
||||
expect(screen.queryAllByLabelText(selectors.pages.Dashboard.SubMenu.submenu)).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -352,7 +352,8 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
|
||||
const inspectPanel = this.getInspectPanel();
|
||||
const showSubMenu = !editPanel && !kioskMode && !this.props.queryParams.editview;
|
||||
|
||||
const toolbar = kioskMode !== KioskMode.Full && !queryParams.editview && (
|
||||
const toolbar = ((kioskMode !== KioskMode.Full && !queryParams.editview) ||
|
||||
(isPublic && !!this.props.dashboard?.meta.publicDashboardTimeSelectionEnabled)) && (
|
||||
<header data-testid={selectors.pages.Dashboard.DashNav.navV2}>
|
||||
<DashNav
|
||||
dashboard={dashboard}
|
||||
@@ -363,6 +364,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
|
||||
kioskMode={kioskMode}
|
||||
hideTimePicker={dashboard.timepicker.hidden}
|
||||
shareModalActiveTab={this.props.queryParams.shareView}
|
||||
isPublic={!!isPublic}
|
||||
/>
|
||||
</header>
|
||||
);
|
||||
|
||||
@@ -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>({
|
||||
|
||||
@@ -280,11 +280,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,6 +44,7 @@ export interface DashboardMeta {
|
||||
publicDashboardAccessToken?: string;
|
||||
publicDashboardUid?: string;
|
||||
publicDashboardEnabled?: boolean;
|
||||
publicDashboardTimeSelectionEnabled?: boolean;
|
||||
hasPublicDashboard?: boolean;
|
||||
dashboardNotFound?: boolean;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user