mirror of https://github.com/grafana/grafana
QueryVariable: Add static options input (#107514)
* static options for query variable * add toggle * fix and add tests * run the hack codegen thing * more test fixes * make betterer happier * also make typecheck happy * make betterer happier * fix i18n key * tranalte static variables sort label * gen translations * update snapshotpull/106511/head
parent
baa89f3eac
commit
8eef17cb37
@ -0,0 +1,106 @@ |
|||||||
|
import { useState } from 'react'; |
||||||
|
|
||||||
|
import { selectors } from '@grafana/e2e-selectors'; |
||||||
|
import { t, Trans } from '@grafana/i18n'; |
||||||
|
import { VariableValueOption } from '@grafana/scenes'; |
||||||
|
import { Button, Input, Stack } from '@grafana/ui'; |
||||||
|
|
||||||
|
interface VariableOptionsFieldProps { |
||||||
|
options: VariableValueOption[]; |
||||||
|
onChange: (options: VariableValueOption[]) => void; |
||||||
|
width?: number; |
||||||
|
} |
||||||
|
|
||||||
|
export function VariableOptionsInput({ options, onChange, width }: VariableOptionsFieldProps) { |
||||||
|
const [optionsLocal, setOptionsLocal] = useState(options.length ? options : [{ value: '', label: '' }]); |
||||||
|
|
||||||
|
const updateOptions = (newOptions: VariableValueOption[]) => { |
||||||
|
setOptionsLocal(newOptions); |
||||||
|
onChange( |
||||||
|
newOptions |
||||||
|
.map((option) => ({ |
||||||
|
label: option.label.trim(), |
||||||
|
value: String(option.value).trim(), |
||||||
|
})) |
||||||
|
.filter((option) => !!option.label) |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
const handleValueChange = (index: number, value: string) => { |
||||||
|
if (optionsLocal[index].value !== value) { |
||||||
|
const newOptions = [...optionsLocal]; |
||||||
|
newOptions[index] = { ...newOptions[index], value }; |
||||||
|
updateOptions(newOptions); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const handleLabelChange = (index: number, label: string) => { |
||||||
|
if (optionsLocal[index].label !== label) { |
||||||
|
const newOptions = [...optionsLocal]; |
||||||
|
newOptions[index] = { ...newOptions[index], label }; |
||||||
|
updateOptions(newOptions); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const addOption = () => { |
||||||
|
const newOption: VariableValueOption = { value: '', label: '' }; |
||||||
|
const newOptions = [...optionsLocal, newOption]; |
||||||
|
updateOptions(newOptions); |
||||||
|
}; |
||||||
|
|
||||||
|
const removeOption = (index: number) => { |
||||||
|
const newOptions = optionsLocal.filter((_, i) => i !== index); |
||||||
|
updateOptions(newOptions); |
||||||
|
}; |
||||||
|
|
||||||
|
return ( |
||||||
|
<Stack direction="column" gap={2} width={width}> |
||||||
|
{optionsLocal.map((option, index) => ( |
||||||
|
<Stack |
||||||
|
direction="row" |
||||||
|
key={index} |
||||||
|
data-testid={selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsStaticOptionsRow} |
||||||
|
> |
||||||
|
<Input |
||||||
|
value={option.label} |
||||||
|
placeholder={t('variables.query-variable-static-options.label-placeholder', 'display label')} |
||||||
|
onChange={(e) => handleLabelChange(index, e.currentTarget.value)} |
||||||
|
data-testid={ |
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsStaticOptionsLabelInput |
||||||
|
} |
||||||
|
/> |
||||||
|
<Input |
||||||
|
value={String(option.value)} |
||||||
|
placeholder={t('variables.query-variable-static-options.value-placeholder', 'value, default empty string')} |
||||||
|
onChange={(e) => handleValueChange(index, e.currentTarget.value)} |
||||||
|
data-testid={ |
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsStaticOptionsValueInput |
||||||
|
} |
||||||
|
/> |
||||||
|
<Button |
||||||
|
icon="times" |
||||||
|
variant="secondary" |
||||||
|
aria-label={t('variables.query-variable-static-options.remove-option-button-label', 'Remove option')} |
||||||
|
onClick={() => removeOption(index)} |
||||||
|
data-testid={ |
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsStaticOptionsDeleteButton |
||||||
|
} |
||||||
|
/> |
||||||
|
</Stack> |
||||||
|
))} |
||||||
|
<div> |
||||||
|
<Button |
||||||
|
icon="plus" |
||||||
|
variant="secondary" |
||||||
|
onClick={addOption} |
||||||
|
data-testid={ |
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsStaticOptionsAddButton |
||||||
|
} |
||||||
|
aria-label={t('variables.query-variable-static-options.add-option-button-label', 'Add option')} |
||||||
|
> |
||||||
|
<Trans i18nKey="variables.query-variable-static-options.add-option-button-label">Add option</Trans> |
||||||
|
</Button> |
||||||
|
</div> |
||||||
|
</Stack> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,93 @@ |
|||||||
|
import { useState } from 'react'; |
||||||
|
|
||||||
|
import { selectors } from '@grafana/e2e-selectors'; |
||||||
|
import { t, Trans } from '@grafana/i18n'; |
||||||
|
import { QueryVariable } from '@grafana/scenes'; |
||||||
|
import { Field, Stack, Switch } from '@grafana/ui'; |
||||||
|
import { VariableLegend } from 'app/features/dashboard-scene/settings/variables/components/VariableLegend'; |
||||||
|
import { VariableOptionsInput } from 'app/features/dashboard-scene/settings/variables/components/VariableOptionsInput'; |
||||||
|
import { VariableSelectField } from 'app/features/dashboard-scene/settings/variables/components/VariableSelectField'; |
||||||
|
|
||||||
|
export type StaticOptionsType = QueryVariable['state']['staticOptions']; |
||||||
|
export type StaticOptionsOrderType = QueryVariable['state']['staticOptionsOrder']; |
||||||
|
|
||||||
|
interface QueryVariableStaticOptionsProps { |
||||||
|
staticOptions: StaticOptionsType; |
||||||
|
staticOptionsOrder: StaticOptionsOrderType; |
||||||
|
onStaticOptionsChange: (staticOptions: StaticOptionsType) => void; |
||||||
|
onStaticOptionsOrderChange: (staticOptionsOrder: StaticOptionsOrderType) => void; |
||||||
|
} |
||||||
|
|
||||||
|
const SORT_OPTIONS = [ |
||||||
|
{ label: 'Before query values', value: 'before' }, |
||||||
|
{ label: 'After query values', value: 'after' }, |
||||||
|
{ label: 'Sorted with query values', value: 'sorted' }, |
||||||
|
]; |
||||||
|
|
||||||
|
export function QueryVariableStaticOptions(props: QueryVariableStaticOptionsProps) { |
||||||
|
const { staticOptions, onStaticOptionsChange, staticOptionsOrder, onStaticOptionsOrderChange } = props; |
||||||
|
|
||||||
|
const value = SORT_OPTIONS.find((o) => o.value === staticOptionsOrder) ?? SORT_OPTIONS[0]; |
||||||
|
|
||||||
|
const [areStaticOptionsEnabled, setAreStaticOptionsEnabled] = useState(!!staticOptions?.length); |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<VariableLegend> |
||||||
|
<Trans i18nKey="dashboard-scene.query-variable-editor-form.static-options-legend">Static options</Trans> |
||||||
|
</VariableLegend> |
||||||
|
<Stack direction="column" gap={2}> |
||||||
|
<Field |
||||||
|
noMargin |
||||||
|
label={t('dashboard-scene.query-variable-editor-form.label-use-static-options', 'Use static options')} |
||||||
|
description={t( |
||||||
|
'variables.query-variable-static-options.description', |
||||||
|
'Add custom options in addition to query results' |
||||||
|
)} |
||||||
|
> |
||||||
|
<> |
||||||
|
<Stack direction="column" gap={2}> |
||||||
|
<Switch |
||||||
|
data-testid={ |
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsStaticOptionsToggle |
||||||
|
} |
||||||
|
value={areStaticOptionsEnabled} |
||||||
|
onChange={(e) => { |
||||||
|
if (e.currentTarget.checked) { |
||||||
|
setAreStaticOptionsEnabled(true); |
||||||
|
} else { |
||||||
|
setAreStaticOptionsEnabled(false); |
||||||
|
if (!!staticOptions?.length) { |
||||||
|
onStaticOptionsChange(undefined); |
||||||
|
} |
||||||
|
} |
||||||
|
}} |
||||||
|
/> |
||||||
|
|
||||||
|
{areStaticOptionsEnabled && ( |
||||||
|
<VariableOptionsInput width={60} options={staticOptions ?? []} onChange={onStaticOptionsChange} /> |
||||||
|
)} |
||||||
|
</Stack> |
||||||
|
</> |
||||||
|
</Field> |
||||||
|
|
||||||
|
{areStaticOptionsEnabled && ( |
||||||
|
<VariableSelectField |
||||||
|
name={t('dashboard-scene.query-variable-editor-form.label-static-options-sort', 'Static options sort')} |
||||||
|
description={t( |
||||||
|
'variables.query-variable-static-options-sort-select.description-values-variable', |
||||||
|
'How to sort static options with query results' |
||||||
|
)} |
||||||
|
value={value} |
||||||
|
options={SORT_OPTIONS} |
||||||
|
onChange={(opt) => onStaticOptionsOrderChange(opt.value)} |
||||||
|
testId={ |
||||||
|
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsStaticOptionsOrderDropdown |
||||||
|
} |
||||||
|
width={25} |
||||||
|
/> |
||||||
|
)} |
||||||
|
</Stack> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
Loading…
Reference in new issue