core-components: handle overlapping display of transient alert messages

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2022-12-05 14:22:44 +01:00
parent b0739c7e68
commit fe0d0a2ec7
2 changed files with 40 additions and 4 deletions
@@ -214,6 +214,39 @@ describe('<AlertDisplay />', () => {
expect(queryByText('transient message')).not.toBeInTheDocument();
jest.useRealTimers();
});
it('renders 3 different messages with overlapping timeout', async () => {
jest.useFakeTimers();
const { queryByText } = await renderInTestApp(
<TestApiProvider apis={apis}>
<AlertDisplay transientTimeoutMs={1500} />
</TestApiProvider>,
);
// 3 messages stacked with 1.5s each, display times: 0-1.5, 1.5-3, 3-4.5
// 0s in, only message 1
expect(queryByText('transient message one')).toBeInTheDocument();
// 1s in, message 1 still shown, message 2 added in background
act(() => jest.advanceTimersByTime(1000));
expect(queryByText('transient message one')).toBeInTheDocument();
expect(queryByText('(1 older message)')).toBeInTheDocument();
// 2s in, message 2 now shown, message 3 added
act(() => jest.advanceTimersByTime(1000));
expect(queryByText('transient message two')).toBeInTheDocument();
expect(queryByText('(1 older message)')).toBeInTheDocument();
// 3.5s in, message 3 now shown
act(() => jest.advanceTimersByTime(1500));
expect(queryByText('transient message three')).toBeInTheDocument();
// 5s in, all messages gone
act(() => jest.advanceTimersByTime(1500));
expect(queryByText('transient message three')).not.toBeInTheDocument();
jest.useRealTimers();
});
});
describe('with multiple messages of mixed display', () => {
@@ -19,6 +19,7 @@ import Snackbar from '@material-ui/core/Snackbar';
import Typography from '@material-ui/core/Typography';
import CloseIcon from '@material-ui/icons/Close';
import { Alert } from '@material-ui/lab';
import { useIsMounted } from '@react-hookz/web';
import pluralize from 'pluralize';
import React, { useEffect, useState } from 'react';
@@ -62,6 +63,7 @@ export type AlertDisplayProps = {
export function AlertDisplay(props: AlertDisplayProps) {
const [messages, setMessages] = useState<Array<AlertMessage>>([]);
const alertApi = useApi(alertApiRef);
const isMounted = useIsMounted();
const {
anchorOrigin = { vertical: 'top', horizontal: 'center' },
@@ -82,13 +84,14 @@ export function AlertDisplay(props: AlertDisplayProps) {
useEffect(() => {
const [current] = messages;
if (current && current.display === 'transient') {
const timer = setTimeout(() => {
setMessages(msgs => msgs.filter(msg => msg !== current));
setTimeout(() => {
if (isMounted()) {
setMessages(msgs => msgs.filter(msg => msg !== current));
}
}, timeoutMs);
return () => clearTimeout(timer);
}
return undefined;
}, [messages, timeoutMs]);
}, [messages, timeoutMs, isMounted]);
if (messages.length === 0) {
return null;