grafana/public/app/features/canvas/elements/button.tsx
Ashley Harrison 47f8717149
React: Use new JSX transform (#88802)
* update eslint, tsconfig + esbuild to handle new jsx transform

* remove thing that breaks the new jsx transform

* remove react imports

* adjust grafana-icons build

* is this the correct syntax?

* try this

* well this was much easier than expected...

* change grafana-plugin-configs webpack config

* fixes

* fix lockfile

* fix 2 more violations

* use path.resolve instead of require.resolve

* remove react import

* fix react imports

* more fixes

* remove React import

* remove import React from docs

* remove another react import
2024-06-25 12:43:47 +01:00

222 lines
5.8 KiB
TypeScript

import { css } from '@emotion/css';
import { useState } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { PluginState } from '@grafana/data/src';
import { TextDimensionMode } from '@grafana/schema';
import { Button, Spinner, useStyles2 } from '@grafana/ui';
import { DimensionContext } from 'app/features/dimensions/context';
import { ColorDimensionEditor } from 'app/features/dimensions/editors';
import { TextDimensionEditor } from 'app/features/dimensions/editors/TextDimensionEditor';
import { APIEditor, APIEditorConfig } from 'app/plugins/panel/canvas/editor/element/APIEditor';
import { ButtonStyleConfig, ButtonStyleEditor } from 'app/plugins/panel/canvas/editor/element/ButtonStyleEditor';
import { callApi } from 'app/plugins/panel/canvas/editor/element/utils';
import { HttpRequestMethod } from 'app/plugins/panel/canvas/panelcfg.gen';
import { CanvasElementItem, CanvasElementOptions, CanvasElementProps, defaultLightTextColor } from '../element';
import { Align, TextConfig, TextData } from '../types';
interface ButtonData extends Omit<TextData, 'valign'> {
api?: APIEditorConfig;
style?: ButtonStyleConfig;
}
interface ButtonConfig extends Omit<TextConfig, 'valign'> {
api?: APIEditorConfig;
style?: ButtonStyleConfig;
}
export const defaultApiConfig: APIEditorConfig = {
endpoint: '',
method: HttpRequestMethod.POST,
data: '{}',
contentType: 'application/json',
queryParams: [],
headerParams: [],
};
export const defaultStyleConfig: ButtonStyleConfig = {
variant: 'primary',
};
const ButtonDisplay = ({ data }: CanvasElementProps<ButtonConfig, ButtonData>) => {
const styles = useStyles2(getStyles, data);
const [isLoading, setIsLoading] = useState(false);
const updateLoadingStateCallback = (loading: boolean) => {
setIsLoading(loading);
};
const onClick = () => {
if (data?.api && data?.api?.endpoint) {
setIsLoading(true);
callApi(data.api, updateLoadingStateCallback);
}
};
return (
<Button type="submit" variant={data?.style?.variant} onClick={onClick} className={styles.button}>
<span>
{isLoading && <Spinner inline={true} className={styles.buttonSpinner} />}
{data?.text}
</span>
</Button>
);
};
const getStyles = (theme: GrafanaTheme2, data: ButtonData | undefined) => ({
button: css({
height: '100%',
width: '100%',
display: 'grid',
'> span': {
display: 'inline-grid',
gridAutoFlow: 'column',
textAlign: data?.align,
fontSize: `${data?.size}px`,
color: data?.color,
},
}),
buttonSpinner: css({
marginRight: theme.spacing(0.5),
}),
});
export const buttonItem: CanvasElementItem<ButtonConfig, ButtonData> = {
id: 'button',
name: 'Button',
description: 'Button',
state: PluginState.beta,
standardEditorConfig: {
background: false,
},
display: ButtonDisplay,
defaultSize: {
width: 150,
height: 45,
},
getNewOptions: (options) => ({
...options,
config: {
text: {
mode: TextDimensionMode.Fixed,
fixed: 'Button',
},
align: Align.Center,
color: {
fixed: defaultLightTextColor,
},
size: 14,
api: defaultApiConfig,
style: defaultStyleConfig,
},
background: {
color: {
fixed: 'transparent',
},
},
placement: {
width: options?.placement?.width ?? 32,
height: options?.placement?.height ?? 78,
top: options?.placement?.top ?? 100,
left: options?.placement?.left ?? 100,
rotation: options?.placement?.rotation ?? 0,
},
}),
// Called when data changes
prepareData: (dimensionContext: DimensionContext, elementOptions: CanvasElementOptions<ButtonConfig>) => {
const buttonConfig = elementOptions.config;
const getAPIConfig = () => {
if (buttonConfig?.api) {
buttonConfig.api = {
...buttonConfig.api,
method: buttonConfig.api.method ?? defaultApiConfig.method,
contentType: buttonConfig.api.contentType ?? defaultApiConfig.contentType,
};
return buttonConfig.api;
}
return undefined;
};
const data: ButtonData = {
text: buttonConfig?.text ? dimensionContext.getText(buttonConfig.text).value() : '',
align: buttonConfig?.align ?? Align.Center,
size: buttonConfig?.size ?? 14,
api: getAPIConfig(),
style: buttonConfig?.style ?? defaultStyleConfig,
};
if (buttonConfig?.color) {
data.color = dimensionContext.getColor(buttonConfig.color).value();
}
return data;
},
// Heatmap overlay options
registerOptionsUI: (builder) => {
const category = ['Button'];
builder
.addCustomEditor({
category,
id: 'styleSelector',
path: 'config.style',
name: 'Style',
editor: ButtonStyleEditor,
})
.addCustomEditor({
category,
id: 'textSelector',
path: 'config.text',
name: 'Text',
editor: TextDimensionEditor,
})
.addCustomEditor({
category,
id: 'config.color',
path: 'config.color',
name: 'Text color',
editor: ColorDimensionEditor,
settings: {},
defaultValue: {},
})
.addRadio({
category,
path: 'config.align',
name: 'Align text',
settings: {
options: [
{ value: Align.Left, label: 'Left' },
{ value: Align.Center, label: 'Center' },
{ value: Align.Right, label: 'Right' },
],
},
defaultValue: Align.Left,
})
.addNumberInput({
category,
path: 'config.size',
name: 'Text size',
settings: {
placeholder: 'Auto',
},
})
.addCustomEditor({
category,
id: 'apiSelector',
path: 'config.api',
name: 'API',
editor: APIEditor,
});
},
};