VizActions: Add confirmation message (#100012)

Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
This commit is contained in:
Adela Almasan
2025-02-07 16:58:09 -06:00
committed by GitHub
parent 378bb6ea3f
commit bce05cd48d
6 changed files with 109 additions and 11 deletions

View File

@@ -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<T = any> {
title: string;
onClick: (event: any, origin?: any) => void;
confirmation?: string;
}
interface FetchOptions {

View File

@@ -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<Field>;
@@ -10,9 +14,29 @@ type ActionButtonProps = ButtonProps & {
* @internal
*/
export function ActionButton({ action, ...buttonProps }: ActionButtonProps) {
const [showConfirm, setShowConfirm] = useState(false);
return (
<Button variant="primary" size="sm" onClick={action.onClick} {...buttonProps}>
{action.title}
</Button>
<>
<Button variant="primary" size="sm" onClick={() => setShowConfirm(true)} {...buttonProps}>
{action.title}
</Button>
{showConfirm && (
<ConfirmModal
isOpen={true}
title={t('grafana-ui.action-editor.button.confirm-action', 'Confirm action')}
body={action.confirmation}
confirmText={t('grafana-ui.action-editor.button.confirm', 'Confirm')}
confirmButtonVariant="primary"
onConfirm={() => {
setShowConfirm(false);
action.onClick(new MouseEvent('click'));
}}
onDismiss={() => {
setShowConfirm(false);
}}
/>
)}
</>
);
}

View File

@@ -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 (
<div className={styles.listItem}>
<Field label="Title">
<Field label={t('grafana-ui.action-editor.modal.action-title', 'Title')} className={styles.inputField}>
<SuggestionsInput
value={value.title}
onChange={onTitleChange}
suggestions={suggestions}
autoFocus={value.title === ''}
placeholder="Action title"
placeholder={t('grafana-ui.action-editor.modal.action-title-placeholder', 'Action title')}
/>
</Field>
<Field
label={t('grafana-ui.viz-tooltip.actions-confirmation-label', 'Confirmation message')}
description={t(
'grafana-ui.viz-tooltip.actions-confirmation-message',
'Provide a descriptive prompt to confirm or cancel the action.'
)}
className={styles.inputField}
>
<SuggestionsInput
value={value.confirmation}
onChange={onConfirmationChange}
suggestions={suggestions}
placeholder={t(
'grafana-ui.viz-tooltip.actions-confirmation-input-placeholder',
'Are you sure you want to {{ actionTitle }}?',
{ actionTitle: value.title || '... ' }
)}
/>
</Field>
<InlineFieldRow>
<InlineField label="Method" labelWidth={LABEL_WIDTH} grow={true}>
<InlineField
label={t('grafana-ui.action-editor.modal.action-method', 'Method')}
labelWidth={LABEL_WIDTH}
grow={true}
>
<RadioButtonGroup<HttpRequestMethod>
value={value?.fetch.method}
options={httpMethodOptions}
@@ -130,7 +159,10 @@ export const ActionEditor = memo(({ index, value, onChange, suggestions }: Actio
</InlineField>
</InlineFieldRow>
<Field label="Query parameters" className={styles.fieldGap}>
<Field
label={t('grafana-ui.action-editor.modal.action-query-params', 'Query parameters')}
className={styles.fieldGap}
>
<ParamsEditor value={value?.fetch.queryParams ?? []} onChange={onQueryParamsChange} suggestions={suggestions} />
</Field>
@@ -144,7 +176,7 @@ export const ActionEditor = memo(({ index, value, onChange, suggestions }: Actio
</Field>
{value?.fetch.method !== HttpRequestMethod.GET && (
<Field label="Body">
<Field label={t('grafana-ui.action-editor.modal.action-body', 'Body')} className={styles.inputField}>
<SuggestionsInput
value={value.fetch.body}
onChange={onBodyChange}
@@ -176,6 +208,9 @@ const getStyles = (theme: GrafanaTheme2) => ({
fieldGap: css({
marginTop: theme.spacing(2),
}),
inputField: css({
marginRight: 4,
}),
});
ActionEditor.displayName = 'ActionEditor';

View File

@@ -50,10 +50,15 @@ export const getActions = (
dataContext.value.calculatedValue = config.calculatedValue;
}
let actionModel: ActionModel<Field> = { 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<Field> = {
title,
confirmation,
onClick: (evt: MouseEvent, origin: Field) => {
buildActionOnClick(action, boundReplaceVariables);
},

View File

@@ -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></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}}"
}

View File

@@ -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></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}}"
}