src/editor.tsx
import { PanelOptionsEditorBuilder, SelectableValue } from '@grafana/data';
import { getBackendSrv } from '@grafana/runtime';
import { Button, Collapse, Field, Input, RadioButtonGroup, Select, TextArea } from '@grafana/ui';
import React from 'react';
import { ButtonOptions, Options } from 'types';
export interface EditorProps {
buttons: ButtonOptions[];
onChange: (buttons: ButtonOptions[]) => void;
}
export const Editor: React.FC<EditorProps> = ({ buttons, onChange }) => {
const [elems, setElems] = React.useState<Array<SelectableValue<string>>>();
const [isOpen, setOpen] = React.useState<boolean[]>(buttons.map((e) => false));
const [cacheButtons, setCacheButtons] = React.useState<ButtonOptions[]>(buttons);
React.useEffect(() => {
let cancel = false;
const fetchData = async () => {
const ds = await getBackendSrv().get('/api/datasources');
if (!cancel) {
setElems(ds.map((i: any) => ({ label: i.name, value: i.name, name: i.name })));
}
};
fetchData();
return (): void => {
cancel = true;
};
}, []);
const updateCacheButtons = (index: number, newButton: ButtonOptions) => {
let currentButton = { ...cacheButtons[index] };
setCacheButtons([
...cacheButtons.slice(0, index),
{
text: newButton.text || currentButton.text,
datasource: newButton.datasource || currentButton.datasource,
query: newButton.query || currentButton.query,
variant: newButton.variant || currentButton.variant,
},
...cacheButtons.slice(index + 1),
]);
};
return (
<React.Fragment>
{cacheButtons.map((b: ButtonOptions, i: number) => (
<Collapse
key={i}
label={'Button ' + (i + 1).toString()}
isOpen={isOpen[i]}
collapsible
onToggle={() => {
setOpen((open) => [...open.slice(0, i), !open[i], ...open.slice(i + 1)]);
}}
>
<Field label="Text" description="Text to be displayed on the button">
<Input
id={'t-' + i.toString()}
value={b.text}
placeholder="Button"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => updateCacheButtons(i, { text: e.target.value })}
/>
</Field>
<Field label="Datasource" description="Choose the Datasource for the query">
<Select
onChange={(e: SelectableValue<string>) => updateCacheButtons(i, { datasource: e.value })}
value={b.datasource}
options={elems}
/>
</Field>
<Field label="Query" description="JSON query to be triggered on Button Click">
<TextArea
id={'q-' + i.toString()}
value={b.query}
placeholder="{ query: 'your query' }"
rows={5}
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => updateCacheButtons(i, { query: e.target.value })}
/>
</Field>
<Field label="Color" description="Color of the button">
<RadioButtonGroup
options={[
{ label: 'Primary', value: 'primary' },
{ label: 'Secondary', value: 'secondary' },
{ label: 'Destructive', value: 'destructive' },
{ label: 'Link', value: 'link' },
]}
value={b.variant || 'primary'}
fullWidth
onChange={(e: any) => updateCacheButtons(i, { variant: e })}
></RadioButtonGroup>
</Field>
<Field>
<>
<Button
icon="trash-alt"
variant="destructive"
onClick={() => onChange([...buttons.slice(0, i), ...buttons.slice(i + 1)])}
>
Delete
</Button>
<Button icon="save" variant="primary" onClick={() => onChange(cacheButtons)}>
Apply
</Button>
</>
</Field>
</Collapse>
))}
<Field>
<Button
variant="secondary"
icon="plus"
size="sm"
onClick={() => {
onChange([...buttons, { text: '', datasource: '', query: '' }]);
}}
>
Add Button
</Button>
</Field>
</React.Fragment>
);
};
export function addEditor(builder: PanelOptionsEditorBuilder<Options>) {
builder
.addRadio({
path: 'orientation',
name: 'Orientation',
description: 'Stacking direction in case of multiple buttons',
defaultValue: 'horizontal',
settings: {
options: [
{ value: 'horizontal', label: 'Horizontal' },
{ value: 'vertical', label: 'Vertical' },
],
},
})
.addCustomEditor({
id: 'buttons',
path: 'buttons',
name: 'Button Configuration',
defaultValue: [{ text: '', datasource: '', query: '' }],
editor: (props) => <Editor buttons={props.value} onChange={props.onChange} />,
});
}