mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
OptionsUI: support string list (#25134)
This commit is contained in:
parent
d526647005
commit
53b9f325c0
@ -75,6 +75,10 @@ export interface OptionsUIRegistryBuilderAPI<
|
|||||||
config: OptionEditorConfig<TOptions, TSettings, string>
|
config: OptionEditorConfig<TOptions, TSettings, string>
|
||||||
): this;
|
): this;
|
||||||
|
|
||||||
|
addStringArray?<TSettings extends StringFieldConfigSettings = StringFieldConfigSettings>(
|
||||||
|
config: OptionEditorConfig<TOptions, TSettings, string[]>
|
||||||
|
): this;
|
||||||
|
|
||||||
addSelect?<TOption, TSettings extends SelectFieldConfigSettings<TOption>>(
|
addSelect?<TOption, TSettings extends SelectFieldConfigSettings<TOption>>(
|
||||||
config: OptionEditorConfig<TOptions, TSettings, TOption>
|
config: OptionEditorConfig<TOptions, TSettings, TOption>
|
||||||
): this;
|
): this;
|
||||||
|
@ -143,6 +143,16 @@ export class PanelOptionsEditorBuilder<TOptions> extends OptionsUIRegistryBuilde
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addStringArray<TSettings>(
|
||||||
|
config: PanelOptionsEditorConfig<TOptions, TSettings & StringFieldConfigSettings, string[]>
|
||||||
|
) {
|
||||||
|
return this.addCustomEditor({
|
||||||
|
...config,
|
||||||
|
id: config.path,
|
||||||
|
editor: standardEditorsRegistry.get('strings').editor as any,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
addSelect<TOption, TSettings extends SelectFieldConfigSettings<TOption>>(
|
addSelect<TOption, TSettings extends SelectFieldConfigSettings<TOption>>(
|
||||||
config: PanelOptionsEditorConfig<TOptions, TSettings, TOption>
|
config: PanelOptionsEditorConfig<TOptions, TSettings, TOption>
|
||||||
) {
|
) {
|
||||||
|
112
packages/grafana-ui/src/components/OptionsUI/strings.tsx
Normal file
112
packages/grafana-ui/src/components/OptionsUI/strings.tsx
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { FieldConfigEditorProps, StringFieldConfigSettings, GrafanaTheme } from '@grafana/data';
|
||||||
|
import { Input } from '../Input/Input';
|
||||||
|
import { Icon } from '../Icon/Icon';
|
||||||
|
import { stylesFactory, getTheme } from '../../themes';
|
||||||
|
import { css } from 'emotion';
|
||||||
|
import { Button } from '../Button';
|
||||||
|
|
||||||
|
type Props = FieldConfigEditorProps<string[], StringFieldConfigSettings>;
|
||||||
|
interface State {
|
||||||
|
showAdd: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StringArrayEditor extends React.PureComponent<Props, State> {
|
||||||
|
state = {
|
||||||
|
showAdd: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
onRemoveString = (index: number) => {
|
||||||
|
const { value, onChange } = this.props;
|
||||||
|
const copy = [...value];
|
||||||
|
copy.splice(index, 1);
|
||||||
|
onChange(copy);
|
||||||
|
};
|
||||||
|
|
||||||
|
onValueChange = (e: React.SyntheticEvent, idx: number) => {
|
||||||
|
const evt = e as React.KeyboardEvent<HTMLInputElement>;
|
||||||
|
if (e.hasOwnProperty('key')) {
|
||||||
|
if (evt.key !== 'Enter') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { value, onChange } = this.props;
|
||||||
|
|
||||||
|
// Form event, or Enter
|
||||||
|
const v = evt.currentTarget.value.trim();
|
||||||
|
if (idx < 0) {
|
||||||
|
if (v) {
|
||||||
|
evt.currentTarget.value = ''; // reset last value
|
||||||
|
onChange([...value, v]);
|
||||||
|
}
|
||||||
|
this.setState({ showAdd: false });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!v) {
|
||||||
|
return this.onRemoveString(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const copy = [...value];
|
||||||
|
copy[idx] = v;
|
||||||
|
onChange(copy);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { value, item } = this.props;
|
||||||
|
const { showAdd } = this.state;
|
||||||
|
const styles = getStyles(getTheme());
|
||||||
|
const placeholder = item.settings?.placeholder || 'Add text';
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{value.map((v, index) => {
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
className={styles.textInput}
|
||||||
|
key={`${index}/${v}`}
|
||||||
|
defaultValue={v || ''}
|
||||||
|
onBlur={e => this.onValueChange(e, index)}
|
||||||
|
onKeyDown={e => this.onValueChange(e, index)}
|
||||||
|
suffix={<Icon className={styles.trashIcon} name="trash-alt" onClick={() => this.onRemoveString(index)} />}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
{showAdd ? (
|
||||||
|
<Input
|
||||||
|
autoFocus
|
||||||
|
className={styles.textInput}
|
||||||
|
placeholder={placeholder}
|
||||||
|
defaultValue={''}
|
||||||
|
onBlur={e => this.onValueChange(e, -1)}
|
||||||
|
onKeyDown={e => this.onValueChange(e, -1)}
|
||||||
|
suffix={<Icon name="plus-circle" />}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Button icon="plus" size="sm" variant="secondary" onClick={() => this.setState({ showAdd: true })}>
|
||||||
|
{placeholder}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||||
|
return {
|
||||||
|
textInput: css`
|
||||||
|
margin-bottom: 5px;
|
||||||
|
&:hover {
|
||||||
|
border: 1px solid ${theme.colors.formInputBorderHover};
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
trashIcon: css`
|
||||||
|
color: ${theme.colors.textWeak};
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: ${theme.colors.text};
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
});
|
@ -118,6 +118,7 @@ export { Slider } from './Slider/Slider';
|
|||||||
|
|
||||||
// TODO: namespace!!
|
// TODO: namespace!!
|
||||||
export { StringValueEditor } from './OptionsUI/string';
|
export { StringValueEditor } from './OptionsUI/string';
|
||||||
|
export { StringArrayEditor } from './OptionsUI/strings';
|
||||||
export { NumberValueEditor } from './OptionsUI/number';
|
export { NumberValueEditor } from './OptionsUI/number';
|
||||||
export { SelectValueEditor } from './OptionsUI/select';
|
export { SelectValueEditor } from './OptionsUI/select';
|
||||||
export { FieldConfigItemHeaderTitle } from './FieldConfigs/FieldConfigItemHeaderTitle';
|
export { FieldConfigItemHeaderTitle } from './FieldConfigs/FieldConfigItemHeaderTitle';
|
||||||
|
@ -20,7 +20,13 @@ import {
|
|||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
|
|
||||||
import { Switch } from '../components/Switch/Switch';
|
import { Switch } from '../components/Switch/Switch';
|
||||||
import { NumberValueEditor, RadioButtonGroup, StringValueEditor, SelectValueEditor } from '../components';
|
import {
|
||||||
|
NumberValueEditor,
|
||||||
|
RadioButtonGroup,
|
||||||
|
StringValueEditor,
|
||||||
|
StringArrayEditor,
|
||||||
|
SelectValueEditor,
|
||||||
|
} from '../components';
|
||||||
import { ValueMappingsValueEditor } from '../components/OptionsUI/mappings';
|
import { ValueMappingsValueEditor } from '../components/OptionsUI/mappings';
|
||||||
import { ThresholdsValueEditor } from '../components/OptionsUI/thresholds';
|
import { ThresholdsValueEditor } from '../components/OptionsUI/thresholds';
|
||||||
import { UnitValueEditor } from '../components/OptionsUI/units';
|
import { UnitValueEditor } from '../components/OptionsUI/units';
|
||||||
@ -227,6 +233,13 @@ export const getStandardOptionEditors = () => {
|
|||||||
editor: StringValueEditor as any,
|
editor: StringValueEditor as any,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const strings: StandardEditorsRegistryItem<string[]> = {
|
||||||
|
id: 'strings',
|
||||||
|
name: 'String array',
|
||||||
|
description: 'An array of strings',
|
||||||
|
editor: StringArrayEditor as any,
|
||||||
|
};
|
||||||
|
|
||||||
const boolean: StandardEditorsRegistryItem<boolean> = {
|
const boolean: StandardEditorsRegistryItem<boolean> = {
|
||||||
id: 'boolean',
|
id: 'boolean',
|
||||||
name: 'Boolean',
|
name: 'Boolean',
|
||||||
@ -290,5 +303,5 @@ export const getStandardOptionEditors = () => {
|
|||||||
description: '',
|
description: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
return [text, number, boolean, radio, select, unit, mappings, thresholds, links, color, statsPicker];
|
return [text, number, boolean, radio, select, unit, mappings, thresholds, links, color, statsPicker, strings];
|
||||||
};
|
};
|
||||||
|
@ -19,6 +19,15 @@ export const plugin = new PanelPlugin<TextOptions>(TextPanel)
|
|||||||
},
|
},
|
||||||
defaultValue: 'markdown',
|
defaultValue: 'markdown',
|
||||||
})
|
})
|
||||||
|
.addStringArray({
|
||||||
|
path: 'strings',
|
||||||
|
name: 'String Array',
|
||||||
|
description: 'list of strings',
|
||||||
|
settings: {
|
||||||
|
placeholder: 'Add a string value (text2 demo)',
|
||||||
|
},
|
||||||
|
defaultValue: ['hello', 'world'],
|
||||||
|
})
|
||||||
.addTextInput({
|
.addTextInput({
|
||||||
path: 'content',
|
path: 'content',
|
||||||
name: 'Content',
|
name: 'Content',
|
||||||
|
Loading…
Reference in New Issue
Block a user