diff --git a/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.test.tsx b/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.test.tsx index 553714c16a3..1bcdc6a4a81 100644 --- a/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.test.tsx +++ b/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.test.tsx @@ -1,10 +1,23 @@ -import { render, screen } from '@testing-library/react'; +import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { ConfirmModal } from './ConfirmModal'; describe('ConfirmModal', () => { + const mockOnConfirm = jest.fn(); + + let user: ReturnType; + + beforeEach(() => { + jest.useFakeTimers(); + user = userEvent.setup({ delay: null }); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + it('should render correct title, body, dismiss-, alternative- and confirm-text', () => { render( { dismissText="Dismiss Text" isOpen={true} confirmationText="My confirmation text" - onConfirm={() => {}} + onConfirm={mockOnConfirm} onDismiss={() => {}} onAlternative={() => {}} /> @@ -85,7 +98,49 @@ describe('ConfirmModal', () => { expect(screen.getByRole('button', { name: 'Please Confirm' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Please Confirm' })).toBeDisabled(); - await userEvent.type(screen.getByPlaceholderText('Type "My confirmation text" to confirm'), 'mY CoNfIrMaTiOn TeXt'); - expect(screen.getByRole('button', { name: 'Please Confirm' })).not.toBeDisabled(); + await user.type(screen.getByPlaceholderText('Type "My confirmation text" to confirm'), 'mY CoNfIrMaTiOn TeXt'); + expect(screen.getByRole('button', { name: 'Please Confirm' })).toBeEnabled(); + + await user.click(screen.getByRole('button', { name: 'Please Confirm' })); + expect(mockOnConfirm).toHaveBeenCalled(); + }); + + it('returning a promise in the onConfirm callback disables the button whilst the callback is in progress', async () => { + mockOnConfirm.mockImplementation(() => { + return new Promise((resolve) => { + setTimeout(() => { + resolve(''); + }, 1000); + }); + }); + render( + {}} + onAlternative={() => {}} + /> + ); + + expect(screen.getByRole('button', { name: 'Please Confirm' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Please Confirm' })).toBeDisabled(); + + await user.type(screen.getByPlaceholderText('Type "My confirmation text" to confirm'), 'My confirmation text'); + expect(screen.getByRole('button', { name: 'Please Confirm' })).toBeEnabled(); + + await user.click(screen.getByRole('button', { name: 'Please Confirm' })); + expect(mockOnConfirm).toHaveBeenCalled(); + expect(screen.getByRole('button', { name: 'Please Confirm' })).toBeDisabled(); + + jest.runAllTimers(); + await waitFor(() => { + return expect(screen.getByRole('button', { name: 'Please Confirm' })).toBeEnabled(); + }); }); }); diff --git a/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.tsx b/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.tsx index d4c4bc995dd..ea077012a61 100644 --- a/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.tsx +++ b/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.tsx @@ -37,8 +37,10 @@ export interface ConfirmModalProps { alternativeText?: string; /** Confirm button variant */ confirmButtonVariant?: ButtonVariant; - /** Confirm action callback */ - onConfirm(): void; + /** Confirm action callback + * Return a promise to disable the confirm button until the promise is resolved + */ + onConfirm(): void | Promise; /** Dismiss action callback */ onDismiss(): void; /** Alternative action callback */ @@ -83,6 +85,15 @@ export const ConfirmModal = ({ } }, [isOpen, confirmationText]); + const onConfirmClick = async () => { + setDisabled(true); + try { + await onConfirm(); + } finally { + setDisabled(false); + } + }; + return (
@@ -102,7 +113,7 @@ export const ConfirmModal = ({ -