speakyourcode/grafana-button-panel

View on GitHub
src/editor.tsx

Summary

Maintainability
A
1 hr
Test Coverage
C
74%
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} />,
    });
}