Last active
May 19, 2022 21:45
-
-
Save rodrigonehring/4d08c478abbd5e07a1f581e7bb1d7e8c to your computer and use it in GitHub Desktop.
mui/material v5 - confirmation dialog returns promise on open
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import { fireEvent, render, screen, waitFor } from '@tests/utils'; | |
import ConfirmationDialog, { useConfirmationDialog } from './ConfirmationDialog'; | |
function fakePromise() { | |
let resolve; | |
const promise = new Promise((promiseResolve) => { | |
resolve = promiseResolve; | |
}); | |
return { promise, resolve }; | |
} | |
function MockComponent({ action }) { | |
const [result, setResult] = React.useState(null); | |
const confirmSomething = useConfirmationDialog(); | |
const handleDelete = async () => { | |
const response = await confirmSomething.open(action); | |
setResult(response); | |
}; | |
if (result !== null) { | |
return <span>your result is: {result ? 'confirmed' : 'cancelled'}</span>; | |
} | |
return ( | |
<div> | |
<button type="button" onClick={handleDelete}> | |
delete everything | |
</button> | |
<ConfirmationDialog {...confirmSomething} title="have you finished" message="are you sure?" /> | |
</div> | |
); | |
} | |
const key = 'confirm dialog for have you finished'; | |
describe('<ConfirmationDialog />', () => { | |
it('renders properly and confirm', async () => { | |
render(<MockComponent />); | |
let dialog = screen.queryByLabelText(key); | |
expect(dialog).not.toBeInTheDocument(); | |
fireEvent.click(screen.getByText('delete everything')); | |
dialog = screen.queryByLabelText(key); | |
expect(dialog).toBeInTheDocument(); | |
fireEvent.click(screen.getByText('Confirm')); | |
await waitFor(() => { | |
expect(screen.getByText('your result is: confirmed')).toBeInTheDocument(); | |
}); | |
}); | |
it('renders properly and cancel', async () => { | |
render(<MockComponent />); | |
fireEvent.click(screen.getByText('delete everything')); | |
fireEvent.click(screen.getByText('Cancel')); | |
await waitFor(() => { | |
expect(screen.getByText('your result is: cancelled')).toBeInTheDocument(); | |
}); | |
}); | |
it('call action and put a loading', async () => { | |
let promise; | |
const action = () => { | |
promise = fakePromise(); | |
return promise.promise; | |
}; | |
render(<MockComponent action={action} />); | |
fireEvent.click(screen.getByText('delete everything')); | |
fireEvent.click(screen.getByText('Confirm')); | |
expect(screen.getByText('Confirm')).toBeDisabled(); | |
promise.resolve(); | |
await waitFor(() => { | |
expect(screen.queryByLabelText(key)).not.toBeInTheDocument(); | |
expect(screen.getByText('your result is: confirmed')).toBeInTheDocument(); | |
}); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState } from 'react'; | |
import { | |
Dialog, | |
Button, | |
DialogTitle, | |
DialogActions, | |
DialogContent, | |
Typography, | |
} from '@mui/material'; | |
import LoadingButton from '@mui/lab/LoadingButton'; | |
type Props = { | |
isOpen: boolean; | |
onResolve: (result: boolean) => void; | |
title: string; | |
message: React.ReactNode; | |
confirmText?: string; | |
cancelText?: string; | |
loading?: boolean; | |
}; | |
type Action = () => Promise<any>; | |
type State = { | |
resolvePromise?: (result: boolean) => void; | |
isOpen: boolean; | |
loading?: boolean; | |
action?: Action; | |
}; | |
const initialState: State = { isOpen: false }; | |
export function useConfirmationDialog() { | |
const [state, setState] = useState(initialState); | |
return { | |
isOpen: Boolean(state.isOpen), | |
loading: Boolean(state.loading), | |
async onResolve(result: boolean) { | |
if (state.action && result) { | |
try { | |
setState({ ...state, loading: true }); | |
await state.action(); | |
} catch (error) { | |
setState({ ...state, loading: false }); | |
} | |
} | |
if (state.resolvePromise) state.resolvePromise(result); | |
setState(initialState); | |
}, | |
open(action?: Action) { | |
return new Promise((resolve) => { | |
setState({ resolvePromise: resolve, isOpen: true, action }); | |
}); | |
}, | |
}; | |
} | |
export default function DialogWarning(props: Props) { | |
const { isOpen, onResolve, loading, title, message, confirmText, cancelText } = props; | |
return ( | |
<Dialog | |
open={isOpen} | |
onClose={() => !loading && onResolve(false)} | |
maxWidth="xs" | |
aria-label={`confirm dialog for ${title}`} | |
sx={{ '& .MuiPaper-root': { maxWidth: 480, px: 1.5, py: 2 } }} | |
> | |
<DialogTitle> | |
<Typography variant="h3" component="span"> | |
{title} | |
</Typography> | |
</DialogTitle> | |
<DialogContent>{message}</DialogContent> | |
<DialogActions> | |
<Button color="secondary" onClick={() => onResolve(false)} disabled={loading}> | |
{cancelText || 'Cancel'} | |
</Button> | |
<LoadingButton | |
color="secondary" | |
onClick={() => onResolve(true)} | |
loading={loading} | |
disabled={loading} | |
variant="contained" | |
> | |
{confirmText || 'Confirm'} | |
</LoadingButton> | |
</DialogActions> | |
</Dialog> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment