mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboards: Switch to useParams hook (#94060)
* Update DashboardScenePage * Update SoloPanelPage * Update DashboardPage * Cleanup * Switch to useLocation * Do not use location from history
This commit is contained in:
parent
4bc7a35f56
commit
9680722b78
@ -1,6 +1,7 @@
|
|||||||
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
|
import { useParams } from 'react-router-dom-v5-compat';
|
||||||
import { TestProvider } from 'test/helpers/TestProvider';
|
import { TestProvider } from 'test/helpers/TestProvider';
|
||||||
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
|
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
|
||||||
|
|
||||||
@ -48,6 +49,11 @@ jest.mock('@grafana/runtime', () => ({
|
|||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('react-router-dom-v5-compat', () => ({
|
||||||
|
...jest.requireActual('react-router-dom-v5-compat'),
|
||||||
|
useParams: jest.fn().mockReturnValue({ uid: 'my-dash-uid' }),
|
||||||
|
}));
|
||||||
|
|
||||||
const getPluginLinkExtensionsMock = jest.mocked(getPluginLinkExtensions);
|
const getPluginLinkExtensionsMock = jest.mocked(getPluginLinkExtensions);
|
||||||
|
|
||||||
function setup({ routeProps }: { routeProps?: Partial<GrafanaRouteComponentProps> } = {}) {
|
function setup({ routeProps }: { routeProps?: Partial<GrafanaRouteComponentProps> } = {}) {
|
||||||
@ -55,12 +61,6 @@ function setup({ routeProps }: { routeProps?: Partial<GrafanaRouteComponentProps
|
|||||||
const defaultRouteProps = getRouteComponentProps();
|
const defaultRouteProps = getRouteComponentProps();
|
||||||
const props: Props = {
|
const props: Props = {
|
||||||
...defaultRouteProps,
|
...defaultRouteProps,
|
||||||
match: {
|
|
||||||
...defaultRouteProps.match,
|
|
||||||
params: {
|
|
||||||
uid: 'my-dash-uid',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
...routeProps,
|
...routeProps,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -163,7 +163,7 @@ describe('DashboardScenePage', () => {
|
|||||||
it('Can render dashboard', async () => {
|
it('Can render dashboard', async () => {
|
||||||
setup();
|
setup();
|
||||||
|
|
||||||
await waitForDashbordToRender();
|
await waitForDashboardToRender();
|
||||||
|
|
||||||
expect(await screen.findByTitle('Panel A')).toBeInTheDocument();
|
expect(await screen.findByTitle('Panel A')).toBeInTheDocument();
|
||||||
expect(await screen.findByText('Content A')).toBeInTheDocument();
|
expect(await screen.findByText('Content A')).toBeInTheDocument();
|
||||||
@ -175,7 +175,7 @@ describe('DashboardScenePage', () => {
|
|||||||
it('routeReloadCounter should trigger reload', async () => {
|
it('routeReloadCounter should trigger reload', async () => {
|
||||||
const { rerender, props } = setup();
|
const { rerender, props } = setup();
|
||||||
|
|
||||||
await waitForDashbordToRender();
|
await waitForDashboardToRender();
|
||||||
|
|
||||||
expect(await screen.findByTitle('Panel A')).toBeInTheDocument();
|
expect(await screen.findByTitle('Panel A')).toBeInTheDocument();
|
||||||
|
|
||||||
@ -196,7 +196,7 @@ describe('DashboardScenePage', () => {
|
|||||||
it('Can inspect panel', async () => {
|
it('Can inspect panel', async () => {
|
||||||
setup();
|
setup();
|
||||||
|
|
||||||
await waitForDashbordToRender();
|
await waitForDashboardToRender();
|
||||||
|
|
||||||
expect(screen.queryByText('Inspect: Panel B')).not.toBeInTheDocument();
|
expect(screen.queryByText('Inspect: Panel B')).not.toBeInTheDocument();
|
||||||
|
|
||||||
@ -218,7 +218,7 @@ describe('DashboardScenePage', () => {
|
|||||||
it('Can view panel in fullscreen', async () => {
|
it('Can view panel in fullscreen', async () => {
|
||||||
setup();
|
setup();
|
||||||
|
|
||||||
await waitForDashbordToRender();
|
await waitForDashboardToRender();
|
||||||
|
|
||||||
expect(await screen.findByTitle('Panel A')).toBeInTheDocument();
|
expect(await screen.findByTitle('Panel A')).toBeInTheDocument();
|
||||||
|
|
||||||
@ -239,7 +239,7 @@ describe('DashboardScenePage', () => {
|
|||||||
it('shows and hides empty state when panels are added and removed', async () => {
|
it('shows and hides empty state when panels are added and removed', async () => {
|
||||||
setup();
|
setup();
|
||||||
|
|
||||||
await waitForDashbordToRender();
|
await waitForDashboardToRender();
|
||||||
|
|
||||||
expect(await screen.queryByText('Start your new dashboard by adding a visualization')).not.toBeInTheDocument();
|
expect(await screen.queryByText('Start your new dashboard by adding a visualization')).not.toBeInTheDocument();
|
||||||
|
|
||||||
@ -272,7 +272,7 @@ describe('DashboardScenePage', () => {
|
|||||||
|
|
||||||
setup();
|
setup();
|
||||||
|
|
||||||
await waitForDashbordToRender();
|
await waitForDashboardToRender();
|
||||||
|
|
||||||
const panelAMenu = await screen.findByLabelText('Menu for panel with title Panel A');
|
const panelAMenu = await screen.findByLabelText('Menu for panel with title Panel A');
|
||||||
expect(panelAMenu).toBeInTheDocument();
|
expect(panelAMenu).toBeInTheDocument();
|
||||||
@ -283,21 +283,17 @@ describe('DashboardScenePage', () => {
|
|||||||
|
|
||||||
describe('home page', () => {
|
describe('home page', () => {
|
||||||
it('should render the dashboard when the route is home', async () => {
|
it('should render the dashboard when the route is home', async () => {
|
||||||
|
(useParams as jest.Mock).mockReturnValue({});
|
||||||
setup({
|
setup({
|
||||||
routeProps: {
|
routeProps: {
|
||||||
route: {
|
route: {
|
||||||
...getRouteComponentProps().route,
|
...getRouteComponentProps().route,
|
||||||
routeName: DashboardRoutes.Home,
|
routeName: DashboardRoutes.Home,
|
||||||
},
|
},
|
||||||
match: {
|
|
||||||
...getRouteComponentProps().match,
|
|
||||||
path: '/',
|
|
||||||
params: {},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitForDashbordToRender();
|
await waitForDashboardToRender();
|
||||||
|
|
||||||
expect(await screen.findByTitle('Panel A')).toBeInTheDocument();
|
expect(await screen.findByTitle('Panel A')).toBeInTheDocument();
|
||||||
expect(await screen.findByText('Content A')).toBeInTheDocument();
|
expect(await screen.findByText('Content A')).toBeInTheDocument();
|
||||||
@ -328,7 +324,7 @@ function CustomVizPanel(props: VizProps) {
|
|||||||
return <div>{props.options.content}</div>;
|
return <div>{props.options.content}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function waitForDashbordToRender() {
|
async function waitForDashboardToRender() {
|
||||||
expect(await screen.findByText('Last 6 hours')).toBeInTheDocument();
|
expect(await screen.findByText('Last 6 hours')).toBeInTheDocument();
|
||||||
expect(await screen.findByTitle('Panel A')).toBeInTheDocument();
|
expect(await screen.findByTitle('Panel A')).toBeInTheDocument();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// Libraries
|
// Libraries
|
||||||
import { useEffect, useMemo } from 'react';
|
import { useEffect, useMemo } from 'react';
|
||||||
|
import { useParams } from 'react-router-dom-v5-compat';
|
||||||
import { usePrevious } from 'react-use';
|
import { usePrevious } from 'react-use';
|
||||||
|
|
||||||
import { PageLayoutType } from '@grafana/data';
|
import { PageLayoutType } from '@grafana/data';
|
||||||
@ -17,13 +18,15 @@ import { DashboardPrompt } from '../saving/DashboardPrompt';
|
|||||||
|
|
||||||
import { getDashboardScenePageStateManager } from './DashboardScenePageStateManager';
|
import { getDashboardScenePageStateManager } from './DashboardScenePageStateManager';
|
||||||
|
|
||||||
export interface Props extends GrafanaRouteComponentProps<DashboardPageRouteParams, DashboardPageRouteSearchParams> {}
|
export interface Props
|
||||||
|
extends Omit<GrafanaRouteComponentProps<DashboardPageRouteParams, DashboardPageRouteSearchParams>, 'match'> {}
|
||||||
|
|
||||||
export function DashboardScenePage({ match, route, queryParams, history }: Props) {
|
export function DashboardScenePage({ route, queryParams, history }: Props) {
|
||||||
const prevMatch = usePrevious(match);
|
const params = useParams();
|
||||||
|
const { type, slug, uid } = params;
|
||||||
|
const prevMatch = usePrevious({ params });
|
||||||
const stateManager = getDashboardScenePageStateManager();
|
const stateManager = getDashboardScenePageStateManager();
|
||||||
const { dashboard, isLoading, loadError } = stateManager.useState();
|
const { dashboard, isLoading, loadError } = stateManager.useState();
|
||||||
|
|
||||||
// After scene migration is complete and we get rid of old dashboard we should refactor dashboardWatcher so this route reload is not need
|
// After scene migration is complete and we get rid of old dashboard we should refactor dashboardWatcher so this route reload is not need
|
||||||
const routeReloadCounter = (history.location.state as any)?.routeReloadCounter;
|
const routeReloadCounter = (history.location.state as any)?.routeReloadCounter;
|
||||||
|
|
||||||
@ -31,14 +34,14 @@ export function DashboardScenePage({ match, route, queryParams, history }: Props
|
|||||||
const comingFromExplore = useMemo(() => {
|
const comingFromExplore = useMemo(() => {
|
||||||
return Boolean(store.getObject<DashboardDTO>(DASHBOARD_FROM_LS_KEY));
|
return Boolean(store.getObject<DashboardDTO>(DASHBOARD_FROM_LS_KEY));
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [match.params.uid, match.params.slug, match.params.type]);
|
}, [uid, slug, type]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (route.routeName === DashboardRoutes.Normal && match.params.type === 'snapshot') {
|
if (route.routeName === DashboardRoutes.Normal && type === 'snapshot') {
|
||||||
stateManager.loadSnapshot(match.params.slug!);
|
stateManager.loadSnapshot(slug!);
|
||||||
} else {
|
} else {
|
||||||
stateManager.loadDashboard({
|
stateManager.loadDashboard({
|
||||||
uid: match.params.uid ?? '',
|
uid: uid ?? '',
|
||||||
route: route.routeName as DashboardRoutes,
|
route: route.routeName as DashboardRoutes,
|
||||||
urlFolderUid: queryParams.folderUid,
|
urlFolderUid: queryParams.folderUid,
|
||||||
keepDashboardFromExploreInLocalStorage: false,
|
keepDashboardFromExploreInLocalStorage: false,
|
||||||
@ -48,15 +51,7 @@ export function DashboardScenePage({ match, route, queryParams, history }: Props
|
|||||||
return () => {
|
return () => {
|
||||||
stateManager.clearState();
|
stateManager.clearState();
|
||||||
};
|
};
|
||||||
}, [
|
}, [stateManager, uid, route.routeName, queryParams.folderUid, routeReloadCounter, slug, type]);
|
||||||
stateManager,
|
|
||||||
match.params.uid,
|
|
||||||
route.routeName,
|
|
||||||
queryParams.folderUid,
|
|
||||||
routeReloadCounter,
|
|
||||||
match.params.slug,
|
|
||||||
match.params.type,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Effect that handles explore->dashboards workflow
|
// Effect that handles explore->dashboards workflow
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -84,9 +79,9 @@ export function DashboardScenePage({ match, route, queryParams, history }: Props
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Do not render anything when transitioning from one dashboard to another
|
// Do not render anything when transitioning from one dashboard to another
|
||||||
// A bit tricky for transition to or from Home dashbord that does not have a uid in the url (but could have it in the dashboard model)
|
// A bit tricky for transition to or from Home dashboard that does not have a uid in the url (but could have it in the dashboard model)
|
||||||
// if prevMatch is undefined we are going from normal route to home route or vice versa
|
// if prevMatch is undefined we are going from normal route to home route or vice versa
|
||||||
if (match.params.type !== 'snapshot' && (!prevMatch || match.params.uid !== prevMatch?.params.uid)) {
|
if (type !== 'snapshot' && (!prevMatch || uid !== prevMatch?.params.uid)) {
|
||||||
console.log('skipping rendering');
|
console.log('skipping rendering');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Libraries
|
// Libraries
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
import { useParams } from 'react-router-dom-v5-compat';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { Alert, Spinner, useStyles2 } from '@grafana/ui';
|
import { Alert, Spinner, useStyles2 } from '@grafana/ui';
|
||||||
@ -20,14 +21,15 @@ export interface Props extends GrafanaRouteComponentProps<DashboardPageRoutePara
|
|||||||
/**
|
/**
|
||||||
* Used for iframe embedding and image rendering of single panels
|
* Used for iframe embedding and image rendering of single panels
|
||||||
*/
|
*/
|
||||||
export function SoloPanelPage({ match, queryParams }: Props) {
|
export function SoloPanelPage({ queryParams }: Props) {
|
||||||
const stateManager = getDashboardScenePageStateManager();
|
const stateManager = getDashboardScenePageStateManager();
|
||||||
const { dashboard } = stateManager.useState();
|
const { dashboard } = stateManager.useState();
|
||||||
|
const { uid = '' } = useParams();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
stateManager.loadDashboard({ uid: match.params.uid!, route: DashboardRoutes.Embedded });
|
stateManager.loadDashboard({ uid, route: DashboardRoutes.Embedded });
|
||||||
return () => stateManager.clearState();
|
return () => stateManager.clearState();
|
||||||
}, [stateManager, match, queryParams]);
|
}, [stateManager, queryParams, uid]);
|
||||||
|
|
||||||
if (!queryParams.panelId) {
|
if (!queryParams.panelId) {
|
||||||
return <EntityNotFound entity="Panel" />;
|
return <EntityNotFound entity="Panel" />;
|
||||||
|
@ -100,9 +100,9 @@ function setup(propOverrides?: Partial<Props>) {
|
|||||||
|
|
||||||
const props: Props = {
|
const props: Props = {
|
||||||
...getRouteComponentProps({
|
...getRouteComponentProps({
|
||||||
match: { params: { slug: 'my-dash', uid: '11' }, isExact: false, path: '', url: '' },
|
|
||||||
route: { routeName: DashboardRoutes.Normal } as RouteDescriptor,
|
route: { routeName: DashboardRoutes.Normal } as RouteDescriptor,
|
||||||
}),
|
}),
|
||||||
|
params: { slug: 'my-dash', uid: '11' },
|
||||||
navIndex: {
|
navIndex: {
|
||||||
'dashboards/browse': {
|
'dashboards/browse': {
|
||||||
text: 'Dashboards',
|
text: 'Dashboards',
|
||||||
@ -166,9 +166,9 @@ describe('DashboardPage', () => {
|
|||||||
it('only calls initDashboard once when wrapped in AppChrome', async () => {
|
it('only calls initDashboard once when wrapped in AppChrome', async () => {
|
||||||
const props: Props = {
|
const props: Props = {
|
||||||
...getRouteComponentProps({
|
...getRouteComponentProps({
|
||||||
match: { params: { slug: 'my-dash', uid: '11' }, isExact: true, path: '', url: '' },
|
|
||||||
route: { routeName: DashboardRoutes.Normal } as RouteDescriptor,
|
route: { routeName: DashboardRoutes.Normal } as RouteDescriptor,
|
||||||
}),
|
}),
|
||||||
|
params: { slug: 'my-dash', uid: '11' },
|
||||||
navIndex: {
|
navIndex: {
|
||||||
'dashboards/browse': {
|
'dashboards/browse': {
|
||||||
text: 'Dashboards',
|
text: 'Dashboards',
|
||||||
@ -270,7 +270,7 @@ describe('DashboardPage', () => {
|
|||||||
const { rerender } = setup();
|
const { rerender } = setup();
|
||||||
rerender({ dashboard: getTestDashboard() });
|
rerender({ dashboard: getTestDashboard() });
|
||||||
rerender({
|
rerender({
|
||||||
match: { params: { uid: 'new-uid' }, isExact: false, path: '', url: '' },
|
params: { uid: 'new-uid' },
|
||||||
dashboard: getTestDashboard({ title: 'Another dashboard' }),
|
dashboard: getTestDashboard({ title: 'Another dashboard' }),
|
||||||
});
|
});
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
|
@ -66,9 +66,11 @@ const mapDispatchToProps = {
|
|||||||
|
|
||||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||||
|
|
||||||
|
export type DashboardPageParams = { slug: string; uid: string; type: string; accessToken: string };
|
||||||
export type Props = Themeable2 &
|
export type Props = Themeable2 &
|
||||||
GrafanaRouteComponentProps<DashboardPageRouteParams, DashboardPageRouteSearchParams> &
|
Omit<GrafanaRouteComponentProps<DashboardPageRouteParams, DashboardPageRouteSearchParams>, 'match'> &
|
||||||
ConnectedProps<typeof connector>;
|
// The params returned from useParams are all optional, so we need to match that type here
|
||||||
|
ConnectedProps<typeof connector> & { params: Partial<DashboardPageParams> };
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
editPanel: PanelModel | null;
|
editPanel: PanelModel | null;
|
||||||
@ -138,7 +140,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
|
|||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.initDashboard();
|
this.initDashboard();
|
||||||
this.forceRouteReloadCounter = (this.props.history.location.state as any)?.routeReloadCounter || 0;
|
this.forceRouteReloadCounter = (this.props.location.state as any)?.routeReloadCounter || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
@ -151,21 +153,21 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initDashboard() {
|
initDashboard() {
|
||||||
const { dashboard, match, queryParams } = this.props;
|
const { dashboard, params, queryParams } = this.props;
|
||||||
|
|
||||||
if (dashboard) {
|
if (dashboard) {
|
||||||
this.closeDashboard();
|
this.closeDashboard();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.initDashboard({
|
this.props.initDashboard({
|
||||||
urlSlug: match.params.slug,
|
urlSlug: params.slug,
|
||||||
urlUid: match.params.uid,
|
urlUid: params.uid,
|
||||||
urlType: match.params.type,
|
urlType: params.type,
|
||||||
urlFolderUid: queryParams.folderUid,
|
urlFolderUid: queryParams.folderUid,
|
||||||
panelType: queryParams.panelType,
|
panelType: queryParams.panelType,
|
||||||
routeName: this.props.route.routeName,
|
routeName: this.props.route.routeName,
|
||||||
fixUrl: true,
|
fixUrl: true,
|
||||||
accessToken: match.params.accessToken,
|
accessToken: params.accessToken,
|
||||||
keybindingSrv: this.context.keybindings,
|
keybindingSrv: this.context.keybindings,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -174,15 +176,15 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||||
const { dashboard, match, templateVarsChangedInUrl } = this.props;
|
const { dashboard, params, templateVarsChangedInUrl } = this.props;
|
||||||
const routeReloadCounter = (this.props.history.location.state as any)?.routeReloadCounter;
|
const routeReloadCounter = (this.props.location.state as any)?.routeReloadCounter;
|
||||||
|
|
||||||
if (!dashboard) {
|
if (!dashboard) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
prevProps.match.params.uid !== match.params.uid ||
|
prevProps.params.uid !== params.uid ||
|
||||||
(routeReloadCounter !== undefined && this.forceRouteReloadCounter !== routeReloadCounter)
|
(routeReloadCounter !== undefined && this.forceRouteReloadCounter !== routeReloadCounter)
|
||||||
) {
|
) {
|
||||||
this.initDashboard();
|
this.initDashboard();
|
||||||
@ -529,7 +531,7 @@ function updateStatePageNavFromProps(props: Props, state: State): State {
|
|||||||
if (!pageNav || dashboard.title !== pageNav.text || dashboard.meta.folderUrl !== pageNav.parentItem?.url) {
|
if (!pageNav || dashboard.title !== pageNav.text || dashboard.meta.folderUrl !== pageNav.parentItem?.url) {
|
||||||
pageNav = {
|
pageNav = {
|
||||||
text: dashboard.title,
|
text: dashboard.title,
|
||||||
url: locationUtil.getUrlForPartial(props.history.location, {
|
url: locationUtil.getUrlForPartial(props.location, {
|
||||||
editview: null,
|
editview: null,
|
||||||
editPanel: null,
|
editPanel: null,
|
||||||
viewPanel: null,
|
viewPanel: null,
|
||||||
@ -539,7 +541,7 @@ function updateStatePageNavFromProps(props: Props, state: State): State {
|
|||||||
|
|
||||||
if (props.route.routeName === DashboardRoutes.Path) {
|
if (props.route.routeName === DashboardRoutes.Path) {
|
||||||
sectionNav = getRootContentNavModel();
|
sectionNav = getRootContentNavModel();
|
||||||
const pageNav = getPageNavFromSlug(props.match.params.slug!);
|
const pageNav = getPageNavFromSlug(props.params.slug!);
|
||||||
if (pageNav?.parentItem) {
|
if (pageNav?.parentItem) {
|
||||||
pageNav.parentItem = pageNav.parentItem;
|
pageNav.parentItem = pageNav.parentItem;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { act, screen, waitFor } from '@testing-library/react';
|
import { act, screen, waitFor } from '@testing-library/react';
|
||||||
|
import { useParams } from 'react-router-dom-v5-compat';
|
||||||
import { Props } from 'react-virtualized-auto-sizer';
|
import { Props } from 'react-virtualized-auto-sizer';
|
||||||
import { render } from 'test/test-utils';
|
import { render } from 'test/test-utils';
|
||||||
|
|
||||||
@ -80,14 +81,19 @@ jest.mock('app/features/dashboard/api/dashboard_api', () => ({
|
|||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function setup(props: Partial<DashboardPageProxyProps>) {
|
jest.mock('react-router-dom-v5-compat', () => ({
|
||||||
|
...jest.requireActual('react-router-dom-v5-compat'),
|
||||||
|
useParams: jest.fn().mockReturnValue({}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
function setup(props: Partial<DashboardPageProxyProps> & { uid?: string }) {
|
||||||
|
(useParams as jest.Mock).mockReturnValue({ uid: props.uid });
|
||||||
return render(
|
return render(
|
||||||
<DashboardPageProxy
|
<DashboardPageProxy
|
||||||
location={locationService.getLocation()}
|
location={locationService.getLocation()}
|
||||||
history={locationService.getHistory()}
|
history={locationService.getHistory()}
|
||||||
queryParams={{}}
|
queryParams={{}}
|
||||||
route={{ routeName: DashboardRoutes.Home, component: () => null, path: '/' }}
|
route={{ routeName: DashboardRoutes.Home, component: () => null, path: '/' }}
|
||||||
match={{ params: {}, isExact: true, path: '/', url: '/' }}
|
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -104,7 +110,6 @@ describe('DashboardPageProxy', () => {
|
|||||||
act(() => {
|
act(() => {
|
||||||
setup({
|
setup({
|
||||||
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },
|
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },
|
||||||
match: { params: {}, isExact: true, path: '/', url: '/' },
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -119,7 +124,7 @@ describe('DashboardPageProxy', () => {
|
|||||||
act(() => {
|
act(() => {
|
||||||
setup({
|
setup({
|
||||||
route: { routeName: DashboardRoutes.Normal, component: () => null, path: '/' },
|
route: { routeName: DashboardRoutes.Normal, component: () => null, path: '/' },
|
||||||
match: { params: { uid: 'abc-def' }, isExact: true, path: '/', url: '/' },
|
uid: 'abc-def',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -140,7 +145,7 @@ describe('DashboardPageProxy', () => {
|
|||||||
act(() => {
|
act(() => {
|
||||||
setup({
|
setup({
|
||||||
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },
|
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },
|
||||||
match: { params: { uid: '' }, isExact: true, path: '/', url: '/' },
|
uid: '',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -154,7 +159,7 @@ describe('DashboardPageProxy', () => {
|
|||||||
act(() => {
|
act(() => {
|
||||||
setup({
|
setup({
|
||||||
route: { routeName: DashboardRoutes.Normal, component: () => null, path: '/' },
|
route: { routeName: DashboardRoutes.Normal, component: () => null, path: '/' },
|
||||||
match: { params: { uid: 'abc-def' }, isExact: true, path: '/', url: '/' },
|
uid: 'abc-def',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
@ -169,14 +174,7 @@ describe('DashboardPageProxy', () => {
|
|||||||
act(() => {
|
act(() => {
|
||||||
setup({
|
setup({
|
||||||
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },
|
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },
|
||||||
match: {
|
|
||||||
params: {
|
|
||||||
uid: '',
|
uid: '',
|
||||||
},
|
|
||||||
isExact: true,
|
|
||||||
path: '/',
|
|
||||||
url: '/',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -190,7 +188,7 @@ describe('DashboardPageProxy', () => {
|
|||||||
act(() => {
|
act(() => {
|
||||||
setup({
|
setup({
|
||||||
route: { routeName: DashboardRoutes.Normal, component: () => null, path: '/' },
|
route: { routeName: DashboardRoutes.Normal, component: () => null, path: '/' },
|
||||||
match: { params: { uid: 'uid' }, isExact: true, path: '/', url: '/' },
|
uid: 'uid',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
@ -203,7 +201,7 @@ describe('DashboardPageProxy', () => {
|
|||||||
act(() => {
|
act(() => {
|
||||||
setup({
|
setup({
|
||||||
route: { routeName: DashboardRoutes.Normal, component: () => null, path: '/' },
|
route: { routeName: DashboardRoutes.Normal, component: () => null, path: '/' },
|
||||||
match: { params: { uid: 'wrongUID' }, isExact: true, path: '/', url: '/' },
|
uid: 'wrongUID',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { useLocation, useParams } from 'react-router-dom-v5-compat';
|
||||||
import { useAsync } from 'react-use';
|
import { useAsync } from 'react-use';
|
||||||
|
|
||||||
import { config } from '@grafana/runtime';
|
import { config } from '@grafana/runtime';
|
||||||
@ -6,12 +7,12 @@ import DashboardScenePage from 'app/features/dashboard-scene/pages/DashboardScen
|
|||||||
import { getDashboardScenePageStateManager } from 'app/features/dashboard-scene/pages/DashboardScenePageStateManager';
|
import { getDashboardScenePageStateManager } from 'app/features/dashboard-scene/pages/DashboardScenePageStateManager';
|
||||||
import { DashboardRoutes } from 'app/types';
|
import { DashboardRoutes } from 'app/types';
|
||||||
|
|
||||||
import DashboardPage from './DashboardPage';
|
import DashboardPage, { DashboardPageParams } from './DashboardPage';
|
||||||
import { DashboardPageRouteParams, DashboardPageRouteSearchParams } from './types';
|
import { DashboardPageRouteParams, DashboardPageRouteSearchParams } from './types';
|
||||||
|
|
||||||
export type DashboardPageProxyProps = GrafanaRouteComponentProps<
|
export type DashboardPageProxyProps = Omit<
|
||||||
DashboardPageRouteParams,
|
GrafanaRouteComponentProps<DashboardPageRouteParams, DashboardPageRouteSearchParams>,
|
||||||
DashboardPageRouteSearchParams
|
'match'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
// This proxy component is used for Dashboard -> Scenes migration.
|
// This proxy component is used for Dashboard -> Scenes migration.
|
||||||
@ -19,6 +20,8 @@ export type DashboardPageProxyProps = GrafanaRouteComponentProps<
|
|||||||
function DashboardPageProxy(props: DashboardPageProxyProps) {
|
function DashboardPageProxy(props: DashboardPageProxyProps) {
|
||||||
const forceScenes = props.queryParams.scenes === true;
|
const forceScenes = props.queryParams.scenes === true;
|
||||||
const forceOld = props.queryParams.scenes === false;
|
const forceOld = props.queryParams.scenes === false;
|
||||||
|
const params = useParams<DashboardPageParams>();
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
if (forceScenes || (config.featureToggles.dashboardScene && !forceOld)) {
|
if (forceScenes || (config.featureToggles.dashboardScene && !forceOld)) {
|
||||||
return <DashboardScenePage {...props} />;
|
return <DashboardScenePage {...props} />;
|
||||||
@ -26,34 +29,33 @@ function DashboardPageProxy(props: DashboardPageProxyProps) {
|
|||||||
|
|
||||||
const stateManager = getDashboardScenePageStateManager();
|
const stateManager = getDashboardScenePageStateManager();
|
||||||
const isScenesSupportedRoute = Boolean(
|
const isScenesSupportedRoute = Boolean(
|
||||||
props.route.routeName === DashboardRoutes.Home ||
|
props.route.routeName === DashboardRoutes.Home || (props.route.routeName === DashboardRoutes.Normal && params.uid)
|
||||||
(props.route.routeName === DashboardRoutes.Normal && props.match.params.uid)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// We pre-fetch dashboard to render dashboard page component depending on dashboard permissions.
|
// We pre-fetch dashboard to render dashboard page component depending on dashboard permissions.
|
||||||
// To avoid querying single dashboard multiple times, stateManager.fetchDashboard uses a simple, short-lived cache.
|
// To avoid querying single dashboard multiple times, stateManager.fetchDashboard uses a simple, short-lived cache.
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
const dashboard = useAsync(async () => {
|
const dashboard = useAsync(async () => {
|
||||||
if (props.match.params.type === 'snapshot') {
|
if (params.type === 'snapshot') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return stateManager.fetchDashboard({
|
return stateManager.fetchDashboard({
|
||||||
route: props.route.routeName as DashboardRoutes,
|
route: props.route.routeName as DashboardRoutes,
|
||||||
uid: props.match.params.uid ?? '',
|
uid: params.uid ?? '',
|
||||||
keepDashboardFromExploreInLocalStorage: true,
|
keepDashboardFromExploreInLocalStorage: true,
|
||||||
});
|
});
|
||||||
}, [props.match.params.uid, props.route.routeName]);
|
}, [params.uid, props.route.routeName]);
|
||||||
|
|
||||||
if (!config.featureToggles.dashboardSceneForViewers) {
|
if (!config.featureToggles.dashboardSceneForViewers) {
|
||||||
return <DashboardPage {...props} />;
|
return <DashboardPage {...props} params={params} location={location} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dashboard.loading) {
|
if (dashboard.loading) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dashboard?.value?.dashboard?.uid !== props.match.params.uid && dashboard.value?.meta?.isNew !== true) {
|
if (dashboard?.value?.dashboard?.uid !== params.uid && dashboard.value?.meta?.isNew !== true) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +66,7 @@ function DashboardPageProxy(props: DashboardPageProxyProps) {
|
|||||||
) {
|
) {
|
||||||
return <DashboardScenePage {...props} />;
|
return <DashboardScenePage {...props} />;
|
||||||
} else {
|
} else {
|
||||||
return <DashboardPage {...props} />;
|
return <DashboardPage {...props} params={params} location={location} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,12 +72,6 @@ function soloPanelPageScenario(description: string, scenarioFn: (ctx: ScenarioCo
|
|||||||
mount: (propOverrides?: Partial<Props>) => {
|
mount: (propOverrides?: Partial<Props>) => {
|
||||||
const props: Props = {
|
const props: Props = {
|
||||||
...getRouteComponentProps({
|
...getRouteComponentProps({
|
||||||
match: {
|
|
||||||
params: { slug: 'my-dash', uid: '11' },
|
|
||||||
isExact: false,
|
|
||||||
path: '',
|
|
||||||
url: '',
|
|
||||||
},
|
|
||||||
queryParams: {
|
queryParams: {
|
||||||
panelId: '1',
|
panelId: '1',
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user