diff --git a/resources/assets/src/components/Toast.tsx b/resources/assets/src/components/Toast.tsx index 356195fc..2a2e28ca 100644 --- a/resources/assets/src/components/Toast.tsx +++ b/resources/assets/src/components/Toast.tsx @@ -33,14 +33,10 @@ const Toast: React.FC = (props) => { const [show, setShow] = useState(false) useEffect(() => { - const id1 = setTimeout(() => setShow(true), 100) - const id2 = setTimeout(() => setShow(false), 3000) - const id3 = setTimeout(props.onClose, 3100) + const timer = setTimeout(() => setShow(true), 100) return () => { - clearTimeout(id1) - clearTimeout(id2) - clearTimeout(id3) + clearTimeout(timer) } }, [props.onClose]) diff --git a/resources/assets/src/scripts/toast.tsx b/resources/assets/src/scripts/toast.tsx index c9e362fb..48127641 100644 --- a/resources/assets/src/scripts/toast.tsx +++ b/resources/assets/src/scripts/toast.tsx @@ -13,12 +13,20 @@ const CLEAR_EVENT = Symbol('clear') export const ToastContainer: React.FC = () => { const [queue, setQueue] = useState([]) + const handleClose = (id: string) => { + setQueue((queue) => queue.filter((el) => el.id !== id)) + } + useEffect(() => { const off1 = emitter.on(TOAST_EVENT, (toast: QueueElement) => { setQueue((queue) => { queue.push(toast) return queue.slice() }) + + setTimeout(() => { + handleClose(toast.id) + }, 3100) }) const off2 = emitter.on(CLEAR_EVENT, () => setQueue([])) @@ -28,12 +36,9 @@ export const ToastContainer: React.FC = () => { } }, []) - const handleClose = (id: string) => { - setQueue((queue) => queue.filter((el) => el.id !== id)) - } - return ( <> + {queue.length} {queue.map((el, i) => ( { } export class Toast { - constructor() { - const container = document.createElement('div') - document.body.appendChild(container) + private container: HTMLDivElement - ReactDOM.render(, container) + constructor() { + this.container = document.createElement('div') + document.body.appendChild(this.container) + + /* istanbul ignore next */ + if (process.env.NODE_ENV !== 'test') { + ReactDOM.render(, this.container) + } } success(message: string) { @@ -75,4 +85,12 @@ export class Toast { clear() { emitter.emit(CLEAR_EVENT) } + + dispose() { + /* istanbul ignore next */ + if (process.env.NODE_ENV !== 'test') { + ReactDOM.unmountComponentAtNode(this.container) + } + this.container.remove() + } } diff --git a/resources/assets/tests/scripts/toast.test.tsx b/resources/assets/tests/scripts/toast.test.tsx index ea6a16da..8fe28703 100644 --- a/resources/assets/tests/scripts/toast.test.tsx +++ b/resources/assets/tests/scripts/toast.test.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { render } from '@testing-library/react' +import { render, fireEvent, screen } from '@testing-library/react' import { Toast, ToastContainer } from '@/scripts/toast' test('"Toast" class', () => { @@ -25,6 +25,7 @@ test('"Toast" class', () => { ) jest.runAllTimers() + toast.dispose() }) test('clear toasts', () => { @@ -36,4 +37,17 @@ test('clear toasts', () => { toast.clear() expect(document.querySelectorAll('.alert')).toHaveLength(0) + + toast.dispose() +}) + +test('close toast manually', () => { + render() + const toast = new Toast() + toast.success('success') + + fireEvent.click(screen.getByText('×')) + expect(document.querySelectorAll('.alert')).toHaveLength(0) + + toast.dispose() })