mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Scene: Variables and All value support (#59635)
* Working on the all value * Support for custom allValue * Fixes * More progress * Progress * Updated * Fixed issue with multi and All value * Clarified tests
This commit is contained in:
parent
d2f9d7f39b
commit
ef46761b9a
@ -9,7 +9,7 @@ import { getGridWithRowLayoutTest } from './gridWithRow';
|
||||
import { getNestedScene } from './nested';
|
||||
import { getQueryVariableDemo } from './queryVariableDemo';
|
||||
import { getSceneWithRows } from './sceneWithRows';
|
||||
import { getVariablesDemo } from './variablesDemo';
|
||||
import { getVariablesDemo, getVariablesDemoWithAll } from './variablesDemo';
|
||||
|
||||
interface SceneDef {
|
||||
title: string;
|
||||
@ -27,6 +27,7 @@ export function getScenes(): SceneDef[] {
|
||||
{ title: 'Grid with rows and different queries and time ranges', getScene: getGridWithMultipleTimeRanges },
|
||||
{ title: 'Multiple grid layouts test', getScene: getMultipleGridLayoutTest },
|
||||
{ title: 'Variables', getScene: getVariablesDemo },
|
||||
{ title: 'Variables with All values', getScene: getVariablesDemoWithAll },
|
||||
{ title: 'Query variable', getScene: getQueryVariableDemo },
|
||||
];
|
||||
}
|
||||
|
@ -85,3 +85,74 @@ export function getVariablesDemo(standalone: boolean): Scene {
|
||||
|
||||
return standalone ? new Scene(state) : new EmbeddedScene(state);
|
||||
}
|
||||
|
||||
export function getVariablesDemoWithAll(): Scene {
|
||||
const scene = new Scene({
|
||||
title: 'Variables with All values',
|
||||
$variables: new SceneVariableSet({
|
||||
variables: [
|
||||
new TestVariable({
|
||||
name: 'server',
|
||||
query: 'A.*',
|
||||
value: 'AA',
|
||||
text: 'AA',
|
||||
includeAll: true,
|
||||
defaultToAll: true,
|
||||
delayMs: 1000,
|
||||
options: [],
|
||||
}),
|
||||
new TestVariable({
|
||||
name: 'pod',
|
||||
query: 'A.$server.*',
|
||||
value: [],
|
||||
delayMs: 1000,
|
||||
isMulti: true,
|
||||
includeAll: true,
|
||||
defaultToAll: true,
|
||||
text: '',
|
||||
options: [],
|
||||
}),
|
||||
new TestVariable({
|
||||
name: 'handler',
|
||||
query: 'A.$server.$pod.*',
|
||||
value: [],
|
||||
delayMs: 1000,
|
||||
includeAll: true,
|
||||
defaultToAll: false,
|
||||
isMulti: true,
|
||||
text: '',
|
||||
options: [],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
layout: new SceneFlexLayout({
|
||||
direction: 'row',
|
||||
children: [
|
||||
new SceneFlexLayout({
|
||||
children: [
|
||||
new VizPanel({
|
||||
pluginId: 'timeseries',
|
||||
title: 'handler: $handler',
|
||||
$data: getQueryRunnerWithRandomWalkQuery({
|
||||
alias: 'handler: $handler',
|
||||
}),
|
||||
}),
|
||||
new SceneCanvasText({
|
||||
size: { width: '40%' },
|
||||
text: 'server: ${server} pod:${pod}',
|
||||
fontSize: 20,
|
||||
align: 'center',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
$timeRange: new SceneTimeRange(),
|
||||
actions: [new SceneTimePicker({})],
|
||||
subMenu: new SceneSubMenu({
|
||||
children: [new VariableValueSelectors({})],
|
||||
}),
|
||||
});
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
@ -1,34 +1,13 @@
|
||||
import { isArray } from 'lodash';
|
||||
import React from 'react';
|
||||
|
||||
import { Select, MultiSelect } from '@grafana/ui';
|
||||
import { MultiSelect, Select } from '@grafana/ui';
|
||||
|
||||
import { SceneComponentProps } from '../../core/types';
|
||||
import { MultiValueVariable } from '../variants/MultiValueVariable';
|
||||
|
||||
export function VariableValueSelect({ model }: SceneComponentProps<MultiValueVariable>) {
|
||||
const { value, key, loading, isMulti, options } = model.useState();
|
||||
|
||||
if (isMulti) {
|
||||
return (
|
||||
<MultiSelect
|
||||
id={key}
|
||||
placeholder="Select value"
|
||||
width="auto"
|
||||
value={isArray(value) ? value : [value]}
|
||||
allowCustomValue
|
||||
isLoading={loading}
|
||||
options={options}
|
||||
closeMenuOnSelect={false}
|
||||
onChange={(newValue) => {
|
||||
model.changeValueTo(
|
||||
newValue.map((v) => v.value!),
|
||||
newValue.map((v) => v.label!)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
const { value, key, loading } = model.useState();
|
||||
|
||||
return (
|
||||
<Select
|
||||
@ -37,11 +16,47 @@ export function VariableValueSelect({ model }: SceneComponentProps<MultiValueVar
|
||||
width="auto"
|
||||
value={value}
|
||||
allowCustomValue
|
||||
tabSelectsValue={false}
|
||||
isLoading={loading}
|
||||
options={options}
|
||||
options={model.getOptionsForSelect()}
|
||||
onChange={(newValue) => {
|
||||
model.changeValueTo(newValue.value!, newValue.label!);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function VariableValueSelectMulti({ model }: SceneComponentProps<MultiValueVariable>) {
|
||||
const { value, key, loading } = model.useState();
|
||||
const arrayValue = isArray(value) ? value : [value];
|
||||
|
||||
return (
|
||||
<MultiSelect
|
||||
id={key}
|
||||
placeholder="Select value"
|
||||
width="auto"
|
||||
value={arrayValue}
|
||||
tabSelectsValue={false}
|
||||
allowCustomValue
|
||||
isLoading={loading}
|
||||
options={model.getOptionsForSelect()}
|
||||
closeMenuOnSelect={false}
|
||||
isClearable={true}
|
||||
onOpenMenu={() => {}}
|
||||
onChange={(newValue) => {
|
||||
model.changeValueTo(
|
||||
newValue.map((v) => v.value!),
|
||||
newValue.map((v) => v.label!)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function renderSelectForVariable(model: MultiValueVariable) {
|
||||
if (model.state.isMulti) {
|
||||
return <VariableValueSelectMulti model={model} />;
|
||||
} else {
|
||||
return <VariableValueSelect model={model} />;
|
||||
}
|
||||
}
|
||||
|
@ -326,6 +326,10 @@ function luceneEscape(value: string) {
|
||||
* unicode handling uses UTF-8 as in ECMA-262.
|
||||
*/
|
||||
function encodeURIComponentStrict(str: VariableValueSingle) {
|
||||
if (typeof str === 'object') {
|
||||
str = String(str);
|
||||
}
|
||||
|
||||
return encodeURIComponent(str).replace(/[!'()*]/g, (c) => {
|
||||
return '%' + c.charCodeAt(0).toString(16).toUpperCase();
|
||||
});
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from 'app/features/variables/constants';
|
||||
|
||||
import { SceneObjectBase } from '../../core/SceneObjectBase';
|
||||
import { SceneObjectStatePlain } from '../../core/types';
|
||||
import { SceneVariableSet } from '../sets/SceneVariableSet';
|
||||
@ -45,6 +47,25 @@ describe('sceneInterpolator', () => {
|
||||
expect(sceneInterpolator(scene.state.nested!, '${atRootOnly}')).toBe('RootValue');
|
||||
});
|
||||
|
||||
describe('Given a variable with allValue', () => {
|
||||
it('Should not escape it', () => {
|
||||
const scene = new TestScene({
|
||||
$variables: new SceneVariableSet({
|
||||
variables: [
|
||||
new TestVariable({
|
||||
name: 'test',
|
||||
value: ALL_VARIABLE_VALUE,
|
||||
text: ALL_VARIABLE_TEXT,
|
||||
allValue: '.*',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
expect(sceneInterpolator(scene, '${test:regex}')).toBe('.*');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Given an expression with fieldPath', () => {
|
||||
it('Should interpolate correctly', () => {
|
||||
const scene = new TestScene({
|
||||
|
@ -87,6 +87,12 @@ function formatValue(
|
||||
return '';
|
||||
}
|
||||
|
||||
// Special handling for custom values that should not be formatted / escaped
|
||||
// This is used by the custom allValue that usually contain wildcards and therefore should not be escaped
|
||||
if (typeof value === 'object' && 'isCustomValue' in value && formatNameOrFn !== FormatRegistryID.text) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
// if (isAdHoc(variable) && format !== FormatRegistryID.queryParam) {
|
||||
// return '';
|
||||
// }
|
||||
|
@ -38,12 +38,21 @@ export interface SceneVariable<TState extends SceneVariableState = SceneVariable
|
||||
|
||||
export type VariableValue = VariableValueSingle | VariableValueSingle[];
|
||||
|
||||
export type VariableValueSingle = string | boolean | number;
|
||||
export type VariableValueSingle = string | boolean | number | VariableValueCustom;
|
||||
|
||||
/**
|
||||
* This is for edge case values like the custom "allValue" that should not be escaped/formatted like other values.
|
||||
* The custom all value usually contain wildcards that should not be escaped.
|
||||
*/
|
||||
export interface VariableValueCustom {
|
||||
isCustomValue: true;
|
||||
toString(): string;
|
||||
}
|
||||
|
||||
export interface ValidateAndUpdateResult {}
|
||||
export interface VariableValueOption {
|
||||
label: string;
|
||||
value: string;
|
||||
value: VariableValueSingle;
|
||||
}
|
||||
|
||||
export interface SceneVariableSetState extends SceneObjectStatePlain {
|
||||
|
@ -1,9 +1,8 @@
|
||||
import React from 'react';
|
||||
import { Observable, of } from 'rxjs';
|
||||
|
||||
import { SceneComponentProps } from '../../core/types';
|
||||
import { VariableDependencyConfig } from '../VariableDependencyConfig';
|
||||
import { VariableValueSelect } from '../components/VariableValueSelect';
|
||||
import { renderSelectForVariable } from '../components/VariableValueSelect';
|
||||
import { VariableValueOption } from '../types';
|
||||
|
||||
import { MultiValueVariable, MultiValueVariableState, VariableGetOptionsArgs } from './MultiValueVariable';
|
||||
@ -43,14 +42,9 @@ export class CustomVariable extends MultiValueVariable<CustomVariableState> {
|
||||
});
|
||||
|
||||
return of(options);
|
||||
|
||||
// TODO: Support 'All'
|
||||
//if (this.state.includeAll) {
|
||||
// options.unshift({ text: ALL_VARIABLE_TEXT, value: ALL_VARIABLE_VALUE, selected: false });
|
||||
//}
|
||||
}
|
||||
|
||||
public static Component = ({ model }: SceneComponentProps<MultiValueVariable>) => {
|
||||
return <VariableValueSelect model={model} />;
|
||||
return renderSelectForVariable(model);
|
||||
};
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import React from 'react';
|
||||
import { Observable, of } from 'rxjs';
|
||||
|
||||
import { stringToJsRegex, DataSourceInstanceSettings } from '@grafana/data';
|
||||
@ -7,7 +6,7 @@ import { getDataSourceSrv } from '@grafana/runtime';
|
||||
import { sceneGraph } from '../../core/sceneGraph';
|
||||
import { SceneComponentProps } from '../../core/types';
|
||||
import { VariableDependencyConfig } from '../VariableDependencyConfig';
|
||||
import { VariableValueSelect } from '../components/VariableValueSelect';
|
||||
import { renderSelectForVariable } from '../components/VariableValueSelect';
|
||||
import { VariableValueOption } from '../types';
|
||||
|
||||
import { MultiValueVariable, MultiValueVariableState, VariableGetOptionsArgs } from './MultiValueVariable';
|
||||
@ -70,11 +69,6 @@ export class DataSourceVariable extends MultiValueVariable<DataSourceVariableSta
|
||||
options.push({ label: 'No data sources found', value: '' });
|
||||
}
|
||||
|
||||
// TODO: Add support for include All
|
||||
// if (instanceState.includeAll) {
|
||||
// options.unshift({ label: ALL_VARIABLE_TEXT, value: ALL_VARIABLE_VALUE });
|
||||
//}
|
||||
|
||||
return of(options);
|
||||
}
|
||||
|
||||
@ -83,7 +77,7 @@ export class DataSourceVariable extends MultiValueVariable<DataSourceVariableSta
|
||||
}
|
||||
|
||||
public static Component = ({ model }: SceneComponentProps<MultiValueVariable>) => {
|
||||
return <VariableValueSelect model={model} />;
|
||||
return renderSelectForVariable(model);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { lastValueFrom, Observable, of } from 'rxjs';
|
||||
|
||||
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from 'app/features/variables/constants';
|
||||
|
||||
import { SceneVariableValueChangedEvent, VariableValueOption } from '../types';
|
||||
import { SceneVariableValueChangedEvent, VariableValueCustom, VariableValueOption } from '../types';
|
||||
import { MultiValueVariable, MultiValueVariableState, VariableGetOptionsArgs } from '../variants/MultiValueVariable';
|
||||
|
||||
export interface ExampleVariableState extends MultiValueVariableState {
|
||||
@ -46,6 +46,22 @@ describe('MultiValueVariable', () => {
|
||||
expect(variable.state.text).toBe('B');
|
||||
});
|
||||
|
||||
it('Should pick All value when defaultToAll is true', async () => {
|
||||
const variable = new ExampleVariable({
|
||||
name: 'test',
|
||||
options: [],
|
||||
optionsToReturn: [
|
||||
{ label: 'B', value: 'B' },
|
||||
{ label: 'C', value: 'C' },
|
||||
],
|
||||
defaultToAll: true,
|
||||
});
|
||||
|
||||
await lastValueFrom(variable.validateAndUpdate());
|
||||
|
||||
expect(variable.state.value).toBe(ALL_VARIABLE_VALUE);
|
||||
});
|
||||
|
||||
it('Should keep current value if current value is valid', async () => {
|
||||
const variable = new ExampleVariable({
|
||||
name: 'test',
|
||||
@ -99,6 +115,26 @@ describe('MultiValueVariable', () => {
|
||||
expect(variable.state.text).toEqual(['A']);
|
||||
});
|
||||
|
||||
it('Should select All option if none of the current values are valid', async () => {
|
||||
const variable = new ExampleVariable({
|
||||
name: 'test',
|
||||
options: [],
|
||||
isMulti: true,
|
||||
defaultToAll: true,
|
||||
optionsToReturn: [
|
||||
{ label: 'A', value: 'A' },
|
||||
{ label: 'C', value: 'C' },
|
||||
],
|
||||
value: ['D', 'E'],
|
||||
text: ['E', 'E'],
|
||||
});
|
||||
|
||||
await lastValueFrom(variable.validateAndUpdate());
|
||||
|
||||
expect(variable.state.value).toEqual([ALL_VARIABLE_VALUE]);
|
||||
expect(variable.state.text).toEqual([ALL_VARIABLE_TEXT]);
|
||||
});
|
||||
|
||||
it('Should handle $__all value and send change event even when value is still $__all', async () => {
|
||||
const variable = new ExampleVariable({
|
||||
name: 'test',
|
||||
@ -123,6 +159,60 @@ describe('MultiValueVariable', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('changeValueTo', () => {
|
||||
it('Should set default empty state to all value if defaultToAll multi', async () => {
|
||||
const variable = new ExampleVariable({
|
||||
name: 'test',
|
||||
options: [],
|
||||
isMulti: true,
|
||||
defaultToAll: true,
|
||||
optionsToReturn: [],
|
||||
value: ['1'],
|
||||
text: ['A'],
|
||||
});
|
||||
|
||||
variable.changeValueTo([]);
|
||||
|
||||
expect(variable.state.value).toEqual([ALL_VARIABLE_VALUE]);
|
||||
});
|
||||
|
||||
it('When changing to all value', async () => {
|
||||
const variable = new ExampleVariable({
|
||||
name: 'test',
|
||||
options: [
|
||||
{ label: 'A', value: '1' },
|
||||
{ label: 'B', value: '2' },
|
||||
],
|
||||
isMulti: true,
|
||||
defaultToAll: true,
|
||||
optionsToReturn: [],
|
||||
value: ['1'],
|
||||
text: ['A'],
|
||||
});
|
||||
|
||||
variable.changeValueTo(['1', ALL_VARIABLE_VALUE]);
|
||||
// Should clear the value so only all value is set
|
||||
expect(variable.state.value).toEqual([ALL_VARIABLE_VALUE]);
|
||||
});
|
||||
|
||||
it('When changing from all value', async () => {
|
||||
const variable = new ExampleVariable({
|
||||
name: 'test',
|
||||
options: [
|
||||
{ label: 'A', value: '1' },
|
||||
{ label: 'B', value: '2' },
|
||||
],
|
||||
isMulti: true,
|
||||
defaultToAll: true,
|
||||
optionsToReturn: [],
|
||||
});
|
||||
|
||||
variable.changeValueTo([ALL_VARIABLE_VALUE, '1']);
|
||||
// Should remove the all value so only the new value is present
|
||||
expect(variable.state.value).toEqual(['1']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getValue and getValueText', () => {
|
||||
it('GetValueText should return text', async () => {
|
||||
const variable = new ExampleVariable({
|
||||
@ -163,6 +253,63 @@ describe('MultiValueVariable', () => {
|
||||
|
||||
expect(variable.getValue()).toEqual(['1', '2']);
|
||||
});
|
||||
|
||||
it('GetValue should return allValue when value is $__all', async () => {
|
||||
const variable = new ExampleVariable({
|
||||
name: 'test',
|
||||
options: [],
|
||||
optionsToReturn: [],
|
||||
value: ALL_VARIABLE_VALUE,
|
||||
allValue: '.*',
|
||||
text: 'A',
|
||||
});
|
||||
|
||||
const value = variable.getValue() as VariableValueCustom;
|
||||
expect(value.isCustomValue).toBe(true);
|
||||
expect(value.toString()).toBe('.*');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getOptionsForSelect', () => {
|
||||
it('Should return options', async () => {
|
||||
const variable = new ExampleVariable({
|
||||
name: 'test',
|
||||
options: [{ label: 'A', value: '1' }],
|
||||
optionsToReturn: [],
|
||||
value: '1',
|
||||
text: 'A',
|
||||
});
|
||||
|
||||
expect(variable.getOptionsForSelect()).toEqual([{ label: 'A', value: '1' }]);
|
||||
});
|
||||
|
||||
it('Should return include All option when includeAll is true', async () => {
|
||||
const variable = new ExampleVariable({
|
||||
name: 'test',
|
||||
options: [{ label: 'A', value: '1' }],
|
||||
optionsToReturn: [],
|
||||
includeAll: true,
|
||||
value: '1',
|
||||
text: 'A',
|
||||
});
|
||||
|
||||
expect(variable.getOptionsForSelect()).toEqual([
|
||||
{ label: ALL_VARIABLE_TEXT, value: ALL_VARIABLE_VALUE },
|
||||
{ label: 'A', value: '1' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('Should add current value if not found', async () => {
|
||||
const variable = new ExampleVariable({
|
||||
name: 'test',
|
||||
options: [],
|
||||
optionsToReturn: [],
|
||||
value: '1',
|
||||
text: 'A',
|
||||
});
|
||||
|
||||
expect(variable.getOptionsForSelect()).toEqual([{ label: 'A', value: '1' }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Url syncing', () => {
|
||||
|
@ -12,6 +12,7 @@ import {
|
||||
ValidateAndUpdateResult,
|
||||
VariableValue,
|
||||
VariableValueOption,
|
||||
VariableValueCustom,
|
||||
VariableValueSingle,
|
||||
} from '../types';
|
||||
|
||||
@ -20,6 +21,10 @@ export interface MultiValueVariableState extends SceneVariableState {
|
||||
text: VariableValue; // old current.value
|
||||
options: VariableValueOption[];
|
||||
isMulti?: boolean;
|
||||
includeAll?: boolean;
|
||||
defaultToAll?: boolean;
|
||||
allValue?: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
export interface VariableGetOptionsArgs {
|
||||
@ -71,8 +76,9 @@ export abstract class MultiValueVariable<TState extends MultiValueVariableState
|
||||
|
||||
// If no valid values pick the first option
|
||||
if (validValues.length === 0) {
|
||||
stateUpdate.value = [options[0].value];
|
||||
stateUpdate.text = [options[0].label];
|
||||
const defaultState = this.getDefaultMultiState(options);
|
||||
stateUpdate.value = defaultState.value;
|
||||
stateUpdate.text = defaultState.text;
|
||||
}
|
||||
// We have valid values, if it's different from current valid values update current values
|
||||
else if (!isEqual(validValues, this.state.value)) {
|
||||
@ -84,9 +90,14 @@ export abstract class MultiValueVariable<TState extends MultiValueVariableState
|
||||
// Single valued variable
|
||||
const foundCurrent = options.find((x) => x.value === this.state.value);
|
||||
if (!foundCurrent) {
|
||||
// Current value is not valid. Set to first of the available options
|
||||
stateUpdate.value = options[0].value;
|
||||
stateUpdate.text = options[0].label;
|
||||
if (this.state.defaultToAll) {
|
||||
stateUpdate.value = ALL_VARIABLE_VALUE;
|
||||
stateUpdate.text = ALL_VARIABLE_TEXT;
|
||||
} else {
|
||||
// Current value is not valid. Set to first of the available options
|
||||
stateUpdate.value = options[0].value;
|
||||
stateUpdate.text = options[0].label;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,6 +115,10 @@ export abstract class MultiValueVariable<TState extends MultiValueVariableState
|
||||
|
||||
public getValue(): VariableValue {
|
||||
if (this.hasAllValue()) {
|
||||
if (this.state.allValue) {
|
||||
return new CustomAllValue(this.state.allValue);
|
||||
}
|
||||
|
||||
return this.state.options.map((x) => x.value);
|
||||
}
|
||||
|
||||
@ -127,22 +142,55 @@ export abstract class MultiValueVariable<TState extends MultiValueVariableState
|
||||
return value === ALL_VARIABLE_VALUE || (Array.isArray(value) && value[0] === ALL_VARIABLE_VALUE);
|
||||
}
|
||||
|
||||
private getDefaultMultiState(options: VariableValueOption[]) {
|
||||
if (this.state.defaultToAll) {
|
||||
return { value: [ALL_VARIABLE_VALUE], text: [ALL_VARIABLE_TEXT] };
|
||||
} else {
|
||||
return { value: [options[0].value], text: [options[0].label] };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the value and publish SceneVariableValueChangedEvent event
|
||||
*/
|
||||
public changeValueTo(value: VariableValue, text?: VariableValue) {
|
||||
if (value !== this.state.value || text !== this.state.text) {
|
||||
if (!text) {
|
||||
if (Array.isArray(value)) {
|
||||
text = value.map((v) => this.findLabelTextForValue(v));
|
||||
} else {
|
||||
text = this.findLabelTextForValue(value);
|
||||
}
|
||||
// Igore if there is no change
|
||||
if (value === this.state.value && text === this.state.text) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!text) {
|
||||
if (Array.isArray(value)) {
|
||||
text = value.map((v) => this.findLabelTextForValue(v));
|
||||
} else {
|
||||
text = this.findLabelTextForValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
// If we are a multi valued variable is cleared (empty array) we need to set the default empty state
|
||||
if (value.length === 0) {
|
||||
const state = this.getDefaultMultiState(this.state.options);
|
||||
value = state.value;
|
||||
text = state.text;
|
||||
}
|
||||
|
||||
this.setStateHelper({ value, text, loading: false });
|
||||
this.publishEvent(new SceneVariableValueChangedEvent(this), true);
|
||||
// If last value is the All value then replace all with it
|
||||
if (value[value.length - 1] === ALL_VARIABLE_VALUE) {
|
||||
value = [ALL_VARIABLE_VALUE];
|
||||
text = [ALL_VARIABLE_TEXT];
|
||||
}
|
||||
// If the first value is the ALL value and we have other values, then remove the All value
|
||||
else if (value[0] === ALL_VARIABLE_VALUE && value.length > 1) {
|
||||
value.shift();
|
||||
if (Array.isArray(text)) {
|
||||
text.shift();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setStateHelper({ value, text, loading: false });
|
||||
this.publishEvent(new SceneVariableValueChangedEvent(this), true);
|
||||
}
|
||||
|
||||
private findLabelTextForValue(value: VariableValueSingle): VariableValueSingle {
|
||||
@ -166,6 +214,36 @@ export abstract class MultiValueVariable<TState extends MultiValueVariableState
|
||||
const test: SceneObject<MultiValueVariableState> = this;
|
||||
test.setState(state);
|
||||
}
|
||||
|
||||
public getOptionsForSelect(): VariableValueOption[] {
|
||||
let options = this.state.options;
|
||||
|
||||
if (this.state.includeAll) {
|
||||
options = [{ value: ALL_VARIABLE_VALUE, label: ALL_VARIABLE_TEXT }, ...options];
|
||||
}
|
||||
|
||||
if (!Array.isArray(this.state.value)) {
|
||||
const current = options.find((x) => x.value === this.state.value);
|
||||
if (!current) {
|
||||
options = [{ value: this.state.value, label: String(this.state.text) }, ...options];
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The custom allValue needs a special wrapping / handling to make it not be formatted / escaped like normal values
|
||||
*/
|
||||
class CustomAllValue implements VariableValueCustom {
|
||||
public isCustomValue: true = true;
|
||||
|
||||
public constructor(private _value: string) {}
|
||||
|
||||
public toString() {
|
||||
return this._value;
|
||||
}
|
||||
}
|
||||
|
||||
export class MultiValueUrlSyncHandler<TState extends MultiValueVariableState = MultiValueVariableState>
|
||||
|
@ -1,4 +1,3 @@
|
||||
import React from 'react';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
|
||||
import { queryMetricTree } from 'app/plugins/datasource/testdata/metricTree';
|
||||
@ -6,7 +5,7 @@ import { queryMetricTree } from 'app/plugins/datasource/testdata/metricTree';
|
||||
import { sceneGraph } from '../../core/sceneGraph';
|
||||
import { SceneComponentProps } from '../../core/types';
|
||||
import { VariableDependencyConfig } from '../VariableDependencyConfig';
|
||||
import { VariableValueSelect } from '../components/VariableValueSelect';
|
||||
import { renderSelectForVariable } from '../components/VariableValueSelect';
|
||||
import { VariableValueOption } from '../types';
|
||||
|
||||
import { MultiValueVariable, MultiValueVariableState, VariableGetOptionsArgs } from './MultiValueVariable';
|
||||
@ -89,6 +88,6 @@ export class TestVariable extends MultiValueVariable<TestVariableState> {
|
||||
}
|
||||
|
||||
public static Component = ({ model }: SceneComponentProps<MultiValueVariable>) => {
|
||||
return <VariableValueSelect model={model} />;
|
||||
return renderSelectForVariable(model);
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user