From d7463556c23c26f6f4ce96353e133b924acda6a0 Mon Sep 17 00:00:00 2001 From: Adela Almasan <88068998+adela-almasan@users.noreply.github.com> Date: Wed, 30 Apr 2025 09:43:06 -0500 Subject: [PATCH] Actions: Add button style customization (#104126) --- packages/grafana-data/src/types/action.ts | 18 +++------ .../src/components/Actions/ActionButton.tsx | 7 +++- .../VizTooltip/VizTooltipFooter.tsx | 2 +- public/app/features/actions/ActionEditor.tsx | 40 ++++++++++++++++++- public/app/features/actions/utils.ts | 5 ++- public/locales/en-US/grafana.json | 8 +++- 6 files changed, 63 insertions(+), 17 deletions(-) diff --git a/packages/grafana-data/src/types/action.ts b/packages/grafana-data/src/types/action.ts index fc17aa25046..06a61e53ecd 100644 --- a/packages/grafana-data/src/types/action.ts +++ b/packages/grafana-data/src/types/action.ts @@ -1,12 +1,13 @@ -import { ScopedVars } from './ScopedVars'; -import { DataFrame, Field, ValueLinkConfig } from './dataFrame'; -import { InterpolateFunction } from './panel'; +import { CSSProperties } from 'react'; + import { SelectableValue } from './select'; export enum ActionType { Fetch = 'fetch', } +type ActionButtonCssProperties = Pick; + export interface Action { type: ActionType; title: string; @@ -17,6 +18,7 @@ export interface Action { [ActionType.Fetch]: FetchOptions; confirmation?: string; oneClick?: boolean; + style?: ActionButtonCssProperties; } /** @@ -27,6 +29,7 @@ export interface ActionModel { onClick: (event: any, origin?: any) => void; confirmation?: string; oneClick?: boolean; + style: ActionButtonCssProperties; } interface FetchOptions { @@ -67,12 +70,3 @@ export const defaultActionConfig: Action = { headers: [['Content-Type', 'application/json']], }, }; - -export type ActionsArgs = { - frame: DataFrame; - field: Field; - fieldScopedVars: ScopedVars; - replaceVariables: InterpolateFunction; - actions: Action[]; - config: ValueLinkConfig; -}; diff --git a/packages/grafana-ui/src/components/Actions/ActionButton.tsx b/packages/grafana-ui/src/components/Actions/ActionButton.tsx index f43329a21b7..9d2987dc47d 100644 --- a/packages/grafana-ui/src/components/Actions/ActionButton.tsx +++ b/packages/grafana-ui/src/components/Actions/ActionButton.tsx @@ -2,6 +2,7 @@ import { useState } from 'react'; import { ActionModel, Field } from '@grafana/data'; +import { useTheme2 } from '../../themes'; import { t } from '../../utils/i18n'; import { Button, ButtonProps } from '../Button'; import { ConfirmModal } from '../ConfirmModal/ConfirmModal'; @@ -14,6 +15,10 @@ type ActionButtonProps = ButtonProps & { * @internal */ export function ActionButton({ action, ...buttonProps }: ActionButtonProps) { + const theme = useTheme2(); + const backgroundColor = action.style.backgroundColor || theme.colors.secondary.main; + const textColor = theme.colors.getContrastText(backgroundColor); + const [showConfirm, setShowConfirm] = useState(false); return ( @@ -23,7 +28,7 @@ export function ActionButton({ action, ...buttonProps }: ActionButtonProps) { size="sm" onClick={() => setShowConfirm(true)} {...buttonProps} - style={{ width: 'fit-content' }} + style={{ width: 'fit-content', backgroundColor, color: textColor }} > {action.title} diff --git a/packages/grafana-ui/src/components/VizTooltip/VizTooltipFooter.tsx b/packages/grafana-ui/src/components/VizTooltip/VizTooltipFooter.tsx index 2552e1e6541..fc666c36cb5 100644 --- a/packages/grafana-ui/src/components/VizTooltip/VizTooltipFooter.tsx +++ b/packages/grafana-ui/src/components/VizTooltip/VizTooltipFooter.tsx @@ -73,7 +73,7 @@ const renderDataLinks = makeRenderLinksOrActions( const renderActions = makeRenderLinksOrActions( (title) => Click to {{ actionTitle: title }}, - (item, i, styles) => + (item, i) => ); export const VizTooltipFooter = ({ dataLinks, actions = [], annotate }: VizTooltipFooterProps) => { diff --git a/public/app/features/actions/ActionEditor.tsx b/public/app/features/actions/ActionEditor.tsx index 626156f87a5..58cdf0483d5 100644 --- a/public/app/features/actions/ActionEditor.tsx +++ b/public/app/features/actions/ActionEditor.tsx @@ -2,7 +2,17 @@ import { css } from '@emotion/css'; import { memo } from 'react'; import { Action, GrafanaTheme2, httpMethodOptions, HttpRequestMethod, VariableSuggestion } from '@grafana/data'; -import { Switch, Field, InlineField, InlineFieldRow, RadioButtonGroup, JSONFormatter, useStyles2 } from '@grafana/ui'; +import { + Switch, + Field, + InlineField, + InlineFieldRow, + RadioButtonGroup, + JSONFormatter, + useStyles2, + ColorPicker, + useTheme2, +} from '@grafana/ui'; import { t } from 'app/core/internationalization'; import { HTMLElementType, SuggestionsInput } from '../transformers/suggestionsInput/SuggestionsInput'; @@ -21,6 +31,7 @@ const LABEL_WIDTH = 13; export const ActionEditor = memo(({ index, value, onChange, suggestions, showOneClick }: ActionEditorProps) => { const styles = useStyles2(getStyles); + const theme = useTheme2(); const onTitleChange = (title: string) => { onChange(index, { ...value, title }); @@ -84,6 +95,16 @@ export const ActionEditor = memo(({ index, value, onChange, suggestions, showOne }); }; + const onBackgroundColorChange = (backgroundColor: string) => { + onChange(index, { + ...value, + style: { + ...value.style, + backgroundColor, + }, + }); + }; + const renderJSON = (data = '{}') => { try { const json = JSON.parse(data); @@ -133,6 +154,19 @@ export const ActionEditor = memo(({ index, value, onChange, suggestions, showOne /> + + + + + + {showOneClick && ( ({ inputField: css({ marginRight: 4, }), + colorPicker: css({ + display: 'flex', + alignItems: 'center', + }), }); ActionEditor.displayName = 'ActionEditor'; diff --git a/public/app/features/actions/utils.ts b/public/app/features/actions/utils.ts index 3874b166444..e8771075178 100644 --- a/public/app/features/actions/utils.ts +++ b/public/app/features/actions/utils.ts @@ -13,7 +13,7 @@ import { textUtil, ValueLinkConfig, } from '@grafana/data'; -import { BackendSrvRequest, getBackendSrv } from '@grafana/runtime'; +import { BackendSrvRequest, getBackendSrv, config as grafanaConfig } from '@grafana/runtime'; import { appEvents } from 'app/core/core'; import { HttpRequestMethod } from '../../plugins/panel/canvas/panelcfg.gen'; @@ -63,6 +63,9 @@ export const getActions = ( buildActionOnClick(action, boundReplaceVariables); }, oneClick: action.oneClick ?? false, + style: { + backgroundColor: action.style?.backgroundColor ?? grafanaConfig.theme2.colors.secondary.main, + }, }; return actionModel; diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 483ba0d2b9c..724c3e565fa 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -44,6 +44,11 @@ }, "actions": { "action-editor": { + "button": { + "style": { + "background-color": "Color" + } + }, "label-headers": "Headers", "label-url": "URL", "placeholder-url": "URL" @@ -5242,7 +5247,8 @@ "action-editor": { "button": { "confirm": "Confirm", - "confirm-action": "Confirm action" + "confirm-action": "Confirm action", + "style": "Button style" }, "inline": { "add-action": "Add action",