mirror of https://github.com/grafana/grafana.git
feat: add support for a switch type of dashboard variable
CodeQL checks / Detect whether code changed (push) Waiting to run
Details
CodeQL checks / Analyze (actions) (push) Blocked by required conditions
Details
CodeQL checks / Analyze (go) (push) Blocked by required conditions
Details
CodeQL checks / Analyze (javascript) (push) Blocked by required conditions
Details
CodeQL checks / Detect whether code changed (push) Waiting to run
Details
CodeQL checks / Analyze (actions) (push) Blocked by required conditions
Details
CodeQL checks / Analyze (go) (push) Blocked by required conditions
Details
CodeQL checks / Analyze (javascript) (push) Blocked by required conditions
Details
This commit is contained in:
parent
aba713002b
commit
dbadd7a685
|
@ -596,6 +596,17 @@ export const versionedPages = {
|
|||
'11.0.0': 'data-testid ad-hoc filters variable mode toggle',
|
||||
},
|
||||
},
|
||||
SwitchVariable: {
|
||||
valuePairTypeSelect: {
|
||||
['12.3.0']: 'data-testid switch variable value pair type select',
|
||||
},
|
||||
enabledValueInput: {
|
||||
['12.3.0']: 'data-testid switch variable enabled value input',
|
||||
},
|
||||
disabledValueInput: {
|
||||
['12.3.0']: 'data-testid switch variable disabled value input',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
SceneVariableState,
|
||||
ControlsLabel,
|
||||
ControlsLayout,
|
||||
sceneUtils,
|
||||
} from '@grafana/scenes';
|
||||
import { useElementSelection, useStyles2 } from '@grafana/ui';
|
||||
|
||||
|
@ -64,6 +65,18 @@ export function VariableValueSelectWrapper({ variable, inMenu }: VariableSelectP
|
|||
}
|
||||
};
|
||||
|
||||
// For switch variables in menu, we want to show the switch on the left and the label on the right
|
||||
if (inMenu && sceneUtils.isSwitchVariable(variable)) {
|
||||
return (
|
||||
<div className={styles.switchMenuContainer} data-testid={selectors.pages.Dashboard.SubMenu.submenuItem}>
|
||||
<div className={styles.switchControl}>
|
||||
<variable.Component model={variable} />
|
||||
</div>
|
||||
<VariableLabel variable={variable} layout={'vertical'} className={styles.switchLabel} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (inMenu) {
|
||||
return (
|
||||
<div className={styles.verticalContainer} data-testid={selectors.pages.Dashboard.SubMenu.submenuItem}>
|
||||
|
@ -134,6 +147,21 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
|||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}),
|
||||
switchMenuContainer: css({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: theme.spacing(1),
|
||||
}),
|
||||
switchControl: css({
|
||||
'& > div': {
|
||||
border: 'none',
|
||||
background: 'transparent',
|
||||
paddingRight: theme.spacing(0.5),
|
||||
},
|
||||
}),
|
||||
switchLabel: css({
|
||||
marginTop: theme.spacing(0.5),
|
||||
}),
|
||||
labelWrapper: css({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
IntervalVariable,
|
||||
QueryVariable,
|
||||
SceneVariableSet,
|
||||
SwitchVariable,
|
||||
TextBoxVariable,
|
||||
} from '@grafana/scenes';
|
||||
import { DataSourceRef, VariableHide, VariableRefresh } from '@grafana/schema';
|
||||
|
@ -877,6 +878,95 @@ describe('sceneVariablesSetToVariables', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should handle SwitchVariable with true value', () => {
|
||||
const variable = new SwitchVariable({
|
||||
name: 'test',
|
||||
label: 'test-label',
|
||||
description: 'test-desc',
|
||||
hide: VariableHide.inControlsMenu,
|
||||
value: true,
|
||||
skipUrlSync: true,
|
||||
});
|
||||
const set = new SceneVariableSet({
|
||||
variables: [variable],
|
||||
});
|
||||
|
||||
const result = sceneVariablesSetToVariables(set);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toMatchInlineSnapshot(`
|
||||
{
|
||||
"current": {
|
||||
"text": "True",
|
||||
"value": "true",
|
||||
},
|
||||
"description": "test-desc",
|
||||
"hide": 3,
|
||||
"label": "test-label",
|
||||
"name": "test",
|
||||
"skipUrlSync": true,
|
||||
"type": "switch",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle SwitchVariable with false value', () => {
|
||||
const variable = new SwitchVariable({
|
||||
name: 'test',
|
||||
label: 'test-label',
|
||||
description: 'test-desc',
|
||||
hide: VariableHide.inControlsMenu,
|
||||
value: false,
|
||||
skipUrlSync: false,
|
||||
});
|
||||
const set = new SceneVariableSet({
|
||||
variables: [variable],
|
||||
});
|
||||
|
||||
const result = sceneVariablesSetToVariables(set);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toMatchInlineSnapshot(`
|
||||
{
|
||||
"current": {
|
||||
"text": "False",
|
||||
"value": "false",
|
||||
},
|
||||
"description": "test-desc",
|
||||
"hide": 3,
|
||||
"label": "test-label",
|
||||
"name": "test",
|
||||
"type": "switch",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle SwitchVariable with minimal configuration', () => {
|
||||
const variable = new SwitchVariable({
|
||||
name: 'minimal',
|
||||
value: true,
|
||||
});
|
||||
const set = new SceneVariableSet({
|
||||
variables: [variable],
|
||||
});
|
||||
|
||||
const result = sceneVariablesSetToVariables(set);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toMatchInlineSnapshot(`
|
||||
{
|
||||
"current": {
|
||||
"text": "True",
|
||||
"value": "true",
|
||||
},
|
||||
"description": undefined,
|
||||
"label": undefined,
|
||||
"name": "minimal",
|
||||
"type": "switch",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
describe('sceneVariablesSetToSchemaV2Variables', () => {
|
||||
it('should handle QueryVariable', () => {
|
||||
const variable = new QueryVariable({
|
||||
|
@ -1455,5 +1545,110 @@ describe('sceneVariablesSetToVariables', () => {
|
|||
expect(() => sceneVariablesSetToSchemaV2Variables(set)).toThrow('Unsupported variable type');
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle SwitchVariable with true value', () => {
|
||||
const variable = new SwitchVariable({
|
||||
name: 'test',
|
||||
label: 'test-label',
|
||||
description: 'test-desc',
|
||||
hide: VariableHide.inControlsMenu,
|
||||
value: true,
|
||||
skipUrlSync: true,
|
||||
});
|
||||
const set = new SceneVariableSet({
|
||||
variables: [variable],
|
||||
});
|
||||
|
||||
const result = sceneVariablesSetToSchemaV2Variables(set);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toMatchInlineSnapshot(`
|
||||
{
|
||||
"kind": "SwitchVariable",
|
||||
"spec": {
|
||||
"current": true,
|
||||
"description": "test-desc",
|
||||
"hide": "inControlsMenu",
|
||||
"label": "test-label",
|
||||
"name": "test",
|
||||
"skipUrlSync": true,
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle SwitchVariable with false value', () => {
|
||||
const variable = new SwitchVariable({
|
||||
name: 'test',
|
||||
label: 'test-label',
|
||||
description: 'test-desc',
|
||||
hide: VariableHide.inControlsMenu,
|
||||
value: false,
|
||||
skipUrlSync: false,
|
||||
});
|
||||
const set = new SceneVariableSet({
|
||||
variables: [variable],
|
||||
});
|
||||
|
||||
const result = sceneVariablesSetToSchemaV2Variables(set);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toMatchInlineSnapshot(`
|
||||
{
|
||||
"kind": "SwitchVariable",
|
||||
"spec": {
|
||||
"current": false,
|
||||
"description": "test-desc",
|
||||
"hide": "inControlsMenu",
|
||||
"label": "test-label",
|
||||
"name": "test",
|
||||
"skipUrlSync": false,
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle SwitchVariable with minimal configuration', () => {
|
||||
const variable = new SwitchVariable({
|
||||
name: 'minimal',
|
||||
value: true,
|
||||
});
|
||||
const set = new SceneVariableSet({
|
||||
variables: [variable],
|
||||
});
|
||||
|
||||
const result = sceneVariablesSetToSchemaV2Variables(set);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toMatchInlineSnapshot(`
|
||||
{
|
||||
"kind": "SwitchVariable",
|
||||
"spec": {
|
||||
"current": true,
|
||||
"description": undefined,
|
||||
"hide": "dontHide",
|
||||
"label": undefined,
|
||||
"name": "minimal",
|
||||
"skipUrlSync": false,
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle SwitchVariable with string value conversion', () => {
|
||||
// Test edge case where value might be passed as string
|
||||
const variable = new SwitchVariable({
|
||||
name: 'test',
|
||||
value: 'true' as any, // Simulating potential string input
|
||||
});
|
||||
const set = new SceneVariableSet({
|
||||
variables: [variable],
|
||||
});
|
||||
|
||||
const result = sceneVariablesSetToSchemaV2Variables(set);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].spec.current).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -26,6 +26,7 @@ import {
|
|||
VariableOption,
|
||||
defaultDataQueryKind,
|
||||
AdHocFilterWithLabels,
|
||||
SwitchVariableKind,
|
||||
} from '@grafana/schema/dist/esm/schema/dashboard/v2';
|
||||
import { getDefaultDatasource } from 'app/features/dashboard/api/ResponseTransformers';
|
||||
|
||||
|
@ -210,6 +211,24 @@ export function sceneVariablesSetToVariables(set: SceneVariables, keepQueryOptio
|
|||
filters: [...validateFiltersOrigin(variable.state.originFilters), ...variable.state.filters],
|
||||
defaultKeys: variable.state.defaultKeys,
|
||||
});
|
||||
} else if (sceneUtils.isSwitchVariable(variable)) {
|
||||
variables.push({
|
||||
...commonProperties,
|
||||
current: {
|
||||
value: variable.state.value,
|
||||
text: variable.state.value,
|
||||
},
|
||||
options: [
|
||||
{
|
||||
value: variable.state.enabledValue,
|
||||
text: variable.state.enabledValue,
|
||||
},
|
||||
{
|
||||
value: variable.state.disabledValue,
|
||||
text: variable.state.disabledValue,
|
||||
},
|
||||
],
|
||||
});
|
||||
} else if (variable.state.type === 'system') {
|
||||
// Not persisted
|
||||
} else {
|
||||
|
@ -264,6 +283,7 @@ export function sceneVariablesSetToSchemaV2Variables(
|
|||
| ConstantVariableKind
|
||||
| GroupByVariableKind
|
||||
| AdhocVariableKind
|
||||
| SwitchVariableKind
|
||||
> {
|
||||
let variables: Array<
|
||||
| QueryVariableKind
|
||||
|
@ -274,6 +294,7 @@ export function sceneVariablesSetToSchemaV2Variables(
|
|||
| ConstantVariableKind
|
||||
| GroupByVariableKind
|
||||
| AdhocVariableKind
|
||||
| SwitchVariableKind
|
||||
> = [];
|
||||
|
||||
for (const variable of set.state.variables) {
|
||||
|
@ -294,6 +315,8 @@ export function sceneVariablesSetToSchemaV2Variables(
|
|||
};
|
||||
|
||||
let options: VariableOption[] = [];
|
||||
|
||||
// Query variable
|
||||
if (sceneUtils.isQueryVariable(variable)) {
|
||||
// Not sure if we actually have to still support this option given
|
||||
// that it's not exposed in the UI
|
||||
|
@ -355,6 +378,8 @@ export function sceneVariablesSetToSchemaV2Variables(
|
|||
},
|
||||
};
|
||||
variables.push(queryVariable);
|
||||
|
||||
// Custom variable
|
||||
} else if (sceneUtils.isCustomVariable(variable)) {
|
||||
options = variableValueOptionsToVariableOptions(variable.state);
|
||||
const customVariable: CustomVariableKind = {
|
||||
|
@ -371,6 +396,8 @@ export function sceneVariablesSetToSchemaV2Variables(
|
|||
},
|
||||
};
|
||||
variables.push(customVariable);
|
||||
|
||||
// Datasource variable
|
||||
} else if (sceneUtils.isDataSourceVariable(variable)) {
|
||||
const datasourceVariable: DatasourceVariableKind = {
|
||||
kind: 'DatasourceVariable',
|
||||
|
@ -392,6 +419,8 @@ export function sceneVariablesSetToSchemaV2Variables(
|
|||
}
|
||||
|
||||
variables.push(datasourceVariable);
|
||||
|
||||
// Constant variable
|
||||
} else if (sceneUtils.isConstantVariable(variable)) {
|
||||
const constantVariable: ConstantVariableKind = {
|
||||
kind: 'ConstantVariable',
|
||||
|
@ -407,6 +436,8 @@ export function sceneVariablesSetToSchemaV2Variables(
|
|||
},
|
||||
};
|
||||
variables.push(constantVariable);
|
||||
|
||||
// Interval variable
|
||||
} else if (sceneUtils.isIntervalVariable(variable)) {
|
||||
const intervals = getIntervalsQueryFromNewIntervalModel(variable.state.intervals);
|
||||
const intervalVariable: IntervalVariableKind = {
|
||||
|
@ -431,6 +462,8 @@ export function sceneVariablesSetToSchemaV2Variables(
|
|||
},
|
||||
};
|
||||
variables.push(intervalVariable);
|
||||
|
||||
// Textbox variable
|
||||
} else if (sceneUtils.isTextBoxVariable(variable)) {
|
||||
const current = {
|
||||
text: variable.state.value,
|
||||
|
@ -447,6 +480,8 @@ export function sceneVariablesSetToSchemaV2Variables(
|
|||
};
|
||||
|
||||
variables.push(textBoxVariable);
|
||||
|
||||
// Groupby variable
|
||||
} else if (sceneUtils.isGroupByVariable(variable) && config.featureToggles.groupByVariable) {
|
||||
options = variableValueOptionsToVariableOptions(variable.state);
|
||||
|
||||
|
@ -483,6 +518,8 @@ export function sceneVariablesSetToSchemaV2Variables(
|
|||
},
|
||||
};
|
||||
variables.push(groupVariable);
|
||||
|
||||
// Adhoc variable
|
||||
} else if (sceneUtils.isAdHocVariable(variable)) {
|
||||
const ds = getDataSourceForQuery(
|
||||
variable.state.datasource,
|
||||
|
@ -508,6 +545,19 @@ export function sceneVariablesSetToSchemaV2Variables(
|
|||
},
|
||||
};
|
||||
variables.push(adhocVariable);
|
||||
|
||||
// Switch variable
|
||||
} else if (sceneUtils.isSwitchVariable(variable)) {
|
||||
const switchVariable: SwitchVariableKind = {
|
||||
kind: 'SwitchVariable',
|
||||
spec: {
|
||||
...commonProperties,
|
||||
current: variable.state.value,
|
||||
enabledValue: variable.state.enabledValue,
|
||||
disabledValue: variable.state.disabledValue,
|
||||
},
|
||||
};
|
||||
variables.push(switchVariable);
|
||||
} else if (variable.state.type === 'system') {
|
||||
// Do nothing
|
||||
} else {
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
SceneVariable,
|
||||
SceneVariableSet,
|
||||
ScopesVariable,
|
||||
SwitchVariable,
|
||||
TextBoxVariable,
|
||||
} from '@grafana/scenes';
|
||||
import {
|
||||
|
@ -34,11 +35,13 @@ import {
|
|||
defaultIntervalVariableKind,
|
||||
defaultQueryVariableKind,
|
||||
defaultTextVariableKind,
|
||||
defaultSwitchVariableKind,
|
||||
GroupByVariableKind,
|
||||
IntervalVariableKind,
|
||||
LibraryPanelKind,
|
||||
PanelKind,
|
||||
QueryVariableKind,
|
||||
SwitchVariableKind,
|
||||
TextVariableKind,
|
||||
} from '@grafana/schema/dist/esm/schema/dashboard/v2';
|
||||
import { DEFAULT_ANNOTATION_COLOR } from '@grafana/ui';
|
||||
|
@ -92,7 +95,8 @@ export type TypedVariableModelV2 =
|
|||
| IntervalVariableKind
|
||||
| CustomVariableKind
|
||||
| GroupByVariableKind
|
||||
| AdhocVariableKind;
|
||||
| AdhocVariableKind
|
||||
| SwitchVariableKind;
|
||||
|
||||
export function transformSaveModelSchemaV2ToScene(dto: DashboardWithAccessInfo<DashboardV2Spec>): DashboardScene {
|
||||
const { spec: dashboard, metadata, apiVersion } = dto;
|
||||
|
@ -415,6 +419,15 @@ function createSceneVariableFromVariableModel(variable: TypedVariableModelV2): S
|
|||
// @ts-expect-error
|
||||
defaultOptions: variable.options,
|
||||
});
|
||||
} else if (variable.kind === defaultSwitchVariableKind().kind) {
|
||||
return new SwitchVariable({
|
||||
...commonProperties,
|
||||
value: variable.spec.current ?? 'false',
|
||||
enabledValue: variable.spec.enabledValue ?? 'true',
|
||||
disabledValue: variable.spec.disabledValue ?? 'false',
|
||||
skipUrlSync: variable.spec.skipUrlSync,
|
||||
hide: transformVariableHideToEnumV1(variable.spec.hide),
|
||||
});
|
||||
} else {
|
||||
throw new Error(`Scenes: Unsupported variable type ${variable.kind}`);
|
||||
}
|
||||
|
@ -520,6 +533,11 @@ export function createSnapshotVariable(variable: TypedVariableModelV2): SceneVar
|
|||
value: '',
|
||||
text: '',
|
||||
};
|
||||
} else if (variable.kind === 'SwitchVariable') {
|
||||
current = {
|
||||
value: variable.spec.current ?? 'false',
|
||||
text: variable.spec.current ?? 'false',
|
||||
};
|
||||
} else {
|
||||
current = {
|
||||
value: variable.spec.current?.value ?? '',
|
||||
|
|
|
@ -44,6 +44,7 @@ import {
|
|||
FieldColor,
|
||||
defaultFieldConfig,
|
||||
defaultDataQueryKind,
|
||||
SwitchVariableKind,
|
||||
} from '../../../../../packages/grafana-schema/src/schema/dashboard/v2';
|
||||
import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet';
|
||||
import { DashboardScene, DashboardSceneState } from '../scene/DashboardScene';
|
||||
|
@ -411,6 +412,7 @@ function getVariables(oldDash: DashboardSceneState, dsReferencesMapping?: DSRefe
|
|||
| ConstantVariableKind
|
||||
| GroupByVariableKind
|
||||
| AdhocVariableKind
|
||||
| SwitchVariableKind
|
||||
> = [];
|
||||
|
||||
if (variablesSet instanceof SceneVariableSet) {
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
import { SwitchVariableForm } from './SwitchVariableForm';
|
||||
|
||||
describe('SwitchVariableForm', () => {
|
||||
const onEnabledValueChange = jest.fn();
|
||||
const onDisabledValueChange = jest.fn();
|
||||
|
||||
const defaultProps = {
|
||||
enabledValue: 'true',
|
||||
disabledValue: 'false',
|
||||
onEnabledValueChange,
|
||||
onDisabledValueChange,
|
||||
};
|
||||
|
||||
function renderForm(props = {}) {
|
||||
return render(<SwitchVariableForm {...defaultProps} {...props} />);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should render the form', () => {
|
||||
render(<SwitchVariableForm {...defaultProps} />);
|
||||
|
||||
expect(screen.getByText('Switch options')).toBeInTheDocument();
|
||||
expect(screen.getByText('Value pair type')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId(selectors.pages.Dashboard.Settings.Variables.Edit.SwitchVariable.valuePairTypeSelect)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not show custom inputs for predefined value pair types', () => {
|
||||
renderForm();
|
||||
|
||||
expect(
|
||||
screen.queryByTestId(selectors.pages.Dashboard.Settings.Variables.Edit.SwitchVariable.enabledValueInput)
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByTestId(selectors.pages.Dashboard.Settings.Variables.Edit.SwitchVariable.disabledValueInput)
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show custom inputs when value pair type is custom', () => {
|
||||
renderForm({
|
||||
enabledValue: 'on',
|
||||
disabledValue: 'off',
|
||||
});
|
||||
|
||||
expect(
|
||||
screen.getByTestId(selectors.pages.Dashboard.Settings.Variables.Edit.SwitchVariable.enabledValueInput)
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId(selectors.pages.Dashboard.Settings.Variables.Edit.SwitchVariable.disabledValueInput)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should call onEnabledValueChange when enabled value input changes', async () => {
|
||||
const user = userEvent.setup();
|
||||
renderForm({
|
||||
enabledValue: '',
|
||||
disabledValue: 'off',
|
||||
});
|
||||
|
||||
const enabledInput = screen.getByTestId(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.SwitchVariable.enabledValueInput
|
||||
);
|
||||
await user.type(enabledInput, 't');
|
||||
|
||||
expect(onEnabledValueChange).toHaveBeenCalledTimes(1);
|
||||
expect(onEnabledValueChange).toHaveBeenNthCalledWith(1, 't');
|
||||
});
|
||||
|
||||
it('should call onDisabledValueChange when disabled value input changes', async () => {
|
||||
const user = userEvent.setup();
|
||||
renderForm({
|
||||
enabledValue: 'on',
|
||||
disabledValue: '',
|
||||
});
|
||||
|
||||
const disabledInput = screen.getByTestId(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.SwitchVariable.disabledValueInput
|
||||
);
|
||||
await user.type(disabledInput, 't');
|
||||
|
||||
expect(onDisabledValueChange).toHaveBeenCalledTimes(1);
|
||||
expect(onDisabledValueChange).toHaveBeenCalledWith('t');
|
||||
});
|
||||
|
||||
it('should handle all predefined value pair types correctly', () => {
|
||||
const testCases = [
|
||||
{ enabled: 'true', disabled: 'false', expected: 'True / False', hasCustomInputs: false },
|
||||
{ enabled: '1', disabled: '0', expected: '1 / 0', hasCustomInputs: false },
|
||||
{ enabled: 'yes', disabled: 'no', expected: 'Yes / No', hasCustomInputs: false },
|
||||
{ enabled: 'custom', disabled: 'value', expected: 'Custom', hasCustomInputs: true },
|
||||
];
|
||||
|
||||
testCases.forEach(({ enabled, disabled, expected, hasCustomInputs }) => {
|
||||
const { unmount } = renderForm({
|
||||
enabledValue: enabled,
|
||||
disabledValue: disabled,
|
||||
});
|
||||
|
||||
expect(
|
||||
screen.getByTestId(selectors.pages.Dashboard.Settings.Variables.Edit.SwitchVariable.valuePairTypeSelect)
|
||||
).toHaveValue(expected);
|
||||
|
||||
if (hasCustomInputs) {
|
||||
expect(
|
||||
screen.getByTestId(selectors.pages.Dashboard.Settings.Variables.Edit.SwitchVariable.enabledValueInput)
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId(selectors.pages.Dashboard.Settings.Variables.Edit.SwitchVariable.disabledValueInput)
|
||||
).toBeInTheDocument();
|
||||
} else {
|
||||
expect(
|
||||
screen.queryByTestId(selectors.pages.Dashboard.Settings.Variables.Edit.SwitchVariable.enabledValueInput)
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByTestId(selectors.pages.Dashboard.Settings.Variables.Edit.SwitchVariable.disabledValueInput)
|
||||
).not.toBeInTheDocument();
|
||||
}
|
||||
|
||||
unmount();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,140 @@
|
|||
import { useState } from 'react';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { Field, Combobox, Input, type ComboboxOption } from '@grafana/ui';
|
||||
|
||||
import { VariableLegend } from './VariableLegend';
|
||||
|
||||
interface SwitchVariableFormProps {
|
||||
enabledValue: string;
|
||||
disabledValue: string;
|
||||
onEnabledValueChange: (value: string) => void;
|
||||
onDisabledValueChange: (value: string) => void;
|
||||
}
|
||||
|
||||
const VALUE_PAIR_OPTIONS: Array<ComboboxOption<string>> = [
|
||||
{ label: 'True / False', value: 'boolean' },
|
||||
{ label: '1 / 0', value: 'number' },
|
||||
{ label: 'Yes / No', value: 'string' },
|
||||
{ label: 'Custom', value: 'custom' },
|
||||
];
|
||||
|
||||
export function SwitchVariableForm({
|
||||
enabledValue,
|
||||
disabledValue,
|
||||
onEnabledValueChange,
|
||||
onDisabledValueChange,
|
||||
}: SwitchVariableFormProps) {
|
||||
const currentValuePairType = getCurrentValuePairType(enabledValue, disabledValue);
|
||||
const [isCustomValuePairType, setIsCustomValuePairType] = useState(currentValuePairType === 'custom');
|
||||
|
||||
const onValuePairTypeChange = (selection: ComboboxOption<string> | null) => {
|
||||
if (!selection?.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (selection.value) {
|
||||
case 'boolean':
|
||||
onEnabledValueChange('true');
|
||||
onDisabledValueChange('false');
|
||||
setIsCustomValuePairType(false);
|
||||
break;
|
||||
case 'number':
|
||||
onEnabledValueChange('1');
|
||||
onDisabledValueChange('0');
|
||||
setIsCustomValuePairType(false);
|
||||
break;
|
||||
case 'string':
|
||||
onEnabledValueChange('yes');
|
||||
onDisabledValueChange('no');
|
||||
setIsCustomValuePairType(false);
|
||||
break;
|
||||
case 'custom':
|
||||
setIsCustomValuePairType(true);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<VariableLegend>
|
||||
<Trans i18nKey="dashboard-scene.switch-variable-form.switch-options">Switch options</Trans>
|
||||
</VariableLegend>
|
||||
|
||||
<Field
|
||||
label={t('dashboard-scene.switch-variable-form.value-pair-type', 'Value pair type')}
|
||||
description={t(
|
||||
'dashboard-scene.switch-variable-form.value-pair-type-description',
|
||||
'Choose the type of values for the switch states'
|
||||
)}
|
||||
>
|
||||
<Combobox
|
||||
width={40}
|
||||
value={currentValuePairType}
|
||||
options={VALUE_PAIR_OPTIONS}
|
||||
onChange={onValuePairTypeChange}
|
||||
data-testid={selectors.pages.Dashboard.Settings.Variables.Edit.SwitchVariable.valuePairTypeSelect}
|
||||
/>
|
||||
</Field>
|
||||
|
||||
{/* Custom value pair type */}
|
||||
{isCustomValuePairType && (
|
||||
<>
|
||||
<Field
|
||||
label={t('dashboard-scene.switch-variable-form.enabled-value', 'Enabled value')}
|
||||
description={t(
|
||||
'dashboard-scene.switch-variable-form.enabled-value-description',
|
||||
'Value when switch is enabled'
|
||||
)}
|
||||
>
|
||||
<Input
|
||||
width={40}
|
||||
value={enabledValue}
|
||||
onChange={(event) => {
|
||||
onEnabledValueChange(event.currentTarget.value);
|
||||
}}
|
||||
placeholder={t(
|
||||
'dashboard-scene.switch-variable-form.enabled-value-placeholder',
|
||||
'e.g. On, Enabled, Active'
|
||||
)}
|
||||
data-testid={selectors.pages.Dashboard.Settings.Variables.Edit.SwitchVariable.enabledValueInput}
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<Field
|
||||
label={t('dashboard-scene.switch-variable-form.disabled-value', 'Disabled value')}
|
||||
description={t(
|
||||
'dashboard-scene.switch-variable-form.disabled-value-description',
|
||||
'Value when switch is disabled'
|
||||
)}
|
||||
>
|
||||
<Input
|
||||
width={40}
|
||||
value={disabledValue}
|
||||
onChange={(event) => onDisabledValueChange(event.currentTarget.value)}
|
||||
placeholder={t(
|
||||
'dashboard-scene.switch-variable-form.disabled-value-placeholder',
|
||||
'e.g. Off, Disabled, Inactive'
|
||||
)}
|
||||
data-testid={selectors.pages.Dashboard.Settings.Variables.Edit.SwitchVariable.disabledValueInput}
|
||||
/>
|
||||
</Field>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function getCurrentValuePairType(enabledValue: string, disabledValue: string) {
|
||||
if (enabledValue === 'true' && disabledValue === 'false') {
|
||||
return 'boolean';
|
||||
}
|
||||
if (enabledValue === '1' && disabledValue === '0') {
|
||||
return 'number';
|
||||
}
|
||||
if (enabledValue === 'yes' && disabledValue === 'no') {
|
||||
return 'string';
|
||||
}
|
||||
return 'custom';
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { SwitchVariable } from '@grafana/scenes';
|
||||
|
||||
import { SwitchVariableEditor } from './SwitchVariableEditor';
|
||||
|
||||
describe('SwitchVariableEditor', () => {
|
||||
it('should render with default value false', () => {
|
||||
const variable = new SwitchVariable({ name: 'test', value: false });
|
||||
render(<SwitchVariableEditor variable={variable} onChange={() => {}} />);
|
||||
|
||||
const switchElement = screen.getByRole('switch');
|
||||
expect(switchElement).not.toBeChecked();
|
||||
});
|
||||
|
||||
it('should render with default value true', () => {
|
||||
const variable = new SwitchVariable({ name: 'test', value: true });
|
||||
render(<SwitchVariableEditor variable={variable} onChange={() => {}} />);
|
||||
|
||||
const switchElement = screen.getByRole('switch');
|
||||
expect(switchElement).toBeChecked();
|
||||
});
|
||||
|
||||
it('should update variable state when switch is clicked', async () => {
|
||||
const variable = new SwitchVariable({ name: 'test', value: false });
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<SwitchVariableEditor variable={variable} onChange={() => {}} />);
|
||||
|
||||
const switchElement = screen.getByRole('switch');
|
||||
await user.click(switchElement);
|
||||
|
||||
expect(variable.state.value).toBe(true);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,57 @@
|
|||
import { t } from '@grafana/i18n';
|
||||
import { SceneVariable, SwitchVariable } from '@grafana/scenes';
|
||||
import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor';
|
||||
|
||||
import { SwitchVariableForm } from '../components/SwitchVariableForm';
|
||||
|
||||
interface SwitchVariableEditorProps {
|
||||
variable: SwitchVariable;
|
||||
}
|
||||
|
||||
export function SwitchVariableEditor({ variable }: SwitchVariableEditorProps) {
|
||||
const { value, enabledValue, disabledValue } = variable.useState();
|
||||
|
||||
const onEnabledValueChange = (newEnabledValue: string) => {
|
||||
const isCurrentlyEnabled = value === enabledValue;
|
||||
|
||||
if (isCurrentlyEnabled) {
|
||||
variable.setState({ enabledValue: newEnabledValue, value: newEnabledValue });
|
||||
} else {
|
||||
variable.setState({ enabledValue: newEnabledValue });
|
||||
}
|
||||
};
|
||||
|
||||
const onDisabledValueChange = (newDisabledValue: string) => {
|
||||
const isCurrentlyDisabled = value === disabledValue;
|
||||
|
||||
if (isCurrentlyDisabled) {
|
||||
variable.setState({ disabledValue: newDisabledValue, value: newDisabledValue });
|
||||
} else {
|
||||
variable.setState({ disabledValue: newDisabledValue });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SwitchVariableForm
|
||||
enabledValue={enabledValue}
|
||||
disabledValue={disabledValue}
|
||||
onEnabledValueChange={onEnabledValueChange}
|
||||
onDisabledValueChange={onDisabledValueChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function getSwitchVariableOptions(variable: SceneVariable): OptionsPaneItemDescriptor[] {
|
||||
if (!(variable instanceof SwitchVariable)) {
|
||||
console.warn('getSwitchVariableOptions: variable is not a SwitchVariable');
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
new OptionsPaneItemDescriptor({
|
||||
title: t('dashboard-scene.switch-variable-form.label-value', 'Default value'),
|
||||
id: `variable-${variable.state.name}-value`,
|
||||
render: () => <SwitchVariableEditor variable={variable} />,
|
||||
}),
|
||||
];
|
||||
}
|
|
@ -18,6 +18,7 @@ import {
|
|||
AdHocFiltersVariable,
|
||||
SceneVariableState,
|
||||
SceneVariableSet,
|
||||
SwitchVariable,
|
||||
} from '@grafana/scenes';
|
||||
import { VariableHide, VariableType } from '@grafana/schema';
|
||||
import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor';
|
||||
|
@ -31,6 +32,7 @@ import { DataSourceVariableEditor, getDataSourceVariableOptions } from './editor
|
|||
import { getGroupByVariableOptions, GroupByVariableEditor } from './editors/GroupByVariableEditor';
|
||||
import { getIntervalVariableOptions, IntervalVariableEditor } from './editors/IntervalVariableEditor';
|
||||
import { getQueryVariableOptions, QueryVariableEditor } from './editors/QueryVariableEditor';
|
||||
import { getSwitchVariableOptions, SwitchVariableEditor } from './editors/SwitchVariableEditor';
|
||||
import { TextBoxVariableEditor, getTextBoxVariableOptions } from './editors/TextBoxVariableEditor';
|
||||
|
||||
interface EditableVariableConfig {
|
||||
|
@ -117,6 +119,15 @@ export const getEditableVariables: () => Record<EditableVariableType, EditableVa
|
|||
editor: TextBoxVariableEditor,
|
||||
getOptions: getTextBoxVariableOptions,
|
||||
},
|
||||
switch: {
|
||||
name: t('dashboard-scene.get-editable-variables.name.switch', 'Switch'),
|
||||
description: t(
|
||||
'dashboard-scene.get-editable-variables.description.users-enter-arbitrary-strings-switch',
|
||||
'A variable that can be toggled on and off'
|
||||
),
|
||||
editor: SwitchVariableEditor,
|
||||
getOptions: getSwitchVariableOptions,
|
||||
},
|
||||
});
|
||||
|
||||
export function getEditableVariableDefinition(type: string): EditableVariableConfig {
|
||||
|
@ -138,6 +149,7 @@ export const EDITABLE_VARIABLES_SELECT_ORDER: EditableVariableType[] = [
|
|||
'interval',
|
||||
'adhoc',
|
||||
'groupby',
|
||||
'switch',
|
||||
];
|
||||
|
||||
export function getVariableTypeSelectOptions(): Array<SelectableValue<EditableVariableType>> {
|
||||
|
@ -187,6 +199,8 @@ export function getVariableScene(type: EditableVariableType, initialState: Commo
|
|||
return new GroupByVariable(initialState);
|
||||
case 'textbox':
|
||||
return new TextBoxVariable(initialState);
|
||||
case 'switch':
|
||||
return new SwitchVariable(initialState);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,7 +276,8 @@ export function isSceneVariableInstance(sceneObject: SceneObject): sceneObject i
|
|||
sceneUtils.isIntervalVariable(sceneObject) ||
|
||||
sceneUtils.isQueryVariable(sceneObject) ||
|
||||
sceneUtils.isTextBoxVariable(sceneObject) ||
|
||||
sceneUtils.isGroupByVariable(sceneObject)
|
||||
sceneUtils.isGroupByVariable(sceneObject) ||
|
||||
sceneUtils.isSwitchVariable(sceneObject)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
IntervalVariableModel,
|
||||
LoadingState,
|
||||
QueryVariableModel,
|
||||
SwitchVariableModel,
|
||||
TextBoxVariableModel,
|
||||
TypedVariableModel,
|
||||
} from '@grafana/data';
|
||||
|
@ -17,6 +18,7 @@ import {
|
|||
GroupByVariable,
|
||||
QueryVariable,
|
||||
SceneVariableSet,
|
||||
SwitchVariable,
|
||||
} from '@grafana/scenes';
|
||||
import { defaultDashboard, defaultTimePickerConfig, VariableType } from '@grafana/schema';
|
||||
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
|
||||
|
@ -657,6 +659,340 @@ describe('when creating variables objects', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should migrate switch variable with string true value', () => {
|
||||
const variable: SwitchVariableModel = {
|
||||
id: 'switch0',
|
||||
global: false,
|
||||
index: 0,
|
||||
state: LoadingState.Done,
|
||||
error: null,
|
||||
name: 'switchVar',
|
||||
label: 'Switch Label',
|
||||
description: 'Switch Description',
|
||||
type: 'switch',
|
||||
rootStateKey: 'N4XLmH5Vz',
|
||||
current: {
|
||||
selected: true,
|
||||
text: 'true',
|
||||
value: 'true',
|
||||
},
|
||||
hide: 0,
|
||||
skipUrlSync: false,
|
||||
options: [
|
||||
{
|
||||
selected: true,
|
||||
text: 'true',
|
||||
value: 'true',
|
||||
},
|
||||
{
|
||||
selected: false,
|
||||
text: 'false',
|
||||
value: 'false',
|
||||
},
|
||||
],
|
||||
query: '',
|
||||
multi: false,
|
||||
includeAll: false,
|
||||
allValue: null,
|
||||
};
|
||||
|
||||
const migrated = createSceneVariableFromVariableModel(variable);
|
||||
const { key, ...rest } = migrated.state;
|
||||
|
||||
expect(migrated).toBeInstanceOf(SwitchVariable);
|
||||
expect(rest).toEqual({
|
||||
description: 'Switch Description',
|
||||
hide: 0,
|
||||
label: 'Switch Label',
|
||||
name: 'switchVar',
|
||||
skipUrlSync: false,
|
||||
type: 'switch',
|
||||
value: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should migrate switch variable with string false value', () => {
|
||||
const variable: SwitchVariableModel = {
|
||||
id: 'switch1',
|
||||
global: false,
|
||||
index: 0,
|
||||
state: LoadingState.Done,
|
||||
error: null,
|
||||
name: 'switchVar2',
|
||||
label: 'Switch Label 2',
|
||||
description: 'Switch Description 2',
|
||||
type: 'switch',
|
||||
rootStateKey: 'N4XLmH5Vz',
|
||||
current: {
|
||||
selected: true,
|
||||
text: 'false',
|
||||
value: 'false',
|
||||
},
|
||||
hide: 1,
|
||||
skipUrlSync: true,
|
||||
options: [
|
||||
{
|
||||
selected: false,
|
||||
text: 'true',
|
||||
value: 'true',
|
||||
},
|
||||
{
|
||||
selected: true,
|
||||
text: 'false',
|
||||
value: 'false',
|
||||
},
|
||||
],
|
||||
query: '',
|
||||
multi: false,
|
||||
includeAll: false,
|
||||
allValue: null,
|
||||
};
|
||||
|
||||
const migrated = createSceneVariableFromVariableModel(variable);
|
||||
const { key, ...rest } = migrated.state;
|
||||
|
||||
expect(migrated).toBeInstanceOf(SwitchVariable);
|
||||
expect(rest).toEqual({
|
||||
description: 'Switch Description 2',
|
||||
hide: 1,
|
||||
label: 'Switch Label 2',
|
||||
name: 'switchVar2',
|
||||
skipUrlSync: true,
|
||||
type: 'switch',
|
||||
value: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should migrate switch variable with array true value', () => {
|
||||
const variable: SwitchVariableModel = {
|
||||
id: 'switch2',
|
||||
global: false,
|
||||
index: 0,
|
||||
state: LoadingState.Done,
|
||||
error: null,
|
||||
name: 'switchVar3',
|
||||
type: 'switch',
|
||||
rootStateKey: 'N4XLmH5Vz',
|
||||
current: {
|
||||
selected: true,
|
||||
text: ['true'],
|
||||
value: ['true'],
|
||||
},
|
||||
hide: 0,
|
||||
skipUrlSync: false,
|
||||
options: [],
|
||||
query: '',
|
||||
multi: false,
|
||||
includeAll: false,
|
||||
allValue: null,
|
||||
};
|
||||
|
||||
const migrated = createSceneVariableFromVariableModel(variable);
|
||||
const { key, ...rest } = migrated.state;
|
||||
|
||||
expect(migrated).toBeInstanceOf(SwitchVariable);
|
||||
expect(rest).toEqual({
|
||||
description: undefined,
|
||||
hide: 0,
|
||||
label: undefined,
|
||||
name: 'switchVar3',
|
||||
skipUrlSync: false,
|
||||
type: 'switch',
|
||||
value: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should migrate switch variable with array false value', () => {
|
||||
const variable: SwitchVariableModel = {
|
||||
id: 'switch3',
|
||||
global: false,
|
||||
index: 0,
|
||||
state: LoadingState.Done,
|
||||
error: null,
|
||||
name: 'switchVar4',
|
||||
type: 'switch',
|
||||
rootStateKey: 'N4XLmH5Vz',
|
||||
current: {
|
||||
selected: true,
|
||||
text: ['false'],
|
||||
value: ['false'],
|
||||
},
|
||||
hide: 0,
|
||||
skipUrlSync: false,
|
||||
options: [],
|
||||
query: '',
|
||||
multi: false,
|
||||
includeAll: false,
|
||||
allValue: null,
|
||||
};
|
||||
|
||||
const migrated = createSceneVariableFromVariableModel(variable);
|
||||
const { key, ...rest } = migrated.state;
|
||||
|
||||
expect(migrated).toBeInstanceOf(SwitchVariable);
|
||||
expect(rest).toEqual({
|
||||
description: undefined,
|
||||
hide: 0,
|
||||
label: undefined,
|
||||
name: 'switchVar4',
|
||||
skipUrlSync: false,
|
||||
type: 'switch',
|
||||
value: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should migrate switch variable with boolean true value', () => {
|
||||
const variable: SwitchVariableModel = {
|
||||
id: 'switch4',
|
||||
global: false,
|
||||
index: 0,
|
||||
state: LoadingState.Done,
|
||||
error: null,
|
||||
name: 'switchVar5',
|
||||
type: 'switch',
|
||||
rootStateKey: 'N4XLmH5Vz',
|
||||
current: {
|
||||
selected: true,
|
||||
text: true,
|
||||
value: true,
|
||||
},
|
||||
hide: 0,
|
||||
skipUrlSync: false,
|
||||
options: [],
|
||||
query: '',
|
||||
multi: false,
|
||||
includeAll: false,
|
||||
allValue: null,
|
||||
};
|
||||
|
||||
const migrated = createSceneVariableFromVariableModel(variable);
|
||||
const { key, ...rest } = migrated.state;
|
||||
|
||||
expect(migrated).toBeInstanceOf(SwitchVariable);
|
||||
expect(rest).toEqual({
|
||||
description: undefined,
|
||||
hide: 0,
|
||||
label: undefined,
|
||||
name: 'switchVar5',
|
||||
skipUrlSync: false,
|
||||
type: 'switch',
|
||||
value: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should migrate switch variable with boolean false value', () => {
|
||||
const variable: SwitchVariableModel = {
|
||||
id: 'switch5',
|
||||
global: false,
|
||||
index: 0,
|
||||
state: LoadingState.Done,
|
||||
error: null,
|
||||
name: 'switchVar6',
|
||||
type: 'switch',
|
||||
rootStateKey: 'N4XLmH5Vz',
|
||||
current: {
|
||||
selected: true,
|
||||
text: false,
|
||||
value: false,
|
||||
},
|
||||
hide: 0,
|
||||
skipUrlSync: false,
|
||||
options: [],
|
||||
query: '',
|
||||
multi: false,
|
||||
includeAll: false,
|
||||
allValue: null,
|
||||
};
|
||||
|
||||
const migrated = createSceneVariableFromVariableModel(variable);
|
||||
const { key, ...rest } = migrated.state;
|
||||
|
||||
expect(migrated).toBeInstanceOf(SwitchVariable);
|
||||
expect(rest).toEqual({
|
||||
description: undefined,
|
||||
hide: 0,
|
||||
label: undefined,
|
||||
name: 'switchVar6',
|
||||
skipUrlSync: false,
|
||||
type: 'switch',
|
||||
value: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should migrate switch variable with no current value', () => {
|
||||
const variable: SwitchVariableModel = {
|
||||
id: 'switch6',
|
||||
global: false,
|
||||
index: 0,
|
||||
state: LoadingState.Done,
|
||||
error: null,
|
||||
name: 'switchVar7',
|
||||
type: 'switch',
|
||||
rootStateKey: 'N4XLmH5Vz',
|
||||
current: undefined,
|
||||
hide: 0,
|
||||
skipUrlSync: false,
|
||||
options: [],
|
||||
query: '',
|
||||
multi: false,
|
||||
includeAll: false,
|
||||
allValue: null,
|
||||
};
|
||||
|
||||
const migrated = createSceneVariableFromVariableModel(variable);
|
||||
const { key, ...rest } = migrated.state;
|
||||
|
||||
expect(migrated).toBeInstanceOf(SwitchVariable);
|
||||
expect(rest).toEqual({
|
||||
description: undefined,
|
||||
hide: 0,
|
||||
label: undefined,
|
||||
name: 'switchVar7',
|
||||
skipUrlSync: false,
|
||||
type: 'switch',
|
||||
value: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should migrate switch variable with array containing non-true value', () => {
|
||||
const variable: SwitchVariableModel = {
|
||||
id: 'switch7',
|
||||
global: false,
|
||||
index: 0,
|
||||
state: LoadingState.Done,
|
||||
error: null,
|
||||
name: 'switchVar8',
|
||||
type: 'switch',
|
||||
rootStateKey: 'N4XLmH5Vz',
|
||||
current: {
|
||||
selected: true,
|
||||
text: ['something'],
|
||||
value: ['something'],
|
||||
},
|
||||
hide: 0,
|
||||
skipUrlSync: false,
|
||||
options: [],
|
||||
query: '',
|
||||
multi: false,
|
||||
includeAll: false,
|
||||
allValue: null,
|
||||
};
|
||||
|
||||
const migrated = createSceneVariableFromVariableModel(variable);
|
||||
const { key, ...rest } = migrated.state;
|
||||
|
||||
expect(migrated).toBeInstanceOf(SwitchVariable);
|
||||
expect(rest).toEqual({
|
||||
description: undefined,
|
||||
hide: 0,
|
||||
label: undefined,
|
||||
name: 'switchVar8',
|
||||
skipUrlSync: false,
|
||||
type: 'switch',
|
||||
value: false,
|
||||
});
|
||||
});
|
||||
|
||||
it.each(['system'])('should throw for unsupported (yet) variables', (type) => {
|
||||
const variable = {
|
||||
name: 'query0',
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
SceneVariable,
|
||||
SceneVariableSet,
|
||||
ScopesVariable,
|
||||
SwitchVariable,
|
||||
TextBoxVariable,
|
||||
} from '@grafana/scenes';
|
||||
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
|
||||
|
@ -156,6 +157,7 @@ export function createSceneVariableFromVariableModel(variable: TypedVariableMode
|
|||
),
|
||||
});
|
||||
}
|
||||
// Custom variable
|
||||
if (variable.type === 'custom') {
|
||||
return new CustomVariable({
|
||||
...commonProperties,
|
||||
|
@ -171,6 +173,7 @@ export function createSceneVariableFromVariableModel(variable: TypedVariableMode
|
|||
hide: variable.hide,
|
||||
allowCustomValue: variable.allowCustomValue,
|
||||
});
|
||||
// Query variable
|
||||
} else if (variable.type === 'query') {
|
||||
return new QueryVariable({
|
||||
...commonProperties,
|
||||
|
@ -196,6 +199,7 @@ export function createSceneVariableFromVariableModel(variable: TypedVariableMode
|
|||
})),
|
||||
staticOptionsOrder: variable.staticOptionsOrder,
|
||||
});
|
||||
// Datasource variable
|
||||
} else if (variable.type === 'datasource') {
|
||||
return new DataSourceVariable({
|
||||
...commonProperties,
|
||||
|
@ -212,6 +216,7 @@ export function createSceneVariableFromVariableModel(variable: TypedVariableMode
|
|||
defaultOptionEnabled: variable.current?.value === DEFAULT_DATASOURCE && variable.current?.text === 'default',
|
||||
allowCustomValue: variable.allowCustomValue,
|
||||
});
|
||||
// Interval variable
|
||||
} else if (variable.type === 'interval') {
|
||||
const intervals = getIntervalsFromQueryString(variable.query);
|
||||
const currentInterval = getCurrentValueForOldIntervalModel(variable, intervals);
|
||||
|
@ -226,6 +231,7 @@ export function createSceneVariableFromVariableModel(variable: TypedVariableMode
|
|||
skipUrlSync: variable.skipUrlSync,
|
||||
hide: variable.hide,
|
||||
});
|
||||
// Constant variable
|
||||
} else if (variable.type === 'constant') {
|
||||
return new ConstantVariable({
|
||||
...commonProperties,
|
||||
|
@ -233,6 +239,7 @@ export function createSceneVariableFromVariableModel(variable: TypedVariableMode
|
|||
skipUrlSync: variable.skipUrlSync,
|
||||
hide: variable.hide,
|
||||
});
|
||||
// Textbox variable
|
||||
} else if (variable.type === 'textbox') {
|
||||
let val;
|
||||
if (!variable?.current?.value) {
|
||||
|
@ -251,6 +258,7 @@ export function createSceneVariableFromVariableModel(variable: TypedVariableMode
|
|||
skipUrlSync: variable.skipUrlSync,
|
||||
hide: variable.hide,
|
||||
});
|
||||
// Groupby variable
|
||||
} else if (config.featureToggles.groupByVariable && variable.type === 'groupby') {
|
||||
return new GroupByVariable({
|
||||
...commonProperties,
|
||||
|
@ -264,6 +272,25 @@ export function createSceneVariableFromVariableModel(variable: TypedVariableMode
|
|||
defaultValue: variable.defaultValue,
|
||||
allowCustomValue: variable.allowCustomValue,
|
||||
});
|
||||
// Switch variable
|
||||
// In the old variable model we are storing the enabled and disabled values in the options:
|
||||
// the first option is the enabled value and the second is the disabled value
|
||||
} else if (variable.type === 'switch') {
|
||||
const pickFirstValue = (value: string | string[]) => {
|
||||
if (Array.isArray(value)) {
|
||||
return value[0];
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
return new SwitchVariable({
|
||||
...commonProperties,
|
||||
value: pickFirstValue(variable.current?.value),
|
||||
enabledValue: pickFirstValue(variable.options?.[0]?.value),
|
||||
disabledValue: pickFirstValue(variable.options?.[1]?.value),
|
||||
skipUrlSync: variable.skipUrlSync,
|
||||
hide: variable.hide,
|
||||
});
|
||||
} else {
|
||||
throw new Error(`Scenes: Unsupported variable type ${variable.type}`);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue