diff --git a/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.story.tsx b/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.story.tsx index 7cd7f0a5bee..4cde4935239 100644 --- a/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.story.tsx +++ b/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.story.tsx @@ -4,7 +4,9 @@ import { action } from '@storybook/addon-actions'; import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; import { ConfirmModal } from '@grafana/ui'; import mdx from './ConfirmModal.mdx'; -import { Props } from './ConfirmModal'; +import { ConfirmModalProps } from './ConfirmModal'; + +const defaultExcludes = ['onConfirm', 'onDismiss', 'onAlternative']; export default { title: 'Overlays/ConfirmModal', @@ -18,11 +20,13 @@ export default { disable: true, }, controls: { - exclude: ['isOpen', 'body'], + exclude: defaultExcludes, }, }, argTypes: { - icon: { control: { type: 'select', options: ['exclamation-triangle', 'power', 'cog', 'lock'] } }, + icon: { control: { type: 'select', options: ['exclamation-triangle', 'power', 'cog', 'lock', 'trash-alt'] } }, + body: { control: { type: 'text' } }, + description: { control: { type: 'text' } }, }, } as Meta; @@ -33,20 +37,27 @@ const defaultActions = { onDismiss: () => { action('Dismiss')('close'); }, + onAlternative: () => { + action('Alternative')('alternative'); + }, }; -interface StoryProps extends Props { - visible: boolean; - bodyText: string; -} - -export const Basic: Story = ({ title, bodyText, confirmText, dismissText, icon, visible }) => { +export const Basic: Story = ({ + title, + body, + description, + confirmText, + dismissText, + icon, + isOpen, +}) => { const { onConfirm, onDismiss } = defaultActions; return ( = ({ title, bodyText, confirmText, dismiss ); }; +Basic.parameters = { + controls: { + exclude: [...defaultExcludes, 'alternativeText', 'confirmationText'], + }, +}; + Basic.args = { title: 'Delete user', - bodyText: 'Are you sure you want to delete this user?', + body: 'Are you sure you want to delete this user?', + description: 'Removing the user will not remove any dashboards the user has created', confirmText: 'Delete', dismissText: 'Cancel', icon: 'exclamation-triangle', - visible: true, + isOpen: true, +}; + +export const AlternativeAction: Story = ({ + title, + body, + description, + confirmText, + dismissText, + icon, + alternativeText, + isOpen, +}) => { + const { onConfirm, onDismiss, onAlternative } = defaultActions; + return ( + + ); +}; + +AlternativeAction.parameters = { + controls: { + exclude: [...defaultExcludes, 'confirmationText'], + }, +}; + +AlternativeAction.args = { + title: 'Delete row', + body: 'Are you sure you want to remove this row and all its panels?', + alternativeText: 'Delete row only', + confirmText: 'Yes', + dismissText: 'Cancel', + icon: 'trash-alt', + isOpen: true, +}; + +export const WithConfirmation: Story = ({ + title, + body, + description, + confirmationText, + confirmText, + dismissText, + icon, + isOpen, +}) => { + const { onConfirm, onDismiss } = defaultActions; + return ( + + ); +}; + +WithConfirmation.parameters = { + controls: { + exclude: [...defaultExcludes, 'alternativeText'], + }, +}; + +WithConfirmation.args = { + title: 'Delete', + body: 'Do you want to delete this notification channel?', + description: 'Deleting this notification channel will not delete from alerts any references to it', + confirmationText: 'Delete', + confirmText: 'Delete', + dismissText: 'Cancel', + icon: 'trash-alt', + isOpen: true, }; diff --git a/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.tsx b/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.tsx index aced1b6bffb..fcac981707c 100644 --- a/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.tsx +++ b/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.tsx @@ -1,50 +1,86 @@ -import React, { FC, useContext } from 'react'; +import React, { useState } from 'react'; import { css } from '@emotion/css'; import { Modal } from '../Modal/Modal'; import { IconName } from '../../types/icon'; import { Button } from '../Button'; -import { stylesFactory, ThemeContext } from '../../themes'; +import { useStyles } from '../../themes'; import { GrafanaTheme } from '@grafana/data'; -import { HorizontalGroup } from '..'; +import { HorizontalGroup, Input } from '..'; +import { selectors } from '@grafana/e2e-selectors'; -export interface Props { +export interface ConfirmModalProps { /** Toggle modal's open/closed state */ isOpen: boolean; /** Title for the modal header */ title: string; /** Modal content */ body: React.ReactNode; + /** Modal description */ + description?: React.ReactNode; /** Text for confirm button */ confirmText: string; /** Text for dismiss button */ dismissText?: string; /** Icon for the modal header */ icon?: IconName; + /** Text user needs to fill in before confirming */ + confirmationText?: string; + /** Text for alternative button */ + alternativeText?: string; /** Confirm action callback */ onConfirm(): void; /** Dismiss action callback */ onDismiss(): void; + /** Alternative action callback */ + onAlternative?(): void; } -export const ConfirmModal: FC = ({ +export const ConfirmModal = ({ isOpen, title, body, + description, confirmText, + confirmationText, dismissText = 'Cancel', + alternativeText, icon = 'exclamation-triangle', onConfirm, onDismiss, -}) => { - const theme = useContext(ThemeContext); - const styles = getStyles(theme); + onAlternative, +}: ConfirmModalProps): JSX.Element => { + const [disabled, setDisabled] = useState(Boolean(confirmationText)); + const styles = useStyles(getStyles); + const onConfirmationTextChange = (event: React.FormEvent) => { + setDisabled(confirmationText?.localeCompare(event.currentTarget.value) !== 0); + }; return (
-
{body}
+
+ {body} + {description ?
{description}
: null} + {confirmationText ? ( +
+ + + +
+ ) : null} +
- + ) : null} +