From f4fa3e4ff45ab2c0381f65c93f293534fc27d033 Mon Sep 17 00:00:00 2001 From: Maria Alexandra <239999+axelavargas@users.noreply.github.com> Date: Fri, 26 Aug 2022 15:36:42 +0200 Subject: [PATCH] Panel edit: Run queries when time range changes in table view (#53111) --- .../PanelEditor/PanelEditorTableView.test.tsx | 181 ++++++++++++++++++ .../PanelEditor/PanelEditorTableView.tsx | 5 +- 2 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 public/app/features/dashboard/components/PanelEditor/PanelEditorTableView.test.tsx diff --git a/public/app/features/dashboard/components/PanelEditor/PanelEditorTableView.test.tsx b/public/app/features/dashboard/components/PanelEditor/PanelEditorTableView.test.tsx new file mode 100644 index 00000000000..30b3f0ca4de --- /dev/null +++ b/public/app/features/dashboard/components/PanelEditor/PanelEditorTableView.test.tsx @@ -0,0 +1,181 @@ +import { act, render, screen } from '@testing-library/react'; +import React, { FC } from 'react'; +import { Provider } from 'react-redux'; +import configureMockStore from 'redux-mock-store'; +import { ReplaySubject } from 'rxjs'; +import { TimeSrvStub } from 'test/specs/helpers'; + +import { + dateTime, + EventBusSrv, + getDefaultTimeRange, + LoadingState, + PanelData, + PanelPlugin, + PanelProps, + TimeRange, +} from '@grafana/data'; +import { getTimeSrv, TimeSrv, setTimeSrv } from 'app/features/dashboard/services/TimeSrv'; + +import { PanelQueryRunner } from '../../../query/state/PanelQueryRunner'; +import { DashboardModel, PanelModel } from '../../state'; + +import { PanelEditorTableView, Props } from './PanelEditorTableView'; + +jest.mock('../../utils/panel', () => ({ + applyPanelTimeOverrides: jest.fn((panel, timeRange) => ({ + ...timeRange, + })), +})); + +jest.mock('app/features/panel/components/PanelRenderer', () => ({ + PanelRenderer: jest.fn(() =>
PanelRenderer Mock
), +})); + +function setupTestContext(options: Partial = {}) { + const mockStore = configureMockStore(); + const subject: ReplaySubject = new ReplaySubject(); + const panelQueryRunner = { + getData: () => subject, + run: () => { + subject.next({ state: LoadingState.Done, series: [], timeRange: getDefaultTimeRange() }); + }, + } as unknown as PanelQueryRunner; + + const defaults = { + panel: new PanelModel({ + id: 123, + hasTitle: jest.fn(), + replaceVariables: jest.fn(), + events: new EventBusSrv(), + getQueryRunner: () => panelQueryRunner, + getOptions: jest.fn(), + getDisplayTitle: jest.fn(), + runAllPanelQueries: jest.fn(), + }), + dashboard: new DashboardModel({ + id: 1, + uid: 'super-unique-id', + panelInitialized: jest.fn(), + events: new EventBusSrv(), + meta: { + isPublic: false, + }, + panels: [], + }), + plugin: { + meta: { skipDataQuery: false }, + panel: TestPanelComponent, + } as unknown as PanelPlugin, + isViewing: false, + isEditing: true, + isInView: false, + width: 100, + height: 100, + onInstanceStateChange: () => {}, + }; + + // Set up the mock store with the defaults + const store = mockStore({ dashboard: defaults.dashboard }); + const timeSrv = getTimeSrv(); + + const props = { ...defaults, ...options }; + const { rerender } = render( + + + + ); + + return { rerender, props, subject, store, timeSrv }; +} + +describe('PanelEditorTableView', () => { + beforeAll(() => { + // Mock the timeSrv singleton + const timeSrvMock2 = new TimeSrvStub() as unknown as TimeSrv; + setTimeSrv(timeSrvMock2); + }); + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should render', async () => { + const { rerender, props, subject, store } = setupTestContext({}); + + // only render the panel when loading is done + act(() => { + subject.next({ state: LoadingState.Loading, series: [], timeRange: getDefaultTimeRange() }); + subject.next({ state: LoadingState.Done, series: [], timeRange: getDefaultTimeRange() }); + }); + + const newProps = { ...props, isInView: true }; + rerender( + + + + ); + expect(screen.getByText(/PanelRenderer Mock/i)).toBeInTheDocument(); + }); + + it('should run all panel queries if time changes', async () => { + const { rerender, props, subject, store, timeSrv } = setupTestContext({}); + + const timeRangeUpdated = { + from: dateTime([2019, 1, 11, 12, 0]), + to: dateTime([2019, 1, 11, 18, 0]), + } as unknown as TimeRange; + + // only render the panel when loading is done + act(() => { + subject.next({ state: LoadingState.Loading, series: [], timeRange: getDefaultTimeRange() }); + subject.next({ state: LoadingState.Done, series: [], timeRange: getDefaultTimeRange() }); + }); + + const newProps = { ...props, isInView: true }; + rerender( + + + + ); + + expect(screen.getByText(/PanelRenderer Mock/i)).toBeInTheDocument(); + + // updating the global time range + act(() => { + timeSrv.setTime(timeRangeUpdated); + props.panel.refresh(); + }); + + // panel queries should have the updated time range + expect(props.panel.runAllPanelQueries).toHaveBeenNthCalledWith(1, { + dashboardId: props.dashboard.id, + dashboardTimezone: '', + dashboardUID: props.dashboard.uid, + timeData: timeRangeUpdated, + width: 100, + }); + + // update global time second time + + const timeRangeUpdated2 = { + from: dateTime([2018, 1, 11, 12, 0]), + to: dateTime([2018, 1, 11, 18, 0]), + } as unknown as TimeRange; + + act(() => { + timeSrv.setTime(timeRangeUpdated2); + props.panel.refresh(); + }); + + // panel queries should have the updated time range + expect(props.panel.runAllPanelQueries).toHaveBeenLastCalledWith({ + dashboardId: props.dashboard.id, + dashboardTimezone: '', + dashboardUID: props.dashboard.uid, + timeData: timeRangeUpdated2, + width: 100, + }); + }); +}); + +const TestPanelComponent: FC = () =>
Plugin Panel to Render
; diff --git a/public/app/features/dashboard/components/PanelEditor/PanelEditorTableView.tsx b/public/app/features/dashboard/components/PanelEditor/PanelEditorTableView.tsx index 633d75ceef7..501e6e9703a 100644 --- a/public/app/features/dashboard/components/PanelEditor/PanelEditorTableView.tsx +++ b/public/app/features/dashboard/components/PanelEditor/PanelEditorTableView.tsx @@ -12,7 +12,7 @@ import { DashboardModel, PanelModel } from '../../state'; import { usePanelLatestData } from './usePanelLatestData'; -interface Props { +export interface Props { width: number; height: number; panel: PanelModel; @@ -30,9 +30,9 @@ export function PanelEditorTableView({ width, height, panel, dashboard }: Props) // Subscribe to panel event useEffect(() => { const timeSrv = getTimeSrv(); - const timeData = applyPanelTimeOverrides(panel, timeSrv.timeRange()); const sub = panel.events.subscribe(RefreshEvent, () => { + const timeData = applyPanelTimeOverrides(panel, timeSrv.timeRange()); panel.runAllPanelQueries({ dashboardId: dashboard.id, dashboardUID: dashboard.uid, @@ -49,7 +49,6 @@ export function PanelEditorTableView({ width, height, panel, dashboard }: Props) if (!data) { return null; } - return ( {(innerWidth, innerHeight) => (