Dashboard: Migration - EditVariable Settings: Implement Constant Variable (#80743)

Co-authored-by: Ivan Ortega <ivanortegaalba@gmail.com>
This commit is contained in:
Alexa V 2024-01-18 11:04:29 +01:00 committed by GitHub
parent 80395e43d8
commit 4b113f87f9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 187 additions and 52 deletions

View File

@ -43,6 +43,7 @@ export function VariableEditorForm({ variable, onTypeChange, onGoBack, onDiscard
const onDescriptionBlur = (e: FormEvent<HTMLTextAreaElement>) =>
variable.setState({ description: e.currentTarget.value });
const onHideChange = (hide: VariableHide) => variable.setState({ hide });
const isHasVariableOptions = hasVariableOptions(variable);
return (
<>
@ -80,7 +81,7 @@ export function VariableEditorForm({ variable, onTypeChange, onGoBack, onDiscard
{EditorToRender && <EditorToRender variable={variable} onRunQuery={onRunQuery} />}
{hasVariableOptions(variable) && <VariableValuesPreview options={variable.getOptionsForSelect()} />}
{isHasVariableOptions && <VariableValuesPreview options={variable.getOptionsForSelect()} />}
<div style={{ marginTop: '16px' }}>
<HorizontalGroup spacing="md" height="inherit">
@ -94,14 +95,17 @@ export function VariableEditorForm({ variable, onTypeChange, onGoBack, onDiscard
>
Back to list
</Button>
<Button
disabled={runQueryState.loading}
variant="secondary"
data-testid={selectors.pages.Dashboard.Settings.Variables.Edit.General.submitButton}
onClick={onRunQuery}
>
{runQueryState.loading ? <LoadingPlaceholder text="Running query..." /> : `Run query`}
</Button>
{isHasVariableOptions && (
<Button
disabled={runQueryState.loading}
variant="secondary"
data-testid={selectors.pages.Dashboard.Settings.Variables.Edit.General.submitButton}
onClick={onRunQuery}
>
{runQueryState.loading ? <LoadingPlaceholder text="Running query..." /> : `Run query`}
</Button>
)}
<Button
variant="destructive"
data-testid={selectors.pages.Dashboard.Settings.Variables.Edit.General.applyButton}

View File

@ -5,9 +5,10 @@ import { Draggable } from 'react-beautiful-dnd';
import { GrafanaTheme2 } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
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 { hasOptions } from 'app/features/variables/guard';
import { getDefinition } from './utils';
export interface VariableEditorListRowProps {
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) {
return {
dragHandle: css({

View File

@ -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}
/>
</>
);
}

View File

@ -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 };
}

View File

@ -2,11 +2,18 @@ import React from 'react';
import { ConstantVariable } from '@grafana/scenes';
import { ConstantVariableForm } from '../components/ConstantVariableForm';
interface ConstantVariableEditorProps {
variable: ConstantVariable;
onChange: (variable: ConstantVariable) => void;
}
export function ConstantVariableEditor(props: ConstantVariableEditorProps) {
return <div>ConstantVariableEditor</div>;
export function ConstantVariableEditor({ variable }: ConstantVariableEditorProps) {
const { value } = variable.useState();
const onConstantValueChange = (event: React.FormEvent<HTMLInputElement>) => {
variable.setState({ value: event.currentTarget.value });
};
return <ConstantVariableForm constantValue={String(value)} onChange={onConstantValueChange} />;
}

View File

@ -26,6 +26,7 @@ import {
getVariableScene,
hasVariableOptions,
EditableVariableType,
getDefinition,
} from './utils';
const templateSrv = {
@ -138,3 +139,64 @@ describe('hasVariableOptions', () => {
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');
});
});

View File

@ -12,6 +12,8 @@ import {
} from '@grafana/scenes';
import { VariableType } from '@grafana/schema';
import { getIntervalsQueryFromNewIntervalModel } from '../../utils/utils';
import { AdHocFiltersVariableEditor } from './editors/AdHocFiltersVariableEditor';
import { ConstantVariableEditor } from './editors/ConstantVariableEditor';
import { CustomVariableEditor } from './editors/CustomVariableEditor';
@ -120,3 +122,21 @@ export function getVariableScene(type: EditableVariableType, initialState: Commo
export function hasVariableOptions(variable: SceneVariable): variable is MultiValueVariable {
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;
}

View File

@ -1,9 +1,7 @@
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 { ConstantVariableModel } from '../types';
@ -11,13 +9,6 @@ export interface Props extends VariableEditorProps<ConstantVariableModel> {}
export class ConstantVariableEditor extends PureComponent<Props> {
onChange = (event: FormEvent<HTMLInputElement>) => {
this.props.onPropChange({
propName: 'query',
propValue: event.currentTarget.value,
});
};
onBlur = (event: FormEvent<HTMLInputElement>) => {
this.props.onPropChange({
propName: 'query',
propValue: event.currentTarget.value,
@ -26,19 +17,6 @@ export class ConstantVariableEditor extends PureComponent<Props> {
};
render() {
return (
<>
<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}
/>
</>
);
return <ConstantVariableForm constantValue={this.props.variable.query} onChange={this.onChange} />;
}
}