core-components: handle overlapping display of transient alert messages with manual removal
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -247,6 +247,43 @@ describe('<AlertDisplay />', () => {
|
||||
expect(queryByText('transient message three')).not.toBeInTheDocument();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('renders 3 different messages with overlapping timeout and manual removal', 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();
|
||||
|
||||
// manually remove message 1
|
||||
fireEvent.click(screen.getByTestId('error-button-close'));
|
||||
expect(screen.getByText('transient message two')).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();
|
||||
|
||||
// 3s in, message 3 now shown
|
||||
act(() => jest.advanceTimersByTime(1500));
|
||||
expect(queryByText('transient message three')).toBeInTheDocument();
|
||||
|
||||
// 4s 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,7 +19,6 @@ 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';
|
||||
|
||||
@@ -63,7 +62,6 @@ 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' },
|
||||
@@ -81,24 +79,25 @@ export function AlertDisplay(props: AlertDisplayProps) {
|
||||
};
|
||||
}, [alertApi]);
|
||||
|
||||
const [firstMessage] = messages;
|
||||
|
||||
useEffect(() => {
|
||||
const [current] = messages;
|
||||
if (current && current.display === 'transient') {
|
||||
setTimeout(() => {
|
||||
if (isMounted()) {
|
||||
setMessages(msgs => msgs.filter(msg => msg !== current));
|
||||
}
|
||||
if (firstMessage && firstMessage.display === 'transient') {
|
||||
const timeout = setTimeout(() => {
|
||||
setMessages(msgs => {
|
||||
const newMsgs = msgs.filter(msg => msg !== firstMessage);
|
||||
return newMsgs.length === msgs.length ? msgs : newMsgs;
|
||||
});
|
||||
}, timeoutMs);
|
||||
return () => clearTimeout(timeout);
|
||||
}
|
||||
return undefined;
|
||||
}, [messages, timeoutMs, isMounted]);
|
||||
}, [firstMessage, timeoutMs]);
|
||||
|
||||
if (messages.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [firstMessage] = messages;
|
||||
|
||||
const handleClose = () => {
|
||||
setMessages(msgs => msgs.filter(msg => msg !== firstMessage));
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user