Variables: Selectively reload panels on URL update (#51003)

This commit is contained in:
Todd Treece 2022-06-24 13:06:36 -04:00 committed by GitHub
parent 7e1667ce87
commit 342344bb03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 92 additions and 9 deletions

View File

@ -4900,12 +4900,12 @@ exports[`no explicit any`] = {
[270, 29, 3, "Unexpected any. Specify a different type.", "193409811"], [270, 29, 3, "Unexpected any. Specify a different type.", "193409811"],
[754, 26, 3, "Unexpected any. Specify a different type.", "193409811"] [754, 26, 3, "Unexpected any. Specify a different type.", "193409811"]
], ],
"public/app/features/variables/state/actions.ts:2709491277": [ "public/app/features/variables/state/actions.ts:443730774": [
[601, 32, 3, "Unexpected any. Specify a different type.", "193409811"], [601, 32, 3, "Unexpected any. Specify a different type.", "193409811"],
[678, 32, 3, "Unexpected any. Specify a different type.", "193409811"], [678, 32, 3, "Unexpected any. Specify a different type.", "193409811"],
[716, 90, 3, "Unexpected any. Specify a different type.", "193409811"], [731, 90, 3, "Unexpected any. Specify a different type.", "193409811"],
[860, 9, 3, "Unexpected any. Specify a different type.", "193409811"], [875, 9, 3, "Unexpected any. Specify a different type.", "193409811"],
[934, 32, 3, "Unexpected any. Specify a different type.", "193409811"] [949, 32, 3, "Unexpected any. Specify a different type.", "193409811"]
], ],
"public/app/features/variables/state/initVariableTransaction.test.ts:1177248976": [ "public/app/features/variables/state/initVariableTransaction.test.ts:1177248976": [
[55, 29, 3, "Unexpected any. Specify a different type.", "193409811"], [55, 29, 3, "Unexpected any. Specify a different type.", "193409811"],

View File

@ -678,7 +678,9 @@ export const templateVarsChangedInUrl =
async (dispatch, getState) => { async (dispatch, getState) => {
const update: Array<Promise<any>> = []; const update: Array<Promise<any>> = [];
const dashboard = getState().dashboard.getModel(); const dashboard = getState().dashboard.getModel();
for (const variable of getVariablesByKey(key, getState())) { const panelIds = new Set<number>();
const variables = getVariablesByKey(key, getState());
for (const variable of variables) {
const key = `var-${variable.name}`; const key = `var-${variable.name}`;
if (!vars.hasOwnProperty(key)) { if (!vars.hasOwnProperty(key)) {
// key not found quick exit // key not found quick exit
@ -704,13 +706,26 @@ export const templateVarsChangedInUrl =
} }
} }
// for adhoc variables we don't know which panels that will be impacted
if (!isAdHoc(variable)) {
getAllAffectedPanelIdsForVariableChange(variable.id, variables, dashboard?.panels ?? []).forEach((id) =>
panelIds.add(id)
);
}
const promise = variableAdapters.get(variable.type).setValueFromUrl(variable, value); const promise = variableAdapters.get(variable.type).setValueFromUrl(variable, value);
update.push(promise); update.push(promise);
} }
if (update.length) { if (update.length) {
await Promise.all(update); await Promise.all(update);
events.publish(new VariablesChangedInUrl({ panelIds: [], refreshAll: true }));
events.publish(
new VariablesChangedInUrl({
refreshAll: panelIds.size === 0,
panelIds: Array.from(panelIds),
})
);
} }
}; };

View File

