mirror of https://github.com/grafana/grafana.git
Dashboard Scene: Sync variable state with TemplateSrv result (#93327)
* Generate options for variables through TemplateSrv * Add refresh when object changes * Remove unnecesary static function * Extract logic * Add extra test case when variable changes and refresh event is triggered * bring back old logic, query options should not live in the dashboard json * add missing change * Add support to keep variable options in query * tests * move refreshEvent to DashboardVariableDependency --------- Co-authored-by: alexandra vargas <alexa1866@gmail.com> Co-authored-by: Victor Marin <victor.marin@grafana.com>
This commit is contained in:
parent
2b320b0f9e
commit
d09017f7a2
|
|
@ -1,5 +1,5 @@
|
||||||
import { CoreApp, GrafanaConfig, LoadingState, getDefaultTimeRange, locationUtil, store } from '@grafana/data';
|
import { CoreApp, GrafanaConfig, LoadingState, getDefaultTimeRange, locationUtil, store } from '@grafana/data';
|
||||||
import { locationService } from '@grafana/runtime';
|
import { locationService, RefreshEvent } from '@grafana/runtime';
|
||||||
import {
|
import {
|
||||||
sceneGraph,
|
sceneGraph,
|
||||||
SceneGridLayout,
|
SceneGridLayout,
|
||||||
|
|
@ -750,6 +750,21 @@ describe('DashboardScene', () => {
|
||||||
variable.setState({ name: 'A' });
|
variable.setState({ name: 'A' });
|
||||||
expect(scene.state.isDirty).toBe(false);
|
expect(scene.state.isDirty).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should trigger scene RefreshEvent when a scene variable changes', () => {
|
||||||
|
const varA = new TestVariable({ name: 'A', query: 'A.*', value: 'A.AA', text: '', options: [], delayMs: 0 });
|
||||||
|
const scene = buildTestScene({
|
||||||
|
$variables: new SceneVariableSet({ variables: [varA] }),
|
||||||
|
});
|
||||||
|
|
||||||
|
scene.activate();
|
||||||
|
|
||||||
|
const eventHandler = jest.fn();
|
||||||
|
// this RefreshEvent is from the scenes library
|
||||||
|
scene.subscribeToEvent(RefreshEvent, eventHandler);
|
||||||
|
varA.changeValueTo('A.AB');
|
||||||
|
expect(eventHandler).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('When a dashboard is restored', () => {
|
describe('When a dashboard is restored', () => {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import {
|
||||||
DataSourceGetTagKeysOptions,
|
DataSourceGetTagKeysOptions,
|
||||||
DataSourceGetTagValuesOptions,
|
DataSourceGetTagValuesOptions,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { config, locationService } from '@grafana/runtime';
|
import { config, locationService, RefreshEvent } from '@grafana/runtime';
|
||||||
import {
|
import {
|
||||||
sceneGraph,
|
sceneGraph,
|
||||||
SceneGridRow,
|
SceneGridRow,
|
||||||
|
|
@ -716,6 +716,9 @@ export class DashboardVariableDependency implements SceneVariableDependencyConfi
|
||||||
if (hasChanged) {
|
if (hasChanged) {
|
||||||
// Temp solution for some core panels (like dashlist) to know that variables have changed
|
// Temp solution for some core panels (like dashlist) to know that variables have changed
|
||||||
appEvents.publish(new VariablesChanged({ refreshAll: true, panelIds: [] }));
|
appEvents.publish(new VariablesChanged({ refreshAll: true, panelIds: [] }));
|
||||||
|
// Backwards compat with plugins that rely on the RefreshEvent when a
|
||||||
|
// variable changes. TODO: We should redirect plugin devs to use VariablesChanged event
|
||||||
|
this._dashboard.publishEvent(new RefreshEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (variable.state.name === PANEL_SEARCH_VAR) {
|
if (variable.state.name === PANEL_SEARCH_VAR) {
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,62 @@ describe('sceneVariablesSetToVariables', () => {
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle Query variable when sceneVariablesSetToVariables should discard options', () => {
|
||||||
|
const variable = new QueryVariable({
|
||||||
|
name: 'test',
|
||||||
|
label: 'test-label',
|
||||||
|
description: 'test-desc',
|
||||||
|
value: ['selected-value'],
|
||||||
|
text: ['selected-value-text'],
|
||||||
|
datasource: { uid: 'fake-std', type: 'fake-std' },
|
||||||
|
query: 'query',
|
||||||
|
options: [
|
||||||
|
{ label: 'test', value: 'test' },
|
||||||
|
{ label: 'test1', value: 'test1' },
|
||||||
|
{ label: 'test2', value: 'test2' },
|
||||||
|
],
|
||||||
|
includeAll: true,
|
||||||
|
allValue: 'test-all',
|
||||||
|
isMulti: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const set = new SceneVariableSet({
|
||||||
|
variables: [variable],
|
||||||
|
});
|
||||||
|
const result = sceneVariablesSetToVariables(set);
|
||||||
|
expect(result).toHaveLength(1);
|
||||||
|
expect(result[0].options).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle Query variable when sceneVariablesSetToVariables shoudl keep options', () => {
|
||||||
|
const variable = new QueryVariable({
|
||||||
|
name: 'test',
|
||||||
|
label: 'test-label',
|
||||||
|
description: 'test-desc',
|
||||||
|
value: ['test'],
|
||||||
|
text: ['test'],
|
||||||
|
datasource: { uid: 'fake-std', type: 'fake-std' },
|
||||||
|
query: 'query',
|
||||||
|
options: [
|
||||||
|
{ label: 'test', value: 'test' },
|
||||||
|
{ label: 'test1', value: 'test1' },
|
||||||
|
{ label: 'test2', value: 'test2' },
|
||||||
|
],
|
||||||
|
includeAll: true,
|
||||||
|
allValue: 'test-all',
|
||||||
|
isMulti: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const set = new SceneVariableSet({
|
||||||
|
variables: [variable],
|
||||||
|
});
|
||||||
|
const keepQueryOptions = true;
|
||||||
|
const result = sceneVariablesSetToVariables(set, keepQueryOptions);
|
||||||
|
expect(result).toHaveLength(1);
|
||||||
|
expect(result[0].options).not.toEqual([]);
|
||||||
|
expect(result[0].options?.length).toEqual(3);
|
||||||
|
});
|
||||||
|
|
||||||
it('should handle DatasourceVariable', () => {
|
it('should handle DatasourceVariable', () => {
|
||||||
const variable = new DataSourceVariable({
|
const variable = new DataSourceVariable({
|
||||||
name: 'test',
|
name: 'test',
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,16 @@ import { VariableHide, VariableModel, VariableOption, VariableRefresh, VariableS
|
||||||
|
|
||||||
import { getIntervalsQueryFromNewIntervalModel } from '../utils/utils';
|
import { getIntervalsQueryFromNewIntervalModel } from '../utils/utils';
|
||||||
|
|
||||||
export function sceneVariablesSetToVariables(set: SceneVariables) {
|
/**
|
||||||
|
* Converts a SceneVariables object into an array of VariableModel objects.
|
||||||
|
* @param set - The SceneVariables object containing the variables to convert.
|
||||||
|
* @param keepQueryOptions - (Optional) A boolean flag indicating whether to keep the options for query variables.
|
||||||
|
* This should be set to `false` when variables are saved in the dashboard model,
|
||||||
|
* but should be set to `true` when variables are used in the templateSrv to keep them in sync.
|
||||||
|
* If `true`, the options for query variables are kept.
|
||||||
|
* */
|
||||||
|
|
||||||
|
export function sceneVariablesSetToVariables(set: SceneVariables, keepQueryOptions?: boolean) {
|
||||||
const variables: VariableModel[] = [];
|
const variables: VariableModel[] = [];
|
||||||
for (const variable of set.state.variables) {
|
for (const variable of set.state.variables) {
|
||||||
const commonProperties = {
|
const commonProperties = {
|
||||||
|
|
@ -19,7 +28,7 @@ export function sceneVariablesSetToVariables(set: SceneVariables) {
|
||||||
let options: VariableOption[] = [];
|
let options: VariableOption[] = [];
|
||||||
// Not sure if we actually have to still support this option given
|
// Not sure if we actually have to still support this option given
|
||||||
// that it's not exposed in the UI
|
// that it's not exposed in the UI
|
||||||
if (variable.state.refresh === VariableRefresh.never) {
|
if (variable.state.refresh === VariableRefresh.never || keepQueryOptions) {
|
||||||
options = variableValueOptionsToVariableOptions(variable.state);
|
options = variableValueOptionsToVariableOptions(variable.state);
|
||||||
}
|
}
|
||||||
variables.push({
|
variables.push({
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,13 @@ import { sceneVariablesSetToVariables } from '../serialization/sceneVariablesSet
|
||||||
|
|
||||||
export function getVariablesCompatibility(sceneObject: SceneObject): TypedVariableModel[] {
|
export function getVariablesCompatibility(sceneObject: SceneObject): TypedVariableModel[] {
|
||||||
const set = sceneGraph.getVariables(sceneObject);
|
const set = sceneGraph.getVariables(sceneObject);
|
||||||
const legacyModels = sceneVariablesSetToVariables(set);
|
const keepQueryOptions = true;
|
||||||
|
|
||||||
|
// `sceneVariablesSetToVariables` is also used when transforming the scene to a save model.
|
||||||
|
// In those cases, query options will be stripped out.
|
||||||
|
// However, when `getVariablesCompatibility` is called from `templateSrv`, it is used to get all variables in the scene.
|
||||||
|
// Therefore, options should be kept.
|
||||||
|
const legacyModels = sceneVariablesSetToVariables(set, keepQueryOptions);
|
||||||
|
|
||||||
// Sadly templateSrv.getVariables returns TypedVariableModel but sceneVariablesSetToVariables return persisted schema model
|
// Sadly templateSrv.getVariables returns TypedVariableModel but sceneVariablesSetToVariables return persisted schema model
|
||||||
// They look close to identical (differ in what is optional in some places).
|
// They look close to identical (differ in what is optional in some places).
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue