mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
CustomVariable: Be able to edit them using scenes (#80193)
--------- Co-authored-by: Alexandra Vargas <alexa1866@gmail.com>
This commit is contained in:
parent
c9211fbd69
commit
3b401d0d4d
@ -4392,8 +4392,7 @@ exports[`better eslint`] = {
|
|||||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
[0, 0, 0, "Do not use any type assertions.", "1"]
|
||||||
],
|
],
|
||||||
"public/app/features/variables/editor/VariableEditorEditor.tsx:5381": [
|
"public/app/features/variables/editor/VariableEditorEditor.tsx:5381": [
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||||
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "1"]
|
|
||||||
],
|
],
|
||||||
"public/app/features/variables/editor/VariableEditorList.tsx:5381": [
|
"public/app/features/variables/editor/VariableEditorList.tsx:5381": [
|
||||||
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"],
|
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"],
|
||||||
|
@ -69,7 +69,7 @@ describe('Variables - Query - Add variable', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption().should('not.exist');
|
e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption().should('not.exist');
|
||||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInputV2().should('not.exist');
|
e2e.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput().should('not.exist');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('adding a single value query variable', () => {
|
it('adding a single value query variable', () => {
|
||||||
@ -152,7 +152,7 @@ describe('Variables - Query - Add variable', () => {
|
|||||||
cy.get('input[type="checkbox"]').click({ force: true }).should('be.checked');
|
cy.get('input[type="checkbox"]').click({ force: true }).should('be.checked');
|
||||||
});
|
});
|
||||||
|
|
||||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInputV2().within((input) => {
|
e2e.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput().within((input) => {
|
||||||
expect(input.attr('placeholder')).equals('blank = auto');
|
expect(input.attr('placeholder')).equals('blank = auto');
|
||||||
expect(input.val()).equals('');
|
expect(input.val()).equals('');
|
||||||
});
|
});
|
||||||
|
@ -137,12 +137,11 @@ export const Pages = {
|
|||||||
generalLabelInputV2: 'data-testid Variable editor Form Label field',
|
generalLabelInputV2: 'data-testid Variable editor Form Label field',
|
||||||
generalHideSelect: 'Variable editor Form Hide select',
|
generalHideSelect: 'Variable editor Form Hide select',
|
||||||
generalHideSelectV2: 'data-testid Variable editor Form Hide select',
|
generalHideSelectV2: 'data-testid Variable editor Form Hide select',
|
||||||
selectionOptionsMultiSwitch: 'Variable editor Form Multi switch',
|
selectionOptionsMultiSwitch: 'data-testid Variable editor Form Multi switch',
|
||||||
selectionOptionsIncludeAllSwitch: 'Variable editor Form IncludeAll switch',
|
selectionOptionsIncludeAllSwitch: 'data-testid Variable editor Form IncludeAll switch',
|
||||||
selectionOptionsCustomAllInput: 'Variable editor Form IncludeAll field',
|
selectionOptionsCustomAllInput: 'data-testid Variable editor Form IncludeAll field',
|
||||||
selectionOptionsCustomAllInputV2: 'data-testid Variable editor Form IncludeAll field',
|
|
||||||
previewOfValuesOption: 'data-testid Variable editor Preview of Values option',
|
previewOfValuesOption: 'data-testid Variable editor Preview of Values option',
|
||||||
submitButton: 'Variable editor Submit button',
|
submitButton: 'data-testid Variable editor Run Query button',
|
||||||
applyButton: 'data-testid Variable editor Apply button',
|
applyButton: 'data-testid Variable editor Apply button',
|
||||||
},
|
},
|
||||||
QueryVariable: {
|
QueryVariable: {
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import React from 'react';
|
import React, { FormEvent } from 'react';
|
||||||
|
import { useAsyncFn } from 'react-use';
|
||||||
|
import { lastValueFrom } from 'rxjs';
|
||||||
|
|
||||||
import { SelectableValue } from '@grafana/data';
|
import { SelectableValue } from '@grafana/data';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { SceneVariable } from '@grafana/scenes';
|
import { SceneVariable } from '@grafana/scenes';
|
||||||
import { VariableHide, defaultVariableModel } from '@grafana/schema';
|
import { VariableHide, defaultVariableModel } from '@grafana/schema';
|
||||||
import { HorizontalGroup, Button } from '@grafana/ui';
|
import { HorizontalGroup, Button, LoadingPlaceholder } from '@grafana/ui';
|
||||||
import { VariableHideSelect } from 'app/features/dashboard-scene/settings/variables/components/VariableHideSelect';
|
import { VariableHideSelect } from 'app/features/dashboard-scene/settings/variables/components/VariableHideSelect';
|
||||||
import { VariableLegend } from 'app/features/dashboard-scene/settings/variables/components/VariableLegend';
|
import { VariableLegend } from 'app/features/dashboard-scene/settings/variables/components/VariableLegend';
|
||||||
import { VariableTextAreaField } from 'app/features/dashboard-scene/settings/variables/components/VariableTextAreaField';
|
import { VariableTextAreaField } from 'app/features/dashboard-scene/settings/variables/components/VariableTextAreaField';
|
||||||
@ -24,11 +26,11 @@ interface VariableEditorFormProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function VariableEditorForm({ variable, onTypeChange, onGoBack, onDiscardChanges }: VariableEditorFormProps) {
|
export function VariableEditorForm({ variable, onTypeChange, onGoBack, onDiscardChanges }: VariableEditorFormProps) {
|
||||||
const { name: initialName, type, label: initialLabel, description: initialDescription, hide } = variable.useState();
|
const { name, type, label, description, hide } = variable.useState();
|
||||||
const EditorToRender = isEditableVariableType(type) ? getVariableEditor(type) : undefined;
|
const EditorToRender = isEditableVariableType(type) ? getVariableEditor(type) : undefined;
|
||||||
const [name, setName] = React.useState(initialName ?? '');
|
const [runQueryState, onRunQuery] = useAsyncFn(async () => {
|
||||||
const [label, setLabel] = React.useState(initialLabel ?? '');
|
await lastValueFrom(variable.validateAndUpdate!());
|
||||||
const [description, setDescription] = React.useState(initialDescription ?? '');
|
}, [variable]);
|
||||||
|
|
||||||
const onVariableTypeChange = (option: SelectableValue<EditableVariableType>) => {
|
const onVariableTypeChange = (option: SelectableValue<EditableVariableType>) => {
|
||||||
if (option.value) {
|
if (option.value) {
|
||||||
@ -36,13 +38,10 @@ export function VariableEditorForm({ variable, onTypeChange, onGoBack, onDiscard
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onNameChange = (e: React.FormEvent<HTMLInputElement>) => setName(e.currentTarget.value);
|
const onNameBlur = (e: FormEvent<HTMLInputElement>) => variable.setState({ name: e.currentTarget.value });
|
||||||
const onLabelChange = (e: React.FormEvent<HTMLInputElement>) => setLabel(e.currentTarget.value);
|
const onLabelBlur = (e: FormEvent<HTMLInputElement>) => variable.setState({ label: e.currentTarget.value });
|
||||||
const onDescriptionChange = (e: React.FormEvent<HTMLTextAreaElement>) => setDescription(e.currentTarget.value);
|
const onDescriptionBlur = (e: FormEvent<HTMLTextAreaElement>) =>
|
||||||
|
variable.setState({ description: e.currentTarget.value });
|
||||||
const onNameBlur = () => variable.setState({ name });
|
|
||||||
const onLabelBlur = () => variable.setState({ label });
|
|
||||||
const onDescriptionBlur = () => variable.setState({ description });
|
|
||||||
const onHideChange = (hide: VariableHide) => variable.setState({ hide });
|
const onHideChange = (hide: VariableHide) => variable.setState({ hide });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -52,12 +51,11 @@ export function VariableEditorForm({ variable, onTypeChange, onGoBack, onDiscard
|
|||||||
|
|
||||||
<VariableLegend>General</VariableLegend>
|
<VariableLegend>General</VariableLegend>
|
||||||
<VariableTextField
|
<VariableTextField
|
||||||
value={name}
|
|
||||||
onBlur={onNameBlur}
|
|
||||||
onChange={onNameChange}
|
|
||||||
name="Name"
|
name="Name"
|
||||||
placeholder="Variable name"
|
|
||||||
description="The name of the template variable. (Max. 50 characters)"
|
description="The name of the template variable. (Max. 50 characters)"
|
||||||
|
placeholder="Variable name"
|
||||||
|
defaultValue={name ?? ''}
|
||||||
|
onBlur={onNameBlur}
|
||||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2}
|
testId={selectors.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2}
|
||||||
maxLength={VariableNameConstraints.MaxSize}
|
maxLength={VariableNameConstraints.MaxSize}
|
||||||
required
|
required
|
||||||
@ -65,16 +63,14 @@ export function VariableEditorForm({ variable, onTypeChange, onGoBack, onDiscard
|
|||||||
<VariableTextField
|
<VariableTextField
|
||||||
name="Label"
|
name="Label"
|
||||||
description="Optional display name"
|
description="Optional display name"
|
||||||
value={label}
|
|
||||||
onChange={onLabelChange}
|
|
||||||
placeholder="Label name"
|
placeholder="Label name"
|
||||||
|
defaultValue={label ?? ''}
|
||||||
onBlur={onLabelBlur}
|
onBlur={onLabelBlur}
|
||||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2}
|
testId={selectors.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2}
|
||||||
/>
|
/>
|
||||||
<VariableTextAreaField
|
<VariableTextAreaField
|
||||||
name="Description"
|
name="Description"
|
||||||
value={description}
|
defaultValue={description ?? ''}
|
||||||
onChange={onDescriptionChange}
|
|
||||||
placeholder="Descriptive text"
|
placeholder="Descriptive text"
|
||||||
onBlur={onDescriptionBlur}
|
onBlur={onDescriptionBlur}
|
||||||
width={52}
|
width={52}
|
||||||
@ -82,24 +78,15 @@ export function VariableEditorForm({ variable, onTypeChange, onGoBack, onDiscard
|
|||||||
|
|
||||||
<VariableHideSelect onChange={onHideChange} hide={hide || defaultVariableModel.hide!} type={type} />
|
<VariableHideSelect onChange={onHideChange} hide={hide || defaultVariableModel.hide!} type={type} />
|
||||||
|
|
||||||
{EditorToRender && <EditorToRender variable={variable} />}
|
{EditorToRender && <EditorToRender variable={variable} onRunQuery={onRunQuery} />}
|
||||||
|
|
||||||
{hasVariableOptions(variable) && <VariableValuesPreview options={variable.state.options} />}
|
{hasVariableOptions(variable) && <VariableValuesPreview options={variable.getOptionsForSelect()} />}
|
||||||
|
|
||||||
<div style={{ marginTop: '16px' }}>
|
<div style={{ marginTop: '16px' }}>
|
||||||
<HorizontalGroup spacing="md" height="inherit">
|
<HorizontalGroup spacing="md" height="inherit">
|
||||||
{/* <Button variant="destructive" fill="outline" onClick={onModalOpen}>
|
{/* <Button variant="destructive" fill="outline" onClick={onModalOpen}>
|
||||||
Delete
|
Delete
|
||||||
</Button> */}
|
</Button> */}
|
||||||
{/* <Button
|
|
||||||
type="submit"
|
|
||||||
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.General.submitButton}
|
|
||||||
disabled={loading}
|
|
||||||
variant="secondary"
|
|
||||||
>
|
|
||||||
Run query
|
|
||||||
{loading && <Icon className="spin-clockwise" name="sync" size="sm" style={{ marginLeft: '2px' }} />}
|
|
||||||
</Button> */}
|
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
data-testid={selectors.pages.Dashboard.Settings.Variables.Edit.General.applyButton}
|
data-testid={selectors.pages.Dashboard.Settings.Variables.Edit.General.applyButton}
|
||||||
@ -107,6 +94,14 @@ export function VariableEditorForm({ variable, onTypeChange, onGoBack, onDiscard
|
|||||||
>
|
>
|
||||||
Back to list
|
Back to list
|
||||||
</Button>
|
</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>
|
||||||
<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}
|
||||||
|
@ -0,0 +1,116 @@
|
|||||||
|
import { render, fireEvent } from '@testing-library/react';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
|
||||||
|
import { CustomVariableForm } from './CustomVariableForm';
|
||||||
|
|
||||||
|
describe('CustomVariableForm', () => {
|
||||||
|
const onQueryChange = jest.fn();
|
||||||
|
const onMultiChange = jest.fn();
|
||||||
|
const onIncludeAllChange = jest.fn();
|
||||||
|
const onAllValueChange = jest.fn();
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the form fields correctly', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<CustomVariableForm
|
||||||
|
query="query"
|
||||||
|
multi={true}
|
||||||
|
allValue="custom value"
|
||||||
|
includeAll={true}
|
||||||
|
onQueryChange={onQueryChange}
|
||||||
|
onMultiChange={onMultiChange}
|
||||||
|
onIncludeAllChange={onIncludeAllChange}
|
||||||
|
onAllValueChange={onAllValueChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const queryInput = getByTestId(selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput);
|
||||||
|
const multiCheckbox = getByTestId(
|
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsMultiSwitch
|
||||||
|
);
|
||||||
|
const includeAllCheckbox = getByTestId(
|
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsIncludeAllSwitch
|
||||||
|
);
|
||||||
|
const allValueInput = getByTestId(
|
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(queryInput).toBeInTheDocument();
|
||||||
|
expect(queryInput).toHaveValue('query');
|
||||||
|
expect(multiCheckbox).toBeInTheDocument();
|
||||||
|
expect(multiCheckbox).toBeChecked();
|
||||||
|
expect(includeAllCheckbox).toBeInTheDocument();
|
||||||
|
expect(includeAllCheckbox).toBeChecked();
|
||||||
|
expect(allValueInput).toBeInTheDocument();
|
||||||
|
expect(allValueInput).toHaveValue('custom value');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call the correct event handlers on input change', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<CustomVariableForm
|
||||||
|
query=""
|
||||||
|
multi={true}
|
||||||
|
allValue=""
|
||||||
|
includeAll={true}
|
||||||
|
onQueryChange={onQueryChange}
|
||||||
|
onMultiChange={onMultiChange}
|
||||||
|
onIncludeAllChange={onIncludeAllChange}
|
||||||
|
onAllValueChange={onAllValueChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const queryInput = getByTestId(selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput);
|
||||||
|
const multiCheckbox = getByTestId(
|
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsMultiSwitch
|
||||||
|
);
|
||||||
|
const includeAllCheckbox = getByTestId(
|
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsIncludeAllSwitch
|
||||||
|
);
|
||||||
|
const allValueInput = getByTestId(
|
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput
|
||||||
|
);
|
||||||
|
|
||||||
|
fireEvent.click(multiCheckbox);
|
||||||
|
fireEvent.click(includeAllCheckbox);
|
||||||
|
fireEvent.change(queryInput, { currentTarget: { value: 'test query' } });
|
||||||
|
fireEvent.change(allValueInput, { currentTarget: { value: 'test value' } });
|
||||||
|
|
||||||
|
expect(onMultiChange).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onIncludeAllChange).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onQueryChange).not.toHaveBeenCalledTimes(1);
|
||||||
|
expect(onAllValueChange).not.toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call the correct event handlers on input blur', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<CustomVariableForm
|
||||||
|
query="query value"
|
||||||
|
multi={true}
|
||||||
|
allValue="custom all value"
|
||||||
|
includeAll={true}
|
||||||
|
onQueryChange={onQueryChange}
|
||||||
|
onMultiChange={onMultiChange}
|
||||||
|
onIncludeAllChange={onIncludeAllChange}
|
||||||
|
onAllValueChange={onAllValueChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const queryInput = getByTestId(selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput);
|
||||||
|
const allValueInput = getByTestId(
|
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput
|
||||||
|
);
|
||||||
|
|
||||||
|
fireEvent.blur(queryInput);
|
||||||
|
fireEvent.blur(allValueInput);
|
||||||
|
|
||||||
|
expect(onQueryChange).toHaveBeenCalled();
|
||||||
|
expect(onAllValueChange).toHaveBeenCalled();
|
||||||
|
expect(onMultiChange).not.toHaveBeenCalled();
|
||||||
|
expect(onIncludeAllChange).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,57 @@
|
|||||||
|
import React, { FormEvent } from 'react';
|
||||||
|
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
|
||||||
|
import { VariableLegend } from '../components/VariableLegend';
|
||||||
|
import { VariableTextAreaField } from '../components/VariableTextAreaField';
|
||||||
|
|
||||||
|
import { SelectionOptionsForm } from './SelectionOptionsForm';
|
||||||
|
|
||||||
|
interface CustomVariableFormProps {
|
||||||
|
query: string;
|
||||||
|
multi: boolean;
|
||||||
|
allValue?: string | null;
|
||||||
|
includeAll: boolean;
|
||||||
|
onQueryChange: (event: FormEvent<HTMLTextAreaElement>) => void;
|
||||||
|
onMultiChange: (event: FormEvent<HTMLInputElement>) => void;
|
||||||
|
onIncludeAllChange: (event: FormEvent<HTMLInputElement>) => void;
|
||||||
|
onAllValueChange: (event: FormEvent<HTMLInputElement>) => void;
|
||||||
|
onQueryBlur?: (event: FormEvent<HTMLTextAreaElement>) => void;
|
||||||
|
onAllValueBlur?: (event: FormEvent<HTMLInputElement>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CustomVariableForm({
|
||||||
|
query,
|
||||||
|
multi,
|
||||||
|
allValue,
|
||||||
|
includeAll,
|
||||||
|
onQueryChange,
|
||||||
|
onMultiChange,
|
||||||
|
onIncludeAllChange,
|
||||||
|
onAllValueChange,
|
||||||
|
}: CustomVariableFormProps) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<VariableLegend>Custom options</VariableLegend>
|
||||||
|
|
||||||
|
<VariableTextAreaField
|
||||||
|
name="Values separated by comma"
|
||||||
|
defaultValue={query}
|
||||||
|
placeholder="1, 10, mykey : myvalue, myvalue, escaped\,value"
|
||||||
|
onBlur={onQueryChange}
|
||||||
|
required
|
||||||
|
width={52}
|
||||||
|
testId={selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput}
|
||||||
|
/>
|
||||||
|
<VariableLegend>Selection options</VariableLegend>
|
||||||
|
<SelectionOptionsForm
|
||||||
|
multi={multi}
|
||||||
|
includeAll={includeAll}
|
||||||
|
allValue={allValue}
|
||||||
|
onMultiChange={onMultiChange}
|
||||||
|
onIncludeAllChange={onIncludeAllChange}
|
||||||
|
onAllValueChange={onAllValueChange}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
import React, { ChangeEvent, FormEvent } from 'react';
|
||||||
|
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
import { VerticalGroup } from '@grafana/ui';
|
||||||
|
import { VariableCheckboxField } from 'app/features/dashboard-scene/settings/variables/components/VariableCheckboxField';
|
||||||
|
import { VariableTextField } from 'app/features/dashboard-scene/settings/variables/components/VariableTextField';
|
||||||
|
|
||||||
|
interface SelectionOptionsFormProps {
|
||||||
|
multi: boolean;
|
||||||
|
includeAll: boolean;
|
||||||
|
allValue?: string | null;
|
||||||
|
onMultiChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
onIncludeAllChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
onAllValueChange: (event: FormEvent<HTMLInputElement>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SelectionOptionsForm({
|
||||||
|
multi,
|
||||||
|
includeAll,
|
||||||
|
allValue,
|
||||||
|
onMultiChange,
|
||||||
|
onIncludeAllChange,
|
||||||
|
onAllValueChange,
|
||||||
|
}: SelectionOptionsFormProps) {
|
||||||
|
return (
|
||||||
|
<VerticalGroup spacing="md" height="inherit">
|
||||||
|
<VariableCheckboxField
|
||||||
|
value={multi}
|
||||||
|
name="Multi-value"
|
||||||
|
description="Enables multiple values to be selected at the same time"
|
||||||
|
onChange={onMultiChange}
|
||||||
|
testId={selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsMultiSwitch}
|
||||||
|
/>
|
||||||
|
<VariableCheckboxField
|
||||||
|
value={includeAll}
|
||||||
|
name="Include All option"
|
||||||
|
description="Enables an option to include all variables"
|
||||||
|
onChange={onIncludeAllChange}
|
||||||
|
testId={selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsIncludeAllSwitch}
|
||||||
|
/>
|
||||||
|
{includeAll && (
|
||||||
|
<VariableTextField
|
||||||
|
defaultValue={allValue ?? ''}
|
||||||
|
onBlur={onAllValueChange}
|
||||||
|
name="Custom all value"
|
||||||
|
placeholder="blank = auto"
|
||||||
|
testId={selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</VerticalGroup>
|
||||||
|
);
|
||||||
|
}
|
@ -5,17 +5,19 @@ import { VariableLegend } from 'app/features/dashboard-scene/settings/variables/
|
|||||||
import { VariableTextField } from 'app/features/dashboard-scene/settings/variables/components/VariableTextField';
|
import { VariableTextField } from 'app/features/dashboard-scene/settings/variables/components/VariableTextField';
|
||||||
|
|
||||||
interface TextBoxVariableFormProps {
|
interface TextBoxVariableFormProps {
|
||||||
value: string;
|
value?: string;
|
||||||
onChange: (event: FormEvent<HTMLInputElement>) => void;
|
defaultValue?: string;
|
||||||
onBlur: (event: FormEvent<HTMLInputElement>) => void;
|
onChange?: (event: FormEvent<HTMLInputElement>) => void;
|
||||||
|
onBlur?: (event: FormEvent<HTMLInputElement>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TextBoxVariableForm({ onChange, onBlur, value }: TextBoxVariableFormProps) {
|
export function TextBoxVariableForm({ defaultValue, value, onChange, onBlur }: TextBoxVariableFormProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<VariableLegend>Text options</VariableLegend>
|
<VariableLegend>Text options</VariableLegend>
|
||||||
<VariableTextField
|
<VariableTextField
|
||||||
value={value}
|
value={value}
|
||||||
|
defaultValue={defaultValue}
|
||||||
name="Default value"
|
name="Default value"
|
||||||
placeholder="default value, if any"
|
placeholder="default value, if any"
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
@ -3,12 +3,13 @@ import React, { ChangeEvent, PropsWithChildren, ReactElement } from 'react';
|
|||||||
|
|
||||||
import { Checkbox } from '@grafana/ui';
|
import { Checkbox } from '@grafana/ui';
|
||||||
|
|
||||||
interface VariableCheckboxFieldProps {
|
interface VariableCheckboxFieldProps extends React.HTMLAttributes<HTMLInputElement> {
|
||||||
value: boolean;
|
value: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
||||||
description?: string;
|
description?: string;
|
||||||
ariaLabel?: string;
|
ariaLabel?: string;
|
||||||
|
testId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function VariableCheckboxField({
|
export function VariableCheckboxField({
|
||||||
@ -17,6 +18,7 @@ export function VariableCheckboxField({
|
|||||||
description,
|
description,
|
||||||
onChange,
|
onChange,
|
||||||
ariaLabel,
|
ariaLabel,
|
||||||
|
testId,
|
||||||
}: PropsWithChildren<VariableCheckboxFieldProps>): ReactElement {
|
}: PropsWithChildren<VariableCheckboxFieldProps>): ReactElement {
|
||||||
const uniqueId = useId();
|
const uniqueId = useId();
|
||||||
|
|
||||||
@ -28,6 +30,7 @@ export function VariableCheckboxField({
|
|||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
aria-label={ariaLabel}
|
aria-label={ariaLabel}
|
||||||
|
data-testid={testId}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,8 @@ import { Field, TextArea, useStyles2 } from '@grafana/ui';
|
|||||||
|
|
||||||
interface VariableTextAreaFieldProps {
|
interface VariableTextAreaFieldProps {
|
||||||
name: string;
|
name: string;
|
||||||
value: string;
|
value?: string;
|
||||||
|
defaultValue?: string;
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
onChange?: (event: FormEvent<HTMLTextAreaElement>) => void;
|
onChange?: (event: FormEvent<HTMLTextAreaElement>) => void;
|
||||||
width: number;
|
width: number;
|
||||||
@ -20,6 +21,7 @@ interface VariableTextAreaFieldProps {
|
|||||||
|
|
||||||
export function VariableTextAreaField({
|
export function VariableTextAreaField({
|
||||||
value,
|
value,
|
||||||
|
defaultValue,
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
placeholder,
|
placeholder,
|
||||||
@ -39,6 +41,7 @@ export function VariableTextAreaField({
|
|||||||
id={id}
|
id={id}
|
||||||
rows={2}
|
rows={2}
|
||||||
value={value}
|
value={value}
|
||||||
|
defaultValue={defaultValue}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
|
@ -4,7 +4,8 @@ import React, { FormEvent, PropsWithChildren } from 'react';
|
|||||||
import { Field, Input } from '@grafana/ui';
|
import { Field, Input } from '@grafana/ui';
|
||||||
|
|
||||||
interface VariableTextFieldProps {
|
interface VariableTextFieldProps {
|
||||||
value: string;
|
value?: string;
|
||||||
|
defaultValue?: string;
|
||||||
name: string;
|
name: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
onChange?: (event: FormEvent<HTMLInputElement>) => void;
|
onChange?: (event: FormEvent<HTMLInputElement>) => void;
|
||||||
@ -21,6 +22,7 @@ interface VariableTextFieldProps {
|
|||||||
|
|
||||||
export function VariableTextField({
|
export function VariableTextField({
|
||||||
value,
|
value,
|
||||||
|
defaultValue,
|
||||||
name,
|
name,
|
||||||
placeholder = '',
|
placeholder = '',
|
||||||
onChange,
|
onChange,
|
||||||
@ -43,6 +45,7 @@ export function VariableTextField({
|
|||||||
id={id}
|
id={id}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
value={value}
|
value={value}
|
||||||
|
defaultValue={defaultValue}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
width={grow ? undefined : width ?? 30}
|
width={grow ? undefined : width ?? 30}
|
||||||
|
@ -0,0 +1,115 @@
|
|||||||
|
import { render, fireEvent } from '@testing-library/react';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
import { CustomVariable } from '@grafana/scenes';
|
||||||
|
|
||||||
|
import { CustomVariableEditor } from './CustomVariableEditor';
|
||||||
|
|
||||||
|
describe('CustomVariableEditor', () => {
|
||||||
|
it('should render the CustomVariableForm with correct initial values', () => {
|
||||||
|
const variable = new CustomVariable({
|
||||||
|
name: 'customVar',
|
||||||
|
query: 'test, test2',
|
||||||
|
value: 'test',
|
||||||
|
isMulti: true,
|
||||||
|
includeAll: true,
|
||||||
|
allValue: 'test',
|
||||||
|
});
|
||||||
|
const onRunQuery = jest.fn();
|
||||||
|
|
||||||
|
const { getByTestId } = render(<CustomVariableEditor variable={variable} onRunQuery={onRunQuery} />);
|
||||||
|
|
||||||
|
const queryInput = getByTestId(
|
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const allValueInput = getByTestId(
|
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const multiCheckbox = getByTestId(
|
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsMultiSwitch
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const includeAllCheckbox = getByTestId(
|
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsIncludeAllSwitch
|
||||||
|
) as HTMLInputElement;
|
||||||
|
|
||||||
|
expect(queryInput.value).toBe('test, test2');
|
||||||
|
expect(allValueInput.value).toBe('test');
|
||||||
|
expect(multiCheckbox.checked).toBe(true);
|
||||||
|
expect(includeAllCheckbox.checked).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update the variable state when input values change', () => {
|
||||||
|
const variable = new CustomVariable({
|
||||||
|
name: 'customVar',
|
||||||
|
query: 'test, test2',
|
||||||
|
value: 'test',
|
||||||
|
});
|
||||||
|
const onRunQuery = jest.fn();
|
||||||
|
|
||||||
|
const { getByTestId } = render(<CustomVariableEditor variable={variable} onRunQuery={onRunQuery} />);
|
||||||
|
|
||||||
|
const multiCheckbox = getByTestId(
|
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsMultiSwitch
|
||||||
|
);
|
||||||
|
const includeAllCheckbox = getByTestId(
|
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsIncludeAllSwitch
|
||||||
|
);
|
||||||
|
|
||||||
|
// It include-all-custom input appears after include-all checkbox is checked only
|
||||||
|
expect(() =>
|
||||||
|
getByTestId(selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput)
|
||||||
|
).toThrow('Unable to find an element');
|
||||||
|
|
||||||
|
fireEvent.click(multiCheckbox);
|
||||||
|
|
||||||
|
fireEvent.click(includeAllCheckbox);
|
||||||
|
const allValueInput = getByTestId(
|
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(variable.state.isMulti).toBe(true);
|
||||||
|
expect(variable.state.includeAll).toBe(true);
|
||||||
|
expect(allValueInput).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call update query and re-run query when input loses focus', async () => {
|
||||||
|
const variable = new CustomVariable({
|
||||||
|
name: 'customVar',
|
||||||
|
query: 'test, test2',
|
||||||
|
value: 'test',
|
||||||
|
});
|
||||||
|
const onRunQuery = jest.fn();
|
||||||
|
|
||||||
|
const { getByTestId } = render(<CustomVariableEditor variable={variable} onRunQuery={onRunQuery} />);
|
||||||
|
|
||||||
|
const queryInput = getByTestId(selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput);
|
||||||
|
fireEvent.change(queryInput, { target: { value: 'test3, test4' } });
|
||||||
|
fireEvent.blur(queryInput);
|
||||||
|
|
||||||
|
expect(onRunQuery).toHaveBeenCalled();
|
||||||
|
expect(variable.state.query).toBe('test3, test4');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update the variable state when all-custom-value input loses focus', () => {
|
||||||
|
const variable = new CustomVariable({
|
||||||
|
name: 'customVar',
|
||||||
|
query: 'test, test2',
|
||||||
|
value: 'test',
|
||||||
|
isMulti: true,
|
||||||
|
includeAll: true,
|
||||||
|
});
|
||||||
|
const onRunQuery = jest.fn();
|
||||||
|
|
||||||
|
const { getByTestId } = render(<CustomVariableEditor variable={variable} onRunQuery={onRunQuery} />);
|
||||||
|
|
||||||
|
const allValueInput = getByTestId(
|
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput
|
||||||
|
) as HTMLInputElement;
|
||||||
|
|
||||||
|
fireEvent.change(allValueInput, { target: { value: 'new custom all' } });
|
||||||
|
fireEvent.blur(allValueInput);
|
||||||
|
|
||||||
|
expect(variable.state.allValue).toBe('new custom all');
|
||||||
|
});
|
||||||
|
});
|
@ -1,11 +1,41 @@
|
|||||||
import React from 'react';
|
import React, { FormEvent } from 'react';
|
||||||
|
|
||||||
import { CustomVariable } from '@grafana/scenes';
|
import { CustomVariable } from '@grafana/scenes';
|
||||||
|
|
||||||
|
import { CustomVariableForm } from '../components/CustomVariableForm';
|
||||||
|
|
||||||
interface CustomVariableEditorProps {
|
interface CustomVariableEditorProps {
|
||||||
variable: CustomVariable;
|
variable: CustomVariable;
|
||||||
|
onRunQuery: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CustomVariableEditor(props: CustomVariableEditorProps) {
|
export function CustomVariableEditor({ variable, onRunQuery }: CustomVariableEditorProps) {
|
||||||
return <div>CustomVariableEditor</div>;
|
const { query, isMulti, allValue, includeAll } = variable.useState();
|
||||||
|
|
||||||
|
const onMultiChange = (event: FormEvent<HTMLInputElement>) => {
|
||||||
|
variable.setState({ isMulti: event.currentTarget.checked });
|
||||||
|
};
|
||||||
|
const onIncludeAllChange = (event: FormEvent<HTMLInputElement>) => {
|
||||||
|
variable.setState({ includeAll: event.currentTarget.checked });
|
||||||
|
};
|
||||||
|
const onQueryChange = (event: FormEvent<HTMLTextAreaElement>) => {
|
||||||
|
variable.setState({ query: event.currentTarget.value });
|
||||||
|
onRunQuery();
|
||||||
|
};
|
||||||
|
const onAllValueChange = (event: FormEvent<HTMLInputElement>) => {
|
||||||
|
variable.setState({ allValue: event.currentTarget.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CustomVariableForm
|
||||||
|
query={query ?? ''}
|
||||||
|
multi={!!isMulti}
|
||||||
|
allValue={allValue ?? ''}
|
||||||
|
includeAll={!!includeAll}
|
||||||
|
onMultiChange={onMultiChange}
|
||||||
|
onIncludeAllChange={onIncludeAllChange}
|
||||||
|
onQueryChange={onQueryChange}
|
||||||
|
onAllValueChange={onAllValueChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { FormEvent } from 'react';
|
||||||
|
|
||||||
import { TextBoxVariable } from '@grafana/scenes';
|
import { TextBoxVariable } from '@grafana/scenes';
|
||||||
|
|
||||||
@ -11,15 +11,10 @@ interface TextBoxVariableEditorProps {
|
|||||||
|
|
||||||
export function TextBoxVariableEditor({ variable }: TextBoxVariableEditorProps) {
|
export function TextBoxVariableEditor({ variable }: TextBoxVariableEditorProps) {
|
||||||
const { value } = variable.useState();
|
const { value } = variable.useState();
|
||||||
const [textValue, setTextValue] = useState(value);
|
|
||||||
|
|
||||||
const onTextValueChange = (event: React.FormEvent<HTMLInputElement>) => {
|
const onTextValueChange = (e: FormEvent<HTMLInputElement>) => {
|
||||||
setTextValue(event.currentTarget.value);
|
variable.setState({ value: e.currentTarget.value });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onBlur = () => {
|
return <TextBoxVariableForm defaultValue={value} onBlur={onTextValueChange} />;
|
||||||
variable.setState({ value: textValue });
|
|
||||||
};
|
|
||||||
|
|
||||||
return <TextBoxVariableForm value={textValue} onChange={onTextValueChange} onBlur={onBlur} />;
|
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
import React, { FormEvent, PureComponent } from 'react';
|
import React, { FormEvent, PureComponent } from 'react';
|
||||||
import { MapDispatchToProps, MapStateToProps } from 'react-redux';
|
import { MapDispatchToProps, MapStateToProps } from 'react-redux';
|
||||||
|
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
|
||||||
import { connectWithStore } from 'app/core/utils/connectWithReduxStore';
|
import { connectWithStore } from 'app/core/utils/connectWithReduxStore';
|
||||||
|
import { CustomVariableForm } from 'app/features/dashboard-scene/settings/variables/components/CustomVariableForm';
|
||||||
import { StoreState } from 'app/types';
|
import { StoreState } from 'app/types';
|
||||||
|
|
||||||
import { VariableLegend } from '../../dashboard-scene/settings/variables/components/VariableLegend';
|
|
||||||
import { VariableTextAreaField } from '../../dashboard-scene/settings/variables/components/VariableTextAreaField';
|
|
||||||
import { SelectionOptionsEditor } from '../editor/SelectionOptionsEditor';
|
|
||||||
import { OnPropChangeArguments, VariableEditorProps } from '../editor/types';
|
import { OnPropChangeArguments, VariableEditorProps } from '../editor/types';
|
||||||
import { changeVariableMultiValue } from '../state/actions';
|
import { changeVariableMultiValue } from '../state/actions';
|
||||||
import { CustomVariableModel, VariableWithMultiSupport } from '../types';
|
import { CustomVariableModel, VariableWithMultiSupport } from '../types';
|
||||||
@ -23,18 +20,11 @@ interface DispatchProps {
|
|||||||
export type Props = OwnProps & ConnectedProps & DispatchProps;
|
export type Props = OwnProps & ConnectedProps & DispatchProps;
|
||||||
|
|
||||||
class CustomVariableEditorUnconnected extends PureComponent<Props> {
|
class CustomVariableEditorUnconnected extends PureComponent<Props> {
|
||||||
onChange = (event: FormEvent<HTMLTextAreaElement>) => {
|
|
||||||
this.props.onPropChange({
|
|
||||||
propName: 'query',
|
|
||||||
propValue: event.currentTarget.value,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onSelectionOptionsChange = async ({ propName, propValue }: OnPropChangeArguments<VariableWithMultiSupport>) => {
|
onSelectionOptionsChange = async ({ propName, propValue }: OnPropChangeArguments<VariableWithMultiSupport>) => {
|
||||||
this.props.onPropChange({ propName, propValue, updateOptions: true });
|
this.props.onPropChange({ propName, propValue, updateOptions: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
onBlur = (event: FormEvent<HTMLTextAreaElement>) => {
|
onQueryChange = (event: FormEvent<HTMLTextAreaElement>) => {
|
||||||
this.props.onPropChange({
|
this.props.onPropChange({
|
||||||
propName: 'query',
|
propName: 'query',
|
||||||
propValue: event.currentTarget.value,
|
propValue: event.currentTarget.value,
|
||||||
@ -44,26 +34,22 @@ class CustomVariableEditorUnconnected extends PureComponent<Props> {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<>
|
<CustomVariableForm
|
||||||
<VariableLegend>Custom options</VariableLegend>
|
query={this.props.variable.query}
|
||||||
|
multi={this.props.variable.multi}
|
||||||
<VariableTextAreaField
|
allValue={this.props.variable.allValue}
|
||||||
name="Values separated by comma"
|
includeAll={this.props.variable.includeAll}
|
||||||
value={this.props.variable.query}
|
onQueryChange={this.onQueryChange}
|
||||||
placeholder="1, 10, mykey : myvalue, myvalue, escaped\,value"
|
onMultiChange={(event) =>
|
||||||
onChange={this.onChange}
|
this.onSelectionOptionsChange({ propName: 'multi', propValue: event.currentTarget.checked })
|
||||||
onBlur={this.onBlur}
|
}
|
||||||
required
|
onIncludeAllChange={(event) =>
|
||||||
width={52}
|
this.onSelectionOptionsChange({ propName: 'includeAll', propValue: event.currentTarget.checked })
|
||||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput}
|
}
|
||||||
|
onAllValueChange={(event) =>
|
||||||
|
this.onSelectionOptionsChange({ propName: 'allValue', propValue: event.currentTarget.value })
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<VariableLegend>Selection options</VariableLegend>
|
|
||||||
<SelectionOptionsEditor
|
|
||||||
variable={this.props.variable}
|
|
||||||
onPropChange={this.onSelectionOptionsChange}
|
|
||||||
onMultiChanged={this.props.changeVariableMultiValue}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
import React, { ChangeEvent, FormEvent, useCallback } from 'react';
|
import React, { ChangeEvent, FormEvent, useCallback } from 'react';
|
||||||
|
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { SelectionOptionsForm } from 'app/features/dashboard-scene/settings/variables/components/SelectionOptionsForm';
|
||||||
import { VerticalGroup } from '@grafana/ui';
|
|
||||||
|
|
||||||
import { VariableCheckboxField } from '../../dashboard-scene/settings/variables/components/VariableCheckboxField';
|
|
||||||
import { VariableTextField } from '../../dashboard-scene/settings/variables/components/VariableTextField';
|
|
||||||
import { KeyedVariableIdentifier } from '../state/types';
|
import { KeyedVariableIdentifier } from '../state/types';
|
||||||
import { VariableWithMultiSupport } from '../types';
|
import { VariableWithMultiSupport } from '../types';
|
||||||
import { toKeyedVariableIdentifier } from '../utils';
|
import { toKeyedVariableIdentifier } from '../utils';
|
||||||
@ -43,29 +40,14 @@ export const SelectionOptionsEditor = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VerticalGroup spacing="md" height="inherit">
|
<SelectionOptionsForm
|
||||||
<VariableCheckboxField
|
multi={variable.multi}
|
||||||
value={variable.multi}
|
includeAll={variable.includeAll}
|
||||||
name="Multi-value"
|
allValue={variable.allValue}
|
||||||
description="Enables multiple values to be selected at the same time"
|
onMultiChange={onMultiChanged}
|
||||||
onChange={onMultiChanged}
|
onIncludeAllChange={onIncludeAllChanged}
|
||||||
|
onAllValueChange={onAllValueChanged}
|
||||||
/>
|
/>
|
||||||
<VariableCheckboxField
|
|
||||||
value={variable.includeAll}
|
|
||||||
name="Include All option"
|
|
||||||
description="Enables an option to include all variables"
|
|
||||||
onChange={onIncludeAllChanged}
|
|
||||||
/>
|
|
||||||
{variable.includeAll && (
|
|
||||||
<VariableTextField
|
|
||||||
value={variable.allValue ?? ''}
|
|
||||||
onChange={onAllValueChanged}
|
|
||||||
name="Custom all value"
|
|
||||||
placeholder="blank = auto"
|
|
||||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInputV2}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</VerticalGroup>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
SelectionOptionsEditor.displayName = 'SelectionOptionsEditor';
|
SelectionOptionsEditor.displayName = 'SelectionOptionsEditor';
|
||||||
|
@ -205,7 +205,7 @@ export class VariableEditorEditorUnConnected extends PureComponent<Props, State>
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.General.submitButton}
|
data-testid={selectors.pages.Dashboard.Settings.Variables.Edit.General.submitButton}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
>
|
>
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import React, { FormEvent, ReactElement, useCallback } from 'react';
|
import React, { FormEvent, ReactElement, useCallback } from 'react';
|
||||||
|
|
||||||
import { TextBoxVariableForm } from 'app/features/dashboard-scene/settings/variables/components/TextBoxVariableForm';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
|
||||||
|
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 { TextBoxVariableModel } from '../types';
|
import { TextBoxVariableModel } from '../types';
|
||||||
|
|
||||||
@ -20,5 +22,18 @@ export function TextBoxVariableEditor({ onPropChange, variable: { query } }: Pro
|
|||||||
const onChange = useCallback((e: FormEvent<HTMLInputElement>) => updateVariable(e, false), [updateVariable]);
|
const onChange = useCallback((e: FormEvent<HTMLInputElement>) => updateVariable(e, false), [updateVariable]);
|
||||||
const onBlur = useCallback((e: FormEvent<HTMLInputElement>) => updateVariable(e, true), [updateVariable]);
|
const onBlur = useCallback((e: FormEvent<HTMLInputElement>) => updateVariable(e, true), [updateVariable]);
|
||||||
|
|
||||||
return <TextBoxVariableForm value={query} onChange={onChange} onBlur={onBlur} />;
|
return (
|
||||||
|
<>
|
||||||
|
<VariableLegend>Text options</VariableLegend>
|
||||||
|
<VariableTextField
|
||||||
|
value={query}
|
||||||
|
name="Default value"
|
||||||
|
placeholder="default value, if any"
|
||||||
|
onChange={onChange}
|
||||||
|
onBlur={onBlur}
|
||||||
|
width={30}
|
||||||
|
testId={selectors.pages.Dashboard.Settings.Variables.Edit.TextBoxVariable.textBoxOptionsQueryInputV2}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user