@ -1,5 +1,5 @@
import { DashboardState, StoreState } from '../../../types'; import { DashboardState, StoreState } from '../../../types';
import { DashboardModel } from '../../dashboard/state'; import { DashboardModel, PanelModel } from '../../dashboard/state';
import { initialState } from '../../dashboard/state/reducers'; import { initialState } from '../../dashboard/state/reducers';
import { variableAdapters } from '../adapters'; import { variableAdapters } from '../adapters';
import { createConstantVariableAdapter } from '../constant/adapter'; import { createConstantVariableAdapter } from '../constant/adapter';
@ -30,9 +30,62 @@ async function getTestContext(urlQueryMap: ExtendedUrlQueryMap = {}, variable: V
.build(); .build();
} }
const variableB = customBuilder()
.withId('variableB')
.withRootStateKey(key)
.withName('variableB')
.withCurrent(['B'])
.withOptions('A', 'B', 'C')
.build();
const setValueFromUrlMock = jest.fn(); const setValueFromUrlMock = jest.fn();
variableAdapters.get(variable.type).setValueFromUrl = setValueFromUrlMock; variableAdapters.get(variable.type).setValueFromUrl = setValueFromUrlMock;
const modelJson = {
id: 1,
type: 'table',
maxDataPoints: 100,
interval: '5m',
showColumns: true,
targets: [{ refId: 'A', queryType: '${variable}' }, { noRefId: true }],
options: null,
fieldConfig: {
defaults: {
unit: 'mpg',
thresholds: {
mode: 'absolute',
steps: [
{ color: 'green', value: null },
{ color: 'red', value: 80 },
],
},
},
overrides: [
{
matcher: {
id: '1',
options: {},
},
properties: [
{
id: 'thresholds',
value: {
mode: 'absolute',
steps: [
{ color: 'green', value: null },
{ color: 'red', value: 80 },
],
},
},
],
},
],
},
};
const panelModelA = new PanelModel(modelJson);
const panelModelB = new PanelModel({ ...modelJson, id: 2, targets: [{ refId: 'B', queryType: '${variableB}' }] });
const templateVariableValueUpdatedMock = jest.fn(); const templateVariableValueUpdatedMock = jest.fn();
const startRefreshMock = jest.fn(); const startRefreshMock = jest.fn();
const dashboard: DashboardState = { const dashboard: DashboardState = {
@ -41,11 +94,12 @@ async function getTestContext(urlQueryMap: ExtendedUrlQueryMap = {}, variable: V
dashboardModel.templateVariableValueUpdated = templateVariableValueUpdatedMock; dashboardModel.templateVariableValueUpdated = templateVariableValueUpdatedMock;
dashboardModel.startRefresh = startRefreshMock; dashboardModel.startRefresh = startRefreshMock;
dashboardModel.templating = { list: [variable] }; dashboardModel.templating = { list: [variable] };
dashboardModel.panels = [panelModelA, panelModelB];
return dashboardModel; return dashboardModel;
}, },
}; };
const variables: VariablesState = { variable }; const variables: VariablesState = { variable, variableB };
const state: Partial<StoreState> = { const state: Partial<StoreState> = {
dashboard, dashboard,
...getPreloadedState(key, { variables }), ...getPreloadedState(key, { variables }),
@ -57,7 +111,7 @@ async function getTestContext(urlQueryMap: ExtendedUrlQueryMap = {}, variable: V
await thunk(dispatch, getState, undefined); await thunk(dispatch, getState, undefined);
return { setValueFromUrlMock, templateVariableValueUpdatedMock, startRefreshMock, variable }; return { setValueFromUrlMock, templateVariableValueUpdatedMock, startRefreshMock, variable, variableB };
} }
describe('templateVarsChangedInUrl', () => { describe('templateVarsChangedInUrl', () => {
@ -107,6 +161,20 @@ describe('templateVarsChangedInUrl', () => {
expect(setValueFromUrlMock).toHaveBeenCalledWith(variable, 'B'); expect(setValueFromUrlMock).toHaveBeenCalledWith(variable, 'B');
expect(templateVariableValueUpdatedMock).toHaveBeenCalledTimes(1); expect(templateVariableValueUpdatedMock).toHaveBeenCalledTimes(1);
expect(startRefreshMock).toHaveBeenCalledTimes(1); expect(startRefreshMock).toHaveBeenCalledTimes(1);
expect(startRefreshMock).toHaveBeenCalledWith({ refreshAll: false, panelIds: [1] });
});
it('should update URL value and only refresh panels with variableB dependency', async () => {
const { setValueFromUrlMock, templateVariableValueUpdatedMock, startRefreshMock, variableB } =
await getTestContext({
'var-variableB': { value: 'A' },
});
expect(setValueFromUrlMock).toHaveBeenCalledTimes(1);
expect(setValueFromUrlMock).toHaveBeenCalledWith(variableB, 'A');
expect(templateVariableValueUpdatedMock).toHaveBeenCalledTimes(1);
expect(startRefreshMock).toHaveBeenCalledTimes(1);
expect(startRefreshMock).toHaveBeenCalledWith({ refreshAll: false, panelIds: [2] });
}); });
describe('but the values in url query map were removed', () => { describe('but the values in url query map were removed', () => {