Chore: Upgrade to react 18 (#64428)

* update react 18 related deps

* fix some types

* make sure we're on react-router-dom >= 5.3.3

* Use new root API

* Remove StrictMode for now - react 18 double rendering causes issues

* fix + ignore some @grafana/ui types

* fix some more types

* use renderHook from @testing-library/react in almost all cases

* fix storybook types

* rewrite useDashboardSave to not use useEffect

* make props optional

* only render if props are provided

* add correct type for useCallback

* make resourcepicker tests more robust

* fix ModalManager rendering

* fix some more unit tests

* store the click coordinates in a ref as setState is NOT synchronous

* fix remaining e2e tests

* rewrite dashboardpage tests to avoid act warnings

* undo lint ignores

* fix ExpanderCell types

* set SymbolCell type correctly

* fix QueryAndExpressionsStep

* looks like the types were actually wrong instead :D

* undo this for now...

* remove spinner waits

* more robust tests

* rewrite errorboundary test to not explicitly count the number of renders

* make urlParam expect async

* increase timeout in waitFor

* revert ExplorePage test changes

* Update public/app/features/dashboard/containers/DashboardPage.test.tsx

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>

* Update public/app/features/dashboard/containers/PublicDashboardPage.test.tsx

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>

* Update public/app/features/dashboard/containers/PublicDashboardPage.test.tsx

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>

* Update public/app/features/dashboard/containers/PublicDashboardPage.test.tsx

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>

* skip fakeTimer test, ignore table types for now + other review comments

* update package peerDeps

* small tweak to resourcepicker test

* update lockfile...

* increase timeout in sharepublicdashboard tests

* ensure ExplorePaneContainer passes correct queries to initializeExplore

* fix LokiContextUI test

* fix unit tests

* make importDashboard flow more consistent

* wait for dashboard name before continuing

* more test fixes

* readd dashboard name to variable e2e tests

* wait for switches to be enabled before clicking

* fix modal rendering

* don't use @testing-library/dom directly

* quick fix for rendering of panels in firefox

* make PromQueryField test more robust

* don't wait for chartData - in react 18 this can happen before the wait code even gets executed

---------

Co-authored-by: kay delaney <kay@grafana.com>
Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
This commit is contained in:
Ashley Harrison
2023-04-11 10:51:54 +01:00
committed by GitHub
co-authored by Alex Khomenko kay delaney
parent a5499bbf70
commit 1261345b81
89 changed files with 921 additions and 920 deletions
@@ -1,5 +1,4 @@
import { within } from '@testing-library/dom';
import { render, screen } from '@testing-library/react';
import { render, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { TestProvider } from 'test/helpers/TestProvider';
@@ -1,5 +1,4 @@
import { within } from '@testing-library/dom';
import { render, screen } from '@testing-library/react';
import { render, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
@@ -1,5 +1,4 @@
import { within } from '@testing-library/dom';
import { render, screen, waitFor } from '@testing-library/react';
import { render, screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
@@ -1,4 +1,4 @@
import { screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react';
import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
@@ -311,6 +311,7 @@ describe('SharePublic - Already persisted', () => {
describe('SharePublic - Report interactions', () => {
beforeEach(() => {
jest.clearAllMocks();
server.use(getExistentPublicDashboardResponse());
server.use(
rest.put('/api/dashboards/uid/:dashboardUid/public-dashboards/:uid', (req, res, ctx) =>
@@ -327,29 +328,41 @@ describe('SharePublic - Report interactions', () => {
it('reports interaction when time range is clicked', async () => {
await renderSharePublicDashboard();
await waitFor(() => {
expect(screen.getByTestId(selectors.EnableTimeRangeSwitch)).toBeEnabled();
});
await userEvent.click(screen.getByTestId(selectors.EnableTimeRangeSwitch));
await waitForElementToBeRemoved(screen.getByTestId('Spinner'));
expect(reportInteraction).toHaveBeenCalledWith('grafana_dashboards_public_time_selection_clicked', {
action: pubdashResponse.timeSelectionEnabled ? 'disable' : 'enable',
await waitFor(() => {
expect(reportInteraction).toHaveBeenCalledWith('grafana_dashboards_public_time_selection_clicked', {
action: pubdashResponse.timeSelectionEnabled ? 'disable' : 'enable',
});
});
});
it('reports interaction when show annotations is clicked', async () => {
await renderSharePublicDashboard();
await waitFor(() => {
expect(screen.getByTestId(selectors.EnableAnnotationsSwitch)).toBeEnabled();
});
await userEvent.click(screen.getByTestId(selectors.EnableAnnotationsSwitch));
await waitForElementToBeRemoved(screen.getByTestId('Spinner'));
expect(reportInteraction).toHaveBeenCalledWith('grafana_dashboards_public_annotations_clicked', {
action: pubdashResponse.annotationsEnabled ? 'disable' : 'enable',
await waitFor(() => {
expect(reportInteraction).toHaveBeenCalledWith('grafana_dashboards_public_annotations_clicked', {
action: pubdashResponse.annotationsEnabled ? 'disable' : 'enable',
});
});
});
it('reports interaction when pause is clicked', async () => {
await renderSharePublicDashboard();
await waitFor(() => {
expect(screen.getByTestId(selectors.PauseSwitch)).toBeEnabled();
});
await userEvent.click(screen.getByTestId(selectors.PauseSwitch));
await waitForElementToBeRemoved(screen.getByTestId('Spinner'));
expect(reportInteraction).toHaveBeenCalledWith('grafana_dashboards_public_enable_clicked', {
action: pubdashResponse.isEnabled ? 'disable' : 'enable',
await waitFor(() => {
expect(reportInteraction).toHaveBeenCalledWith('grafana_dashboards_public_enable_clicked', {
action: pubdashResponse.isEnabled ? 'disable' : 'enable',
});
});
});
});
@@ -1,4 +1,4 @@
import { render, screen } from '@testing-library/react';
import { render, screen, waitFor } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { match, Router } from 'react-router-dom';
@@ -66,16 +66,6 @@ jest.mock('react-virtualized-auto-sizer', () => {
return ({ children }: AutoSizerProps) => children({ height: 1, width: 1 });
});
interface ScenarioContext {
dashboard?: DashboardModel | null;
container?: HTMLElement;
mount: (propOverrides?: Partial<Props>) => void;
unmount: () => void;
props: Props;
rerender: (propOverrides?: Partial<Props>) => void;
setup: (fn: () => void) => void;
}
function getTestDashboard(overrides?: Partial<Dashboard>, metaOverrides?: Partial<DashboardMeta>): DashboardModel {
const data = Object.assign(
{
@@ -95,123 +85,98 @@ function getTestDashboard(overrides?: Partial<Dashboard>, metaOverrides?: Partia
return createDashboardModelFixture(data, metaOverrides);
}
function dashboardPageScenario(description: string, scenarioFn: (ctx: ScenarioContext) => void) {
describe(description, () => {
let setupFn: () => void;
const mockInitDashboard = jest.fn();
const mockCleanUpDashboardAndVariables = jest.fn();
const ctx: ScenarioContext = {
setup: (fn) => {
setupFn = fn;
},
mount: (propOverrides?: Partial<Props>) => {
config.bootData.navTree = [
{ text: 'Dashboards', id: 'dashboards' },
{ text: 'Home', id: HOME_NAV_ID },
];
function setup(propOverrides?: Partial<Props>) {
config.bootData.navTree = [
{ text: 'Dashboards', id: 'dashboards' },
{ text: 'Home', id: HOME_NAV_ID },
];
const store = configureStore();
const props: Props = {
...getRouteComponentProps({
match: { params: { slug: 'my-dash', uid: '11' } } as unknown as match,
route: { routeName: DashboardRoutes.Normal } as RouteDescriptor,
}),
navIndex: {
dashboards: { text: 'Dashboards', id: 'dashboards', parentItem: { text: 'Home', id: HOME_NAV_ID } },
[HOME_NAV_ID]: { text: 'Home', id: HOME_NAV_ID },
},
initPhase: DashboardInitPhase.NotStarted,
initError: null,
initDashboard: jest.fn(),
notifyApp: mockToolkitActionCreator(notifyApp),
cleanUpDashboardAndVariables: jest.fn(),
cancelVariables: jest.fn(),
templateVarsChangedInUrl: jest.fn(),
dashboard: null,
theme: createTheme(),
};
const store = configureStore();
const props: Props = {
...getRouteComponentProps({
match: { params: { slug: 'my-dash', uid: '11' } } as unknown as match,
route: { routeName: DashboardRoutes.Normal } as RouteDescriptor,
}),
navIndex: {
dashboards: { text: 'Dashboards', id: 'dashboards', parentItem: { text: 'Home', id: HOME_NAV_ID } },
[HOME_NAV_ID]: { text: 'Home', id: HOME_NAV_ID },
},
initPhase: DashboardInitPhase.NotStarted,
initError: null,
initDashboard: mockInitDashboard,
notifyApp: mockToolkitActionCreator(notifyApp),
cleanUpDashboardAndVariables: mockCleanUpDashboardAndVariables,
cancelVariables: jest.fn(),
templateVarsChangedInUrl: jest.fn(),
dashboard: null,
theme: createTheme(),
};
Object.assign(props, propOverrides);
Object.assign(props, propOverrides);
ctx.props = props;
ctx.dashboard = props.dashboard;
const context = getGrafanaContextMock();
const context = getGrafanaContextMock();
const { unmount, rerender } = render(
<GrafanaContext.Provider value={context}>
<Provider store={store}>
<Router history={locationService.getHistory()}>
<UnthemedDashboardPage {...props} />
</Router>
</Provider>
</GrafanaContext.Provider>
);
const { container, rerender, unmount } = render(
<GrafanaContext.Provider value={context}>
<Provider store={store}>
<Router history={locationService.getHistory()}>
<UnthemedDashboardPage {...props} />
</Router>
</Provider>
</GrafanaContext.Provider>
);
const wrappedRerender = (newProps: Partial<Props>) => {
Object.assign(props, newProps);
return rerender(
<GrafanaContext.Provider value={context}>
<Provider store={store}>
<Router history={locationService.getHistory()}>
<UnthemedDashboardPage {...props} />
</Router>
</Provider>
</GrafanaContext.Provider>
);
};
ctx.container = container;
ctx.rerender = (newProps?: Partial<Props>) => {
Object.assign(props, newProps);
rerender(
<GrafanaContext.Provider value={context}>
<Provider store={store}>
<Router history={locationService.getHistory()}>
<UnthemedDashboardPage {...props} />
</Router>
</Provider>
</GrafanaContext.Provider>
);
};
ctx.unmount = unmount;
},
props: {} as Props,
rerender: () => {},
unmount: () => {},
};
beforeEach(() => {
setupFn();
});
scenarioFn(ctx);
});
return { rerender: wrappedRerender, unmount };
}
describe('DashboardPage', () => {
dashboardPageScenario('Given initial state', (ctx) => {
ctx.setup(() => {
ctx.mount();
beforeEach(() => {
jest.clearAllMocks();
});
it('Should call initDashboard on mount', () => {
setup();
expect(mockInitDashboard).toBeCalledWith({
fixUrl: true,
routeName: 'normal-dashboard',
urlSlug: 'my-dash',
urlUid: '11',
keybindingSrv: expect.anything(),
});
});
describe('Given a simple dashboard', () => {
it('Should render panels', async () => {
setup({ dashboard: getTestDashboard() });
expect(await screen.findByText('My panel title')).toBeInTheDocument();
});
it('Should call initDashboard on mount', () => {
expect(ctx.props.initDashboard).toBeCalledWith({
fixUrl: true,
routeName: 'normal-dashboard',
urlSlug: 'my-dash',
urlUid: '11',
keybindingSrv: expect.anything(),
it('Should update title', async () => {
setup({ dashboard: getTestDashboard() });
await waitFor(() => {
expect(document.title).toBe('My dashboard - Dashboards - Grafana');
});
});
});
dashboardPageScenario('Given a simple dashboard', (ctx) => {
ctx.setup(() => {
ctx.mount();
ctx.rerender({ dashboard: getTestDashboard() });
});
it('Should render panels', () => {
expect(screen.getByText('My panel title')).toBeInTheDocument();
});
it('Should update title', () => {
expect(document.title).toBe('My dashboard - Dashboards - Grafana');
});
});
dashboardPageScenario('When going into view mode', (ctx) => {
ctx.setup(() => {
describe('When going into view mode', () => {
beforeEach(() => {
setDataSourceSrv({
get: jest.fn().mockResolvedValue({ getRef: jest.fn(), query: jest.fn().mockResolvedValue([]) }),
getInstanceSettings: jest.fn().mockReturnValue({ meta: {} }),
@@ -221,101 +186,110 @@ describe('DashboardPage', () => {
setDashboardSrv({
getCurrent: () => getTestDashboard(),
} as DashboardSrv);
ctx.mount({
dashboard: getTestDashboard(),
});
it('Should render panel in view mode', async () => {
const dashboard = getTestDashboard();
setup({
dashboard,
queryParams: { viewPanel: '1' },
});
await waitFor(() => {
expect(dashboard.panelInView).toBeDefined();
expect(dashboard.panels[0].isViewing).toBe(true);
});
});
it('Should render panel in view mode', () => {
expect(ctx.dashboard?.panelInView).toBeDefined();
expect(ctx.dashboard?.panels[0].isViewing).toBe(true);
});
it('Should reset state when leaving', async () => {
const dashboard = getTestDashboard();
const { rerender } = setup({
dashboard,
queryParams: { viewPanel: '1' },
});
rerender({ queryParams: {}, dashboard });
it('Should reset state when leaving', () => {
ctx.rerender({ queryParams: {} });
expect(ctx.dashboard?.panelInView).toBeUndefined();
expect(ctx.dashboard?.panels[0].isViewing).toBe(false);
await waitFor(() => {
expect(dashboard.panelInView).toBeUndefined();
expect(dashboard.panels[0].isViewing).toBe(false);
});
});
});
dashboardPageScenario('When going into edit mode', (ctx) => {
ctx.setup(() => {
ctx.mount({
dashboard: getTestDashboard(),
describe('When going into edit mode', () => {
it('Should render panel in edit mode', async () => {
const dashboard = getTestDashboard();
setup({
dashboard,
queryParams: { editPanel: '1' },
});
await waitFor(() => {
expect(dashboard.panelInEdit).toBeDefined();
});
});
it('Should render panel in edit mode', () => {
expect(ctx.dashboard?.panelInEdit).toBeDefined();
it('Should render panel editor', async () => {
const dashboard = getTestDashboard();
setup({
dashboard,
queryParams: { editPanel: '1' },
});
expect(await screen.findByTitle('Apply changes and go back to dashboard')).toBeInTheDocument();
});
it('Should render panel editor', () => {
expect(screen.getByTitle('Apply changes and go back to dashboard')).toBeInTheDocument();
});
it('Should reset state when leaving', () => {
ctx.rerender({ queryParams: {} });
expect(screen.queryByTitle('Apply changes and go back to dashboard')).not.toBeInTheDocument();
it('Should reset state when leaving', async () => {
const dashboard = getTestDashboard();
const { rerender } = setup({
dashboard,
queryParams: { editPanel: '1' },
});
rerender({ queryParams: {} });
await waitFor(() => {
expect(screen.queryByTitle('Apply changes and go back to dashboard')).not.toBeInTheDocument();
});
});
});
dashboardPageScenario('When dashboard unmounts', (ctx) => {
ctx.setup(() => {
ctx.mount();
ctx.rerender({ dashboard: getTestDashboard() });
ctx.unmount();
});
it('Should call close action', () => {
expect(ctx.props.cleanUpDashboardAndVariables).toHaveBeenCalledTimes(1);
describe('When dashboard unmounts', () => {
it('Should call close action', async () => {
const { rerender, unmount } = setup();
rerender({ dashboard: getTestDashboard() });
unmount();
await waitFor(() => {
expect(mockCleanUpDashboardAndVariables).toHaveBeenCalledTimes(1);
});
});
});
dashboardPageScenario('When dashboard changes', (ctx) => {
ctx.setup(() => {
ctx.mount();
ctx.rerender({ dashboard: getTestDashboard() });
ctx.rerender({
match: {
params: { uid: 'new-uid' },
} as unknown as match,
describe('When dashboard changes', () => {
it('Should call clean up action and init', async () => {
const { rerender } = setup();
rerender({ dashboard: getTestDashboard() });
rerender({
match: { params: { uid: 'new-uid' } } as unknown as match,
dashboard: getTestDashboard({ title: 'Another dashboard' }),
});
});
it('Should call clean up action and init', () => {
expect(ctx.props.cleanUpDashboardAndVariables).toHaveBeenCalledTimes(1);
expect(ctx.props.initDashboard).toHaveBeenCalledTimes(2);
});
});
dashboardPageScenario('No kiosk mode tv', (ctx) => {
ctx.setup(() => {
ctx.mount({ dashboard: getTestDashboard() });
ctx.rerender({ dashboard: ctx.dashboard });
});
it('should render dashboard page toolbar and submenu', () => {
expect(screen.queryAllByTestId(selectors.pages.Dashboard.DashNav.navV2)).toHaveLength(1);
expect(screen.queryAllByLabelText(selectors.pages.Dashboard.SubMenu.submenu)).toHaveLength(1);
});
});
dashboardPageScenario('When in full kiosk mode', (ctx) => {
ctx.setup(() => {
ctx.mount({
queryParams: { kiosk: true },
dashboard: getTestDashboard(),
await waitFor(() => {
expect(mockCleanUpDashboardAndVariables).toHaveBeenCalledTimes(1);
expect(mockInitDashboard).toHaveBeenCalledTimes(2);
});
ctx.rerender({ dashboard: ctx.dashboard });
});
});
it('should not render page toolbar and submenu', () => {
expect(screen.queryAllByTestId(selectors.pages.Dashboard.DashNav.navV2)).toHaveLength(0);
expect(screen.queryAllByLabelText(selectors.pages.Dashboard.SubMenu.submenu)).toHaveLength(0);
describe('No kiosk mode tv', () => {
it('should render dashboard page toolbar and submenu', async () => {
setup({ dashboard: getTestDashboard() });
expect(await screen.findAllByTestId(selectors.pages.Dashboard.DashNav.navV2)).toHaveLength(1);
expect(screen.getAllByLabelText(selectors.pages.Dashboard.SubMenu.submenu)).toHaveLength(1);
});
});
describe('When in full kiosk mode', () => {
it('should not render page toolbar and submenu', async () => {
setup({ dashboard: getTestDashboard(), queryParams: { kiosk: true } });
await waitFor(() => {
expect(screen.queryAllByTestId(selectors.pages.Dashboard.DashNav.navV2)).toHaveLength(0);
expect(screen.queryAllByLabelText(selectors.pages.Dashboard.SubMenu.submenu)).toHaveLength(0);
});
});
});
});
@@ -1,4 +1,4 @@
import { render, RenderResult, screen } from '@testing-library/react';
import { render, screen, waitFor } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router-dom';
@@ -48,29 +48,27 @@ 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,
}: {
props: Props;
initialState?: Partial<appTypes.StoreState>;
}): RenderResult => {
const setup = (propOverrides?: Partial<Props>, initialState?: Partial<appTypes.StoreState>) => {
const context = getGrafanaContextMock();
const store = configureStore(initialState);
return render(
const props: Props = {
...getRouteComponentProps({
match: { params: { accessToken: 'an-access-token' }, isExact: true, url: '', path: '' },
route: {
routeName: DashboardRoutes.Public,
path: '/public-dashboards/:accessToken',
component: SafeDynamicImport(
() =>
import(/* webpackChunkName: "PublicDashboardPage"*/ 'app/features/dashboard/containers/PublicDashboardPage')
),
},
}),
};
Object.assign(props, propOverrides);
const { unmount, rerender } = render(
<GrafanaContext.Provider value={context}>
<Provider store={store}>
<Router history={locationService.getHistory()}>
@@ -79,6 +77,21 @@ const renderWithProvider = ({
</Provider>
</GrafanaContext.Provider>
);
const wrappedRerender = (newProps: Partial<Props>) => {
Object.assign(props, newProps);
return rerender(
<GrafanaContext.Provider value={context}>
<Provider store={store}>
<Router history={locationService.getHistory()}>
<PublicDashboardPage {...props} />
</Router>
</Provider>
</GrafanaContext.Provider>
);
};
return { rerender: wrappedRerender, unmount };
};
const selectors = e2eSelectors.components;
@@ -110,185 +123,114 @@ const getTestDashboard = (overrides?: Partial<Dashboard>, metaOverrides?: Partia
return new DashboardModel(data, metaOverrides);
};
function dashboardPageScenario(description: string, scenarioFn: (ctx: ScenarioContext) => void) {
describe(description, () => {
let setupFn: () => void;
describe('PublicDashboardPage', () => {
beforeEach(() => {
jest.clearAllMocks();
});
const ctx: ScenarioContext = {
setup: (fn) => {
setupFn = fn;
it('Should call initDashboard on mount', () => {
setup();
expect(initDashboard).toBeCalledWith({
fixUrl: false,
accessToken: 'an-access-token',
routeName: 'public-dashboard',
keybindingSrv: expect.anything(),
});
});
describe('Given a simple public dashboard', () => {
const newState = {
dashboard: {
getModel: getTestDashboard,
initError: null,
initPhase: DashboardInitPhase.Completed,
permissions: [],
},
mount: () => {
const props: Props = {
...getRouteComponentProps({
match: { params: { accessToken: 'an-access-token' }, isExact: true, url: '', path: '' },
route: {
routeName: DashboardRoutes.Public,
path: '/public-dashboards/:accessToken',
component: SafeDynamicImport(
() =>
import(
/* webpackChunkName: "PublicDashboardPage"*/ 'app/features/dashboard/containers/PublicDashboardPage'
)
),
},
}),
};
const { rerender } = renderWithProvider({ props });
ctx.rerender = ({
propsOverride,
newState,
}: {
propsOverride?: Partial<Props>;
newState?: Partial<appTypes.StoreState>;
}) => {
Object.assign(props, propsOverride);
const context = getGrafanaContextMock();
const store = configureStore(newState);
rerender(
<GrafanaContext.Provider value={context}>
<Provider store={store}>
<Router history={locationService.getHistory()}>
<PublicDashboardPage {...props} />
</Router>
</Provider>
</GrafanaContext.Provider>
);
};
},
rerender: () => {},
};
beforeEach(() => {
setupFn();
it('Should render panels', async () => {
setup(undefined, newState);
expect(await screen.findByText('My panel title')).toBeInTheDocument();
});
scenarioFn(ctx);
});
}
describe('PublicDashboardPage', () => {
dashboardPageScenario('Given initial state', (ctx) => {
ctx.setup(() => {
ctx.mount();
it('Should update title', async () => {
setup(undefined, newState);
await waitFor(() => {
expect(document.title).toBe('My dashboard - Grafana');
});
});
it('Should call initDashboard on mount', () => {
expect(initDashboard).toBeCalledWith({
fixUrl: false,
accessToken: 'an-access-token',
routeName: 'public-dashboard',
keybindingSrv: expect.anything(),
it('Should not render neither time range nor refresh picker buttons', async () => {
setup(undefined, newState);
await waitFor(() => {
expect(screen.queryByTestId(selectors.TimePicker.openButton)).not.toBeInTheDocument();
expect(screen.queryByTestId(selectors.RefreshPicker.runButtonV2)).not.toBeInTheDocument();
expect(screen.queryByTestId(selectors.RefreshPicker.intervalButtonV2)).not.toBeInTheDocument();
});
});
it('Should not render paused or deleted screen', async () => {
setup(undefined, newState);
await waitFor(() => {
expect(screen.queryByTestId(publicDashboardSelector.NotAvailable.container)).not.toBeInTheDocument();
});
});
});
dashboardPageScenario('Given a simple public dashboard', (ctx) => {
ctx.setup(() => {
ctx.mount();
ctx.rerender({
newState: {
dashboard: {
getModel: getTestDashboard,
initError: null,
initPhase: DashboardInitPhase.Completed,
permissions: [],
},
describe('Given a public dashboard with time range enabled', () => {
it('Should render time range and refresh picker buttons', async () => {
setup(undefined, {
dashboard: {
getModel: () =>
getTestDashboard({
timepicker: { hidden: false, collapse: false, enable: true, refresh_intervals: [], time_options: [] },
}),
initError: null,
initPhase: DashboardInitPhase.Completed,
permissions: [],
},
});
});
it('Should render panels', () => {
expect(screen.getByText('My panel title')).toBeInTheDocument();
});
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();
});
it('Should not render paused or deleted screen', () => {
expect(screen.queryByTestId(publicDashboardSelector.NotAvailable.container)).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(await screen.findByTestId(selectors.TimePicker.openButton)).toBeInTheDocument();
expect(screen.getByTestId(selectors.RefreshPicker.runButtonV2)).toBeInTheDocument();
expect(screen.getByTestId(selectors.RefreshPicker.intervalButtonV2)).toBeInTheDocument();
});
});
dashboardPageScenario('Given paused public dashboard', (ctx) => {
ctx.setup(() => {
ctx.mount();
ctx.rerender({
newState: {
dashboard: {
getModel: () => getTestDashboard(undefined, { publicDashboardEnabled: false, dashboardNotFound: false }),
initError: null,
initPhase: DashboardInitPhase.Completed,
permissions: [],
},
describe('Given paused public dashboard', () => {
it('Should render public dashboard paused screen', async () => {
setup(undefined, {
dashboard: {
getModel: () => getTestDashboard(undefined, { publicDashboardEnabled: false, dashboardNotFound: false }),
initError: null,
initPhase: DashboardInitPhase.Completed,
permissions: [],
},
});
});
it('Should render public dashboard paused screen', () => {
expect(screen.queryByTestId(publicDashboardSelector.page)).not.toBeInTheDocument();
await waitFor(() => {
expect(screen.queryByTestId(publicDashboardSelector.page)).not.toBeInTheDocument();
});
expect(screen.getByTestId(publicDashboardSelector.NotAvailable.title)).toBeInTheDocument();
expect(screen.getByTestId(publicDashboardSelector.NotAvailable.pausedDescription)).toBeInTheDocument();
});
});
dashboardPageScenario('Given deleted public dashboard', (ctx) => {
ctx.setup(() => {
ctx.mount();
ctx.rerender({
newState: {
dashboard: {
getModel: () => getTestDashboard(undefined, { dashboardNotFound: true }),
initError: null,
initPhase: DashboardInitPhase.Completed,
permissions: [],
},
describe('Given deleted public dashboard', () => {
it('Should render public dashboard deleted screen', async () => {
setup(undefined, {
dashboard: {
getModel: () => getTestDashboard(undefined, { dashboardNotFound: true }),
initError: null,
initPhase: DashboardInitPhase.Completed,
permissions: [],
},
});
});
it('Should render public dashboard deleted screen', () => {
expect(screen.queryByTestId(publicDashboardSelector.page)).not.toBeInTheDocument();
await waitFor(() => {
expect(screen.queryByTestId(publicDashboardSelector.page)).not.toBeInTheDocument();
expect(screen.queryByTestId(publicDashboardSelector.NotAvailable.pausedDescription)).not.toBeInTheDocument();
});
expect(screen.getByTestId(publicDashboardSelector.NotAvailable.title)).toBeInTheDocument();
expect(screen.queryByTestId(publicDashboardSelector.NotAvailable.pausedDescription)).not.toBeInTheDocument();
});
});
});
@@ -8,9 +8,10 @@ import { DashboardModel } from '../state';
import { createDashboardModelFixture } from '../state/__fixtures__/dashboardFixtures';
import { DashboardGrid, Props } from './DashboardGrid';
import { Props as LazyLoaderProps } from './LazyLoader';
jest.mock('app/features/dashboard/dashgrid/LazyLoader', () => {
const LazyLoader = ({ children }: React.PropsWithChildren<{}>) => {
const LazyLoader = ({ children }: LazyLoaderProps) => {
return <>{children}</>;
};
return { LazyLoader };
@@ -71,7 +71,7 @@ export class DashboardPanelUnconnected extends PureComponent<Props> {
}
};
renderPanel = (isInView: boolean) => {
renderPanel = ({ isInView }: { isInView: boolean }) => {
const { dashboard, panel, isViewing, isEditing, width, height, plugin, timezone, hideMenu } = this.props;
if (!plugin) {
@@ -118,7 +118,7 @@ export class DashboardPanelUnconnected extends PureComponent<Props> {
{this.renderPanel}
</LazyLoader>
) : (
this.renderPanel(true)
this.renderPanel({ isInView: true })
);
}
}
@@ -55,7 +55,9 @@ LazyLoader.addCallback = (id: string, c: (e: IntersectionObserverEntry) => void)
LazyLoader.observer = new IntersectionObserver(
(entries) => {
for (const entry of entries) {
LazyLoader.callbacks[entry.target.id](entry);
if (LazyLoader.callbacks[entry.target.id]) {
LazyLoader.callbacks[entry.target.id](entry);
}
}
},
{ rootMargin: '100px' }