diff --git a/packages/grafana-data/src/types/action.ts b/packages/grafana-data/src/types/action.ts index a3cb099ebe8..bbdfd91b4f5 100644 --- a/packages/grafana-data/src/types/action.ts +++ b/packages/grafana-data/src/types/action.ts @@ -15,6 +15,7 @@ export interface Action { // Currently this is required because there is only one valid type (fetch) // once multiple types are valid, usage of this will need to be optional [ActionType.Fetch]: FetchOptions; + confirmation?: string; } /** @@ -23,6 +24,7 @@ export interface Action { export interface ActionModel { title: string; onClick: (event: any, origin?: any) => void; + confirmation?: string; } interface FetchOptions { diff --git a/packages/grafana-ui/src/components/Actions/ActionButton.tsx b/packages/grafana-ui/src/components/Actions/ActionButton.tsx index 49b3c82c5c9..ed023601c1f 100644 --- a/packages/grafana-ui/src/components/Actions/ActionButton.tsx +++ b/packages/grafana-ui/src/components/Actions/ActionButton.tsx @@ -1,6 +1,10 @@ +import { useState } from 'react'; + import { ActionModel, Field } from '@grafana/data'; +import { t } from '../../utils/i18n'; import { Button, ButtonProps } from '../Button'; +import { ConfirmModal } from '../ConfirmModal/ConfirmModal'; type ActionButtonProps = ButtonProps & { action: ActionModel; @@ -10,9 +14,29 @@ type ActionButtonProps = ButtonProps & { * @internal */ export function ActionButton({ action, ...buttonProps }: ActionButtonProps) { + const [showConfirm, setShowConfirm] = useState(false); + return ( - + <> + + {showConfirm && ( + { + setShowConfirm(false); + action.onClick(new MouseEvent('click')); + }} + onDismiss={() => { + setShowConfirm(false); + }} + /> + )} + ); } diff --git a/public/app/features/actions/ActionEditor.tsx b/public/app/features/actions/ActionEditor.tsx index fc7b006a8fc..bc8edcddd1d 100644 --- a/public/app/features/actions/ActionEditor.tsx +++ b/public/app/features/actions/ActionEditor.tsx @@ -8,6 +8,7 @@ import { InlineFieldRow } from '@grafana/ui/src/components/Forms/InlineFieldRow' import { RadioButtonGroup } from '@grafana/ui/src/components/Forms/RadioButtonGroup/RadioButtonGroup'; import { JSONFormatter } from '@grafana/ui/src/components/JSONFormatter/JSONFormatter'; import { useStyles2 } from '@grafana/ui/src/themes'; +import { t } from '@grafana/ui/src/utils/i18n'; import { HTMLElementType, SuggestionsInput } from '../transformers/suggestionsInput/SuggestionsInput'; @@ -29,6 +30,10 @@ export const ActionEditor = memo(({ index, value, onChange, suggestions }: Actio onChange(index, { ...value, title }); }; + const onConfirmationChange = (confirmation: string) => { + onChange(index, { ...value, confirmation }); + }; + const onUrlChange = (url: string) => { onChange(index, { ...value, @@ -98,18 +103,42 @@ export const ActionEditor = memo(({ index, value, onChange, suggestions }: Actio return (
- + + + + + - + value={value?.fetch.method} options={httpMethodOptions} @@ -130,7 +159,10 @@ export const ActionEditor = memo(({ index, value, onChange, suggestions }: Actio - + @@ -144,7 +176,7 @@ export const ActionEditor = memo(({ index, value, onChange, suggestions }: Actio {value?.fetch.method !== HttpRequestMethod.GET && ( - + ({ fieldGap: css({ marginTop: theme.spacing(2), }), + inputField: css({ + marginRight: 4, + }), }); ActionEditor.displayName = 'ActionEditor'; diff --git a/public/app/features/actions/utils.ts b/public/app/features/actions/utils.ts index f33fa82eb9a..29751c4f4e3 100644 --- a/public/app/features/actions/utils.ts +++ b/public/app/features/actions/utils.ts @@ -50,10 +50,15 @@ export const getActions = ( dataContext.value.calculatedValue = config.calculatedValue; } - let actionModel: ActionModel = { title: '', onClick: (e) => {} }; + const title = replaceVariables(action.title, actionScopedVars); + const confirmation = replaceVariables( + action.confirmation || `Are you sure you want to ${action.title}?`, + actionScopedVars + ); - actionModel = { - title: replaceVariables(action.title || '', actionScopedVars), + const actionModel: ActionModel = { + title, + confirmation, onClick: (evt: MouseEvent, origin: Field) => { buildActionOnClick(action, boundReplaceVariables); }, diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index a313559321e..c40a820b16d 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -1569,6 +1569,19 @@ "send-custom-feedback": "Send" }, "grafana-ui": { + "action-editor": { + "button": { + "confirm": "Confirm", + "confirm-action": "Confirm action" + }, + "modal": { + "action-body": "Body", + "action-method": "Method", + "action-query-params": "Query parameters", + "action-title": "Title", + "action-title-placeholder": "Action title" + } + }, "auto-save-field": { "saved": "Saved!", "saving": "Saving <1>" @@ -1676,6 +1689,9 @@ "right-axis-indicator": "(right y-axis)" }, "viz-tooltip": { + "actions-confirmation-input-placeholder": "Are you sure you want to {{ actionTitle }}?", + "actions-confirmation-label": "Confirmation message", + "actions-confirmation-message": "Provide a descriptive prompt to confirm or cancel the action.", "footer-add-annotation": "Add annotation", "footer-click-to-navigate": "Click to open {{linkTitle}}" } diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index 69a34972f6b..f67fd6afb55 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -1569,6 +1569,19 @@ "send-custom-feedback": "Ŝęʼnđ" }, "grafana-ui": { + "action-editor": { + "button": { + "confirm": "Cőʼnƒįřm", + "confirm-action": "Cőʼnƒįřm äčŧįőʼn" + }, + "modal": { + "action-body": "ßőđy", + "action-method": "Męŧĥőđ", + "action-query-params": "Qūęřy päřämęŧęřş", + "action-title": "Ŧįŧľę", + "action-title-placeholder": "Åčŧįőʼn ŧįŧľę" + } + }, "auto-save-field": { "saved": "Ŝävęđ!", "saving": "Ŝävįʼnģ <1>" @@ -1676,6 +1689,9 @@ "right-axis-indicator": "(řįģĥŧ y-äχįş)" }, "viz-tooltip": { + "actions-confirmation-input-placeholder": "Åřę yőū şūřę yőū ŵäʼnŧ ŧő {{ actionTitle }}?", + "actions-confirmation-label": "Cőʼnƒįřmäŧįőʼn męşşäģę", + "actions-confirmation-message": "Přővįđę ä đęşčřįpŧįvę přőmpŧ ŧő čőʼnƒįřm őř čäʼnčęľ ŧĥę äčŧįőʼn.", "footer-add-annotation": "Åđđ äʼnʼnőŧäŧįőʼn", "footer-click-to-navigate": "Cľįčĸ ŧő őpęʼn {{linkTitle}}" }