mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboard: Migration - EditVariable Settings: Implement Constant Variable (#80743)
Co-authored-by: Ivan Ortega <ivanortegaalba@gmail.com>
This commit is contained in:
parent
80395e43d8
commit
4b113f87f9
@ -43,6 +43,7 @@ export function VariableEditorForm({ variable, onTypeChange, onGoBack, onDiscard
|
|||||||
const onDescriptionBlur = (e: FormEvent<HTMLTextAreaElement>) =>
|
const onDescriptionBlur = (e: FormEvent<HTMLTextAreaElement>) =>
|
||||||
variable.setState({ description: e.currentTarget.value });
|
variable.setState({ description: e.currentTarget.value });
|
||||||
const onHideChange = (hide: VariableHide) => variable.setState({ hide });
|
const onHideChange = (hide: VariableHide) => variable.setState({ hide });
|
||||||
|
const isHasVariableOptions = hasVariableOptions(variable);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -80,7 +81,7 @@ export function VariableEditorForm({ variable, onTypeChange, onGoBack, onDiscard
|
|||||||
|
|
||||||
{EditorToRender && <EditorToRender variable={variable} onRunQuery={onRunQuery} />}
|
{EditorToRender && <EditorToRender variable={variable} onRunQuery={onRunQuery} />}
|
||||||
|
|
||||||
{hasVariableOptions(variable) && <VariableValuesPreview options={variable.getOptionsForSelect()} />}
|
{isHasVariableOptions && <VariableValuesPreview options={variable.getOptionsForSelect()} />}
|
||||||
|
|
||||||
<div style={{ marginTop: '16px' }}>
|
<div style={{ marginTop: '16px' }}>
|
||||||
<HorizontalGroup spacing="md" height="inherit">
|
<HorizontalGroup spacing="md" height="inherit">
|
||||||
@ -94,14 +95,17 @@ export function VariableEditorForm({ variable, onTypeChange, onGoBack, onDiscard
|
|||||||
>
|
>
|
||||||
Back to list
|
Back to list
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
|
||||||
disabled={runQueryState.loading}
|
{isHasVariableOptions && (
|
||||||
variant="secondary"
|
<Button
|
||||||
data-testid={selectors.pages.Dashboard.Settings.Variables.Edit.General.submitButton}
|
disabled={runQueryState.loading}
|
||||||
onClick={onRunQuery}
|
variant="secondary"
|
||||||
>
|
data-testid={selectors.pages.Dashboard.Settings.Variables.Edit.General.submitButton}
|
||||||
{runQueryState.loading ? <LoadingPlaceholder text="Running query..." /> : `Run query`}
|
onClick={onRunQuery}
|
||||||
</Button>
|
>
|
||||||
|
{runQueryState.loading ? <LoadingPlaceholder text="Running query..." /> : `Run query`}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
data-testid={selectors.pages.Dashboard.Settings.Variables.Edit.General.applyButton}
|
data-testid={selectors.pages.Dashboard.Settings.Variables.Edit.General.applyButton}
|
||||||
|
@ -5,9 +5,10 @@ import { Draggable } from 'react-beautiful-dnd';
|
|||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { reportInteraction } from '@grafana/runtime';
|
import { reportInteraction } from '@grafana/runtime';
|
||||||
import { QueryVariable, SceneVariable } from '@grafana/scenes';
|
import { SceneVariable } from '@grafana/scenes';
|
||||||
import { Button, ConfirmModal, Icon, IconButton, useStyles2, useTheme2 } from '@grafana/ui';
|
import { Button, ConfirmModal, Icon, IconButton, useStyles2, useTheme2 } from '@grafana/ui';
|
||||||
import { hasOptions } from 'app/features/variables/guard';
|
|
||||||
|
import { getDefinition } from './utils';
|
||||||
|
|
||||||
export interface VariableEditorListRowProps {
|
export interface VariableEditorListRowProps {
|
||||||
index: number;
|
index: number;
|
||||||
@ -121,20 +122,6 @@ export function VariableEditorListRow({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDefinition(model: SceneVariable): string {
|
|
||||||
let definition = '';
|
|
||||||
if (model instanceof QueryVariable) {
|
|
||||||
if (model.state.definition) {
|
|
||||||
definition = model.state.definition;
|
|
||||||
} else if (typeof model.state.query === 'string') {
|
|
||||||
definition = model.state.query;
|
|
||||||
}
|
|
||||||
} else if (hasOptions(model.state)) {
|
|
||||||
definition = model.state.query;
|
|
||||||
}
|
|
||||||
return definition;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStyles(theme: GrafanaTheme2) {
|
function getStyles(theme: GrafanaTheme2) {
|
||||||
return {
|
return {
|
||||||
dragHandle: css({
|
dragHandle: css({
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
import React, { FormEvent } from 'react';
|
||||||
|
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
|
||||||
|
import { VariableLegend } from './VariableLegend';
|
||||||
|
import { VariableTextField } from './VariableTextField';
|
||||||
|
|
||||||
|
interface ConstantVariableFormProps {
|
||||||
|
constantValue: string;
|
||||||
|
onChange: (event: FormEvent<HTMLInputElement>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ConstantVariableForm({ onChange, constantValue }: ConstantVariableFormProps) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<VariableLegend>Constant options</VariableLegend>
|
||||||
|
<VariableTextField
|
||||||
|
defaultValue={constantValue}
|
||||||
|
name="Value"
|
||||||
|
placeholder="your metric prefix"
|
||||||
|
onBlur={onChange}
|
||||||
|
testId={selectors.pages.Dashboard.Settings.Variables.Edit.ConstantVariable.constantOptionsQueryInputV2}
|
||||||
|
width={30}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { ConstantVariable } from '@grafana/scenes';
|
||||||
|
|
||||||
|
import { ConstantVariableEditor } from './ConstantVariableEditor';
|
||||||
|
|
||||||
|
describe('ConstantVariableEditor', () => {
|
||||||
|
let constantVar: ConstantVariable;
|
||||||
|
beforeEach(async () => {
|
||||||
|
const result = await buildTestScene();
|
||||||
|
constantVar = result.constantVar;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders constant value', () => {
|
||||||
|
render(<ConstantVariableEditor variable={constantVar} />);
|
||||||
|
const input = screen.getByRole('textbox', { name: 'Value' });
|
||||||
|
expect(input).toBeInTheDocument();
|
||||||
|
expect(input).toHaveValue('constant value');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('changes the value', async () => {
|
||||||
|
render(<ConstantVariableEditor variable={constantVar} />);
|
||||||
|
|
||||||
|
const input = screen.getByRole('textbox', { name: 'Value' });
|
||||||
|
expect(input).toBeInTheDocument();
|
||||||
|
expect(input).toHaveValue('constant value');
|
||||||
|
|
||||||
|
// change input value
|
||||||
|
const newValue = 'new constant value';
|
||||||
|
await userEvent.clear(input);
|
||||||
|
await userEvent.type(input, newValue);
|
||||||
|
|
||||||
|
expect(input).toHaveValue(newValue);
|
||||||
|
|
||||||
|
await userEvent.tab();
|
||||||
|
expect(constantVar.state.value).toBe(newValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
async function buildTestScene() {
|
||||||
|
const constantVar = new ConstantVariable({
|
||||||
|
name: 'constantVar',
|
||||||
|
type: 'constant',
|
||||||
|
value: 'constant value',
|
||||||
|
});
|
||||||
|
|
||||||
|
return { constantVar };
|
||||||
|
}
|
@ -2,11 +2,18 @@ import React from 'react';
|
|||||||
|
|
||||||
import { ConstantVariable } from '@grafana/scenes';
|
import { ConstantVariable } from '@grafana/scenes';
|
||||||
|
|
||||||
|
import { ConstantVariableForm } from '../components/ConstantVariableForm';
|
||||||
|
|
||||||
interface ConstantVariableEditorProps {
|
interface ConstantVariableEditorProps {
|
||||||
variable: ConstantVariable;
|
variable: ConstantVariable;
|
||||||
onChange: (variable: ConstantVariable) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ConstantVariableEditor(props: ConstantVariableEditorProps) {
|
export function ConstantVariableEditor({ variable }: ConstantVariableEditorProps) {
|
||||||
return <div>ConstantVariableEditor</div>;
|
const { value } = variable.useState();
|
||||||
|
|
||||||
|
const onConstantValueChange = (event: React.FormEvent<HTMLInputElement>) => {
|
||||||
|
variable.setState({ value: event.currentTarget.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
return <ConstantVariableForm constantValue={String(value)} onChange={onConstantValueChange} />;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import {
|
|||||||
getVariableScene,
|
getVariableScene,
|
||||||
hasVariableOptions,
|
hasVariableOptions,
|
||||||
EditableVariableType,
|
EditableVariableType,
|
||||||
|
getDefinition,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
const templateSrv = {
|
const templateSrv = {
|
||||||
@ -138,3 +139,64 @@ describe('hasVariableOptions', () => {
|
|||||||
expect(hasVariableOptions(variableWithoutOptions)).toBe(false);
|
expect(hasVariableOptions(variableWithoutOptions)).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getDefinition', () => {
|
||||||
|
it('returns the correct definition for QueryVariable when definition is defined', () => {
|
||||||
|
const model = new QueryVariable({
|
||||||
|
name: 'custom0',
|
||||||
|
query: '',
|
||||||
|
definition: 'legacy ABC query definition',
|
||||||
|
});
|
||||||
|
expect(getDefinition(model)).toBe('legacy ABC query definition');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the correct definition for QueryVariable when definition is not defined', () => {
|
||||||
|
const model = new QueryVariable({
|
||||||
|
name: 'custom0',
|
||||||
|
query: 'ABC query',
|
||||||
|
definition: '',
|
||||||
|
});
|
||||||
|
expect(getDefinition(model)).toBe('ABC query');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the correct definition for DataSourceVariable', () => {
|
||||||
|
const model = new DataSourceVariable({
|
||||||
|
name: 'ds0',
|
||||||
|
pluginId: 'datasource-plugin',
|
||||||
|
value: 'datasource-value',
|
||||||
|
});
|
||||||
|
expect(getDefinition(model)).toBe('datasource-plugin');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the correct definition for CustomVariable', () => {
|
||||||
|
const model = new CustomVariable({
|
||||||
|
name: 'custom0',
|
||||||
|
query: 'Custom, A, B, C',
|
||||||
|
});
|
||||||
|
expect(getDefinition(model)).toBe('Custom, A, B, C');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the correct definition for IntervalVariable', () => {
|
||||||
|
const model = new IntervalVariable({
|
||||||
|
name: 'interval0',
|
||||||
|
intervals: ['1m', '5m', '15m', '30m', '1h', '6h', '12h', '1d'],
|
||||||
|
});
|
||||||
|
expect(getDefinition(model)).toBe('1m,5m,15m,30m,1h,6h,12h,1d');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the correct definition for TextBoxVariable', () => {
|
||||||
|
const model = new TextBoxVariable({
|
||||||
|
name: 'textbox0',
|
||||||
|
value: 'TextBox Value',
|
||||||
|
});
|
||||||
|
expect(getDefinition(model)).toBe('TextBox Value');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the correct definition for ConstantVariable', () => {
|
||||||
|
const model = new ConstantVariable({
|
||||||
|
name: 'constant0',
|
||||||
|
value: 'Constant Value',
|
||||||
|
});
|
||||||
|
expect(getDefinition(model)).toBe('Constant Value');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -12,6 +12,8 @@ import {
|
|||||||
} from '@grafana/scenes';
|
} from '@grafana/scenes';
|
||||||
import { VariableType } from '@grafana/schema';
|
import { VariableType } from '@grafana/schema';
|
||||||
|
|
||||||
|
import { getIntervalsQueryFromNewIntervalModel } from '../../utils/utils';
|
||||||
|
|
||||||
import { AdHocFiltersVariableEditor } from './editors/AdHocFiltersVariableEditor';
|
import { AdHocFiltersVariableEditor } from './editors/AdHocFiltersVariableEditor';
|
||||||
import { ConstantVariableEditor } from './editors/ConstantVariableEditor';
|
import { ConstantVariableEditor } from './editors/ConstantVariableEditor';
|
||||||
import { CustomVariableEditor } from './editors/CustomVariableEditor';
|
import { CustomVariableEditor } from './editors/CustomVariableEditor';
|
||||||
@ -120,3 +122,21 @@ export function getVariableScene(type: EditableVariableType, initialState: Commo
|
|||||||
export function hasVariableOptions(variable: SceneVariable): variable is MultiValueVariable {
|
export function hasVariableOptions(variable: SceneVariable): variable is MultiValueVariable {
|
||||||
return 'options' in variable.state;
|
return 'options' in variable.state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getDefinition(model: SceneVariable): string {
|
||||||
|
let definition = '';
|
||||||
|
|
||||||
|
if (model instanceof QueryVariable) {
|
||||||
|
definition = model.state.definition || (typeof model.state.query === 'string' ? model.state.query : '');
|
||||||
|
} else if (model instanceof DataSourceVariable) {
|
||||||
|
definition = String(model.state.pluginId);
|
||||||
|
} else if (model instanceof CustomVariable) {
|
||||||
|
definition = model.state.query;
|
||||||
|
} else if (model instanceof IntervalVariable) {
|
||||||
|
definition = getIntervalsQueryFromNewIntervalModel(model.state.intervals);
|
||||||
|
} else if (model instanceof TextBoxVariable || model instanceof ConstantVariable) {
|
||||||
|
definition = String(model.state.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return definition;
|
||||||
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import React, { FormEvent, PureComponent } from 'react';
|
import React, { FormEvent, PureComponent } from 'react';
|
||||||
|
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { ConstantVariableForm } from 'app/features/dashboard-scene/settings/variables/components/ConstantVariableForm';
|
||||||
|
|
||||||
import { VariableLegend } from '../../dashboard-scene/settings/variables/components/VariableLegend';
|
|
||||||
import { VariableTextField } from '../../dashboard-scene/settings/variables/components/VariableTextField';
|
|
||||||
import { VariableEditorProps } from '../editor/types';
|
import { VariableEditorProps } from '../editor/types';
|
||||||
import { ConstantVariableModel } from '../types';
|
import { ConstantVariableModel } from '../types';
|
||||||
|
|
||||||
@ -11,13 +9,6 @@ export interface Props extends VariableEditorProps<ConstantVariableModel> {}
|
|||||||
|
|
||||||
export class ConstantVariableEditor extends PureComponent<Props> {
|
export class ConstantVariableEditor extends PureComponent<Props> {
|
||||||
onChange = (event: FormEvent<HTMLInputElement>) => {
|
onChange = (event: FormEvent<HTMLInputElement>) => {
|
||||||
this.props.onPropChange({
|
|
||||||
propName: 'query',
|
|
||||||
propValue: event.currentTarget.value,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onBlur = (event: FormEvent<HTMLInputElement>) => {
|
|
||||||
this.props.onPropChange({
|
this.props.onPropChange({
|
||||||
propName: 'query',
|
propName: 'query',
|
||||||
propValue: event.currentTarget.value,
|
propValue: event.currentTarget.value,
|
||||||
@ -26,19 +17,6 @@ export class ConstantVariableEditor extends PureComponent<Props> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return <ConstantVariableForm constantValue={this.props.variable.query} onChange={this.onChange} />;
|
||||||
<>
|
|
||||||
<VariableLegend>Constant options</VariableLegend>
|
|
||||||
<VariableTextField
|
|
||||||
value={this.props.variable.query}
|
|
||||||
name="Value"
|
|
||||||
placeholder="your metric prefix"
|
|
||||||
onChange={this.onChange}
|
|
||||||
onBlur={this.onBlur}
|
|
||||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.ConstantVariable.constantOptionsQueryInputV2}
|
|
||||||
width={30}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user