@transferwise/components 46.166.0 → 46.168.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/build/CardContainer/CardContainer.js +34 -0
- package/build/CardContainer/CardContainer.js.map +1 -0
- package/build/CardContainer/CardContainer.mjs +30 -0
- package/build/CardContainer/CardContainer.mjs.map +1 -0
- package/build/CardWidget/CardWidget.js +98 -0
- package/build/CardWidget/CardWidget.js.map +1 -0
- package/build/CardWidget/CardWidget.mjs +94 -0
- package/build/CardWidget/CardWidget.mjs.map +1 -0
- package/build/CardWidget/CardWidgetAction.js +73 -0
- package/build/CardWidget/CardWidgetAction.js.map +1 -0
- package/build/CardWidget/CardWidgetAction.mjs +68 -0
- package/build/CardWidget/CardWidgetAction.mjs.map +1 -0
- package/build/Link/Link.js.map +1 -1
- package/build/Link/Link.mjs.map +1 -1
- package/build/ListItem/ListItem.js +2 -0
- package/build/ListItem/ListItem.js.map +1 -1
- package/build/ListItem/ListItem.mjs +2 -0
- package/build/ListItem/ListItem.mjs.map +1 -1
- package/build/Loader/Loader.js.map +1 -1
- package/build/Loader/Loader.mjs.map +1 -1
- package/build/Prompt/PrimitivePrompt/PrimitivePrompt.js +2 -1
- package/build/Prompt/PrimitivePrompt/PrimitivePrompt.js.map +1 -1
- package/build/Prompt/PrimitivePrompt/PrimitivePrompt.mjs +2 -1
- package/build/Prompt/PrimitivePrompt/PrimitivePrompt.mjs.map +1 -1
- package/build/Prompt/Snackbar/Snackbar.js +205 -0
- package/build/Prompt/Snackbar/Snackbar.js.map +1 -0
- package/build/Prompt/Snackbar/Snackbar.messages.js +14 -0
- package/build/Prompt/Snackbar/Snackbar.messages.js.map +1 -0
- package/build/Prompt/Snackbar/Snackbar.messages.mjs +10 -0
- package/build/Prompt/Snackbar/Snackbar.messages.mjs.map +1 -0
- package/build/Prompt/Snackbar/Snackbar.mjs +203 -0
- package/build/Prompt/Snackbar/Snackbar.mjs.map +1 -0
- package/build/Prompt/Snackbar/SnackbarContext.js.map +1 -0
- package/build/Prompt/Snackbar/SnackbarContext.mjs.map +1 -0
- package/build/Prompt/Snackbar/SnackbarPortal.js +12 -0
- package/build/Prompt/Snackbar/SnackbarPortal.js.map +1 -0
- package/build/Prompt/Snackbar/SnackbarPortal.mjs +8 -0
- package/build/Prompt/Snackbar/SnackbarPortal.mjs.map +1 -0
- package/build/Prompt/Snackbar/SnackbarProvider.js +42 -0
- package/build/Prompt/Snackbar/SnackbarProvider.js.map +1 -0
- package/build/Prompt/Snackbar/SnackbarProvider.mjs +38 -0
- package/build/Prompt/Snackbar/SnackbarProvider.mjs.map +1 -0
- package/build/Prompt/Snackbar/constants.js +17 -0
- package/build/Prompt/Snackbar/constants.js.map +1 -0
- package/build/Prompt/Snackbar/constants.mjs +11 -0
- package/build/Prompt/Snackbar/constants.mjs.map +1 -0
- package/build/Prompt/Snackbar/useSnackbar.js.map +1 -0
- package/build/Prompt/Snackbar/useSnackbar.mjs.map +1 -0
- package/build/Prompt/Snackbar/useSnackbarAutoDismiss.js +66 -0
- package/build/Prompt/Snackbar/useSnackbarAutoDismiss.js.map +1 -0
- package/build/Prompt/Snackbar/useSnackbarAutoDismiss.mjs +62 -0
- package/build/Prompt/Snackbar/useSnackbarAutoDismiss.mjs.map +1 -0
- package/build/Prompt/Snackbar/useSnackbarManager.js +153 -0
- package/build/Prompt/Snackbar/useSnackbarManager.js.map +1 -0
- package/build/Prompt/Snackbar/useSnackbarManager.mjs +149 -0
- package/build/Prompt/Snackbar/useSnackbarManager.mjs.map +1 -0
- package/build/Prompt/Snackbar/useSnackbarSwipeDismiss.js +135 -0
- package/build/Prompt/Snackbar/useSnackbarSwipeDismiss.js.map +1 -0
- package/build/Prompt/Snackbar/useSnackbarSwipeDismiss.mjs +131 -0
- package/build/Prompt/Snackbar/useSnackbarSwipeDismiss.mjs.map +1 -0
- package/build/Section/Section.js.map +1 -1
- package/build/Section/Section.mjs.map +1 -1
- package/build/_deprecated/Select/Select.js +1 -1
- package/build/_deprecated/Select/Select.js.map +1 -1
- package/build/_deprecated/Select/Select.mjs +1 -1
- package/build/_deprecated/Select/Select.mjs.map +1 -1
- package/build/common/BottomSheet/BottomSheet.js +1 -3
- package/build/common/BottomSheet/BottomSheet.js.map +1 -1
- package/build/common/BottomSheet/BottomSheet.mjs +1 -3
- package/build/common/BottomSheet/BottomSheet.mjs.map +1 -1
- package/build/i18n/en.json +1 -0
- package/build/i18n/en.json.js +1 -0
- package/build/i18n/en.json.js.map +1 -1
- package/build/i18n/en.json.mjs +1 -0
- package/build/i18n/en.json.mjs.map +1 -1
- package/build/index.js +7 -3
- package/build/index.js.map +1 -1
- package/build/index.mjs +5 -3
- package/build/index.mjs.map +1 -1
- package/build/main.css +331 -75
- package/build/styles/CardContainer/CardContainer.css +108 -0
- package/build/styles/CardWidget/CardWidget.css +78 -0
- package/build/styles/IconButton/IconButton.css +3 -4
- package/build/styles/ListItem/ListItem.css +8 -0
- package/build/styles/Prompt/Snackbar/Snackbar.css +134 -0
- package/build/styles/main.css +331 -75
- package/build/types/Body/Body.d.ts +2 -2
- package/build/types/CardContainer/CardContainer.d.ts +8 -0
- package/build/types/CardContainer/CardContainer.d.ts.map +1 -0
- package/build/types/CardContainer/CardContainer.types.d.ts +55 -0
- package/build/types/CardContainer/CardContainer.types.d.ts.map +1 -0
- package/build/types/CardContainer/_stories/helpers.d.ts +3 -0
- package/build/types/CardContainer/_stories/helpers.d.ts.map +1 -0
- package/build/types/CardContainer/index.d.ts +3 -0
- package/build/types/CardContainer/index.d.ts.map +1 -0
- package/build/types/CardWidget/CardWidget.d.ts +10 -0
- package/build/types/CardWidget/CardWidget.d.ts.map +1 -0
- package/build/types/CardWidget/CardWidget.types.d.ts +55 -0
- package/build/types/CardWidget/CardWidget.types.d.ts.map +1 -0
- package/build/types/CardWidget/CardWidgetAction.d.ts +54 -0
- package/build/types/CardWidget/CardWidgetAction.d.ts.map +1 -0
- package/build/types/CardWidget/index.d.ts +4 -0
- package/build/types/CardWidget/index.d.ts.map +1 -0
- package/build/types/IconButton/IconButton.d.ts +1 -1
- package/build/types/Link/Link.d.ts +1 -1
- package/build/types/ListItem/ListItem.d.ts +1 -1
- package/build/types/ListItem/ListItem.d.ts.map +1 -1
- package/build/types/Loader/Loader.d.ts +1 -1
- package/build/types/MoneyInput/MoneyInput.d.ts +1 -1
- package/build/types/MoneyInput/MoneyInput.d.ts.map +1 -1
- package/build/types/Prompt/PrimitivePrompt/PrimitivePrompt.d.ts +11 -0
- package/build/types/Prompt/PrimitivePrompt/PrimitivePrompt.d.ts.map +1 -1
- package/build/types/Prompt/Snackbar/Snackbar.d.ts +62 -0
- package/build/types/Prompt/Snackbar/Snackbar.d.ts.map +1 -0
- package/build/types/Prompt/Snackbar/Snackbar.messages.d.ts +9 -0
- package/build/types/Prompt/Snackbar/Snackbar.messages.d.ts.map +1 -0
- package/build/types/Prompt/Snackbar/Snackbar.types.d.ts +12 -0
- package/build/types/Prompt/Snackbar/Snackbar.types.d.ts.map +1 -0
- package/build/types/{Snackbar → Prompt/Snackbar}/SnackbarContext.d.ts +2 -2
- package/build/types/Prompt/Snackbar/SnackbarContext.d.ts.map +1 -0
- package/build/types/Prompt/Snackbar/SnackbarPortal.d.ts +4 -0
- package/build/types/Prompt/Snackbar/SnackbarPortal.d.ts.map +1 -0
- package/build/types/Prompt/Snackbar/SnackbarProvider.d.ts +13 -0
- package/build/types/Prompt/Snackbar/SnackbarProvider.d.ts.map +1 -0
- package/build/types/Prompt/Snackbar/constants.d.ts +9 -0
- package/build/types/Prompt/Snackbar/constants.d.ts.map +1 -0
- package/build/types/{Snackbar → Prompt/Snackbar}/index.d.ts +1 -1
- package/build/types/Prompt/Snackbar/index.d.ts.map +1 -0
- package/build/types/Prompt/Snackbar/useSnackbar.d.ts +3 -0
- package/build/types/Prompt/Snackbar/useSnackbar.d.ts.map +1 -0
- package/build/types/Prompt/Snackbar/useSnackbarAutoDismiss.d.ts +13 -0
- package/build/types/Prompt/Snackbar/useSnackbarAutoDismiss.d.ts.map +1 -0
- package/build/types/Prompt/Snackbar/useSnackbarManager.d.ts +20 -0
- package/build/types/Prompt/Snackbar/useSnackbarManager.d.ts.map +1 -0
- package/build/types/Prompt/Snackbar/useSnackbarSwipeDismiss.d.ts +22 -0
- package/build/types/Prompt/Snackbar/useSnackbarSwipeDismiss.d.ts.map +1 -0
- package/build/types/Section/Section.d.ts +1 -1
- package/build/types/Title/Title.d.ts +2 -2
- package/build/types/Upload/Steps/UploadImageStep/UploadImageStep.d.ts +1 -1
- package/build/types/UploadInput/UploadItem/UploadItemLink.d.ts +1 -1
- package/build/types/_deprecated/Select/Select.d.ts +1 -1
- package/build/types/common/BottomSheet/BottomSheet.d.ts +1 -3
- package/build/types/common/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/build/types/index.d.ts +9 -5
- package/build/types/index.d.ts.map +1 -1
- package/build/types/test-utils/index.d.ts +2 -0
- package/build/types/test-utils/index.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/CardContainer/CardContainer.css +108 -0
- package/src/CardContainer/CardContainer.less +171 -0
- package/src/CardContainer/CardContainer.test.tsx +150 -0
- package/src/CardContainer/CardContainer.tsx +51 -0
- package/src/CardContainer/CardContainer.types.ts +62 -0
- package/src/CardContainer/_stories/CardContainer.story.tsx +167 -0
- package/src/CardContainer/_stories/CardContainer.test.story.tsx +103 -0
- package/src/CardContainer/_stories/helpers.tsx +50 -0
- package/src/CardContainer/index.ts +6 -0
- package/src/CardWidget/CardWidget.css +78 -0
- package/src/CardWidget/CardWidget.less +107 -0
- package/src/CardWidget/CardWidget.test.tsx +221 -0
- package/src/CardWidget/CardWidget.tsx +78 -0
- package/src/CardWidget/CardWidget.types.ts +63 -0
- package/src/CardWidget/CardWidgetAction.tsx +128 -0
- package/src/CardWidget/_stories/CardWidget.story.tsx +238 -0
- package/src/CardWidget/_stories/CardWidget.test.story.tsx +350 -0
- package/src/CardWidget/index.ts +3 -0
- package/src/IconButton/IconButton.css +3 -4
- package/src/IconButton/IconButton.less +14 -1
- package/src/Link/Link.tsx +1 -1
- package/src/ListItem/AdditionalInfo/_stories/ListItemAdditionalInfo.story.tsx +1 -1
- package/src/ListItem/AvatarView/_stories/ListItemAvatarView.story.tsx +3 -3
- package/src/ListItem/ListItem.css +8 -0
- package/src/ListItem/ListItem.less +8 -0
- package/src/ListItem/ListItem.test.tsx +20 -0
- package/src/ListItem/ListItem.tsx +3 -1
- package/src/ListItem/_stories/ListItem.disabled.story.tsx +1 -1
- package/src/ListItem/_stories/ListItem.layout.test.story.tsx +1 -1
- package/src/ListItem/_stories/ListItem.scenarios.story.tsx +2 -2
- package/src/ListItem/_stories/ListItem.story.tsx +1 -1
- package/src/Loader/Loader.tsx +1 -1
- package/src/Prompt/CriticalBanner/_stories/CriticalBanner.docs.mdx +122 -0
- package/src/Prompt/CriticalBanner/_stories/CriticalBanner.story.tsx +0 -2
- package/src/Prompt/PrimitivePrompt/PrimitivePrompt.test.tsx +24 -1
- package/src/Prompt/PrimitivePrompt/PrimitivePrompt.tsx +8 -2
- package/src/Prompt/Snackbar/Snackbar.css +134 -0
- package/src/Prompt/Snackbar/Snackbar.less +159 -0
- package/src/Prompt/Snackbar/Snackbar.messages.ts +9 -0
- package/src/Prompt/Snackbar/Snackbar.test.tsx +1079 -0
- package/src/Prompt/Snackbar/Snackbar.tsx +245 -0
- package/src/Prompt/Snackbar/Snackbar.types.ts +17 -0
- package/src/{Snackbar → Prompt/Snackbar}/SnackbarContext.ts +2 -2
- package/src/Prompt/Snackbar/SnackbarPortal.tsx +8 -0
- package/src/Prompt/Snackbar/SnackbarProvider.test.tsx +783 -0
- package/src/Prompt/Snackbar/SnackbarProvider.tsx +41 -0
- package/src/Prompt/Snackbar/_stories/Snackbar.accessibility.docs.mdx +58 -0
- package/src/{Snackbar → Prompt/Snackbar}/_stories/Snackbar.rtl.test.story.tsx +17 -9
- package/src/Prompt/Snackbar/_stories/Snackbar.story.tsx +416 -0
- package/src/Prompt/Snackbar/_stories/Snackbar.test.story.tsx +263 -0
- package/src/Prompt/Snackbar/constants.ts +10 -0
- package/src/{Snackbar → Prompt/Snackbar}/index.ts +1 -1
- package/src/Prompt/Snackbar/useSnackbarAutoDismiss.ts +74 -0
- package/src/Prompt/Snackbar/useSnackbarManager.ts +169 -0
- package/src/Prompt/Snackbar/useSnackbarSwipeDismiss.ts +169 -0
- package/src/Section/Section.tsx +1 -1
- package/src/_deprecated/Select/Select.tsx +1 -1
- package/src/_deprecated/Select/_stories/Select.story.tsx +1 -1
- package/src/common/BottomSheet/BottomSheet.tsx +1 -3
- package/src/i18n/en.json +1 -0
- package/src/index.ts +18 -5
- package/src/main.css +331 -75
- package/src/main.less +3 -1
- package/build/Snackbar/Snackbar.js +0 -128
- package/build/Snackbar/Snackbar.js.map +0 -1
- package/build/Snackbar/Snackbar.mjs +0 -123
- package/build/Snackbar/Snackbar.mjs.map +0 -1
- package/build/Snackbar/SnackbarContext.js.map +0 -1
- package/build/Snackbar/SnackbarContext.mjs.map +0 -1
- package/build/Snackbar/SnackbarProvider.js +0 -46
- package/build/Snackbar/SnackbarProvider.js.map +0 -1
- package/build/Snackbar/SnackbarProvider.mjs +0 -42
- package/build/Snackbar/SnackbarProvider.mjs.map +0 -1
- package/build/Snackbar/useSnackbar.js.map +0 -1
- package/build/Snackbar/useSnackbar.mjs.map +0 -1
- package/build/styles/Snackbar/Snackbar.css +0 -71
- package/build/types/Snackbar/Snackbar.d.ts +0 -16
- package/build/types/Snackbar/Snackbar.d.ts.map +0 -1
- package/build/types/Snackbar/SnackbarContext.d.ts.map +0 -1
- package/build/types/Snackbar/SnackbarProvider.d.ts +0 -6
- package/build/types/Snackbar/SnackbarProvider.d.ts.map +0 -1
- package/build/types/Snackbar/index.d.ts.map +0 -1
- package/build/types/Snackbar/useSnackbar.d.ts +0 -3
- package/build/types/Snackbar/useSnackbar.d.ts.map +0 -1
- package/src/FlowNavigation/_stories/FlowNavigation.CriticalBanner.docs.mdx +0 -66
- package/src/Snackbar/Snackbar.css +0 -71
- package/src/Snackbar/Snackbar.less +0 -80
- package/src/Snackbar/Snackbar.test.tsx +0 -206
- package/src/Snackbar/Snackbar.tsx +0 -129
- package/src/Snackbar/SnackbarProvider.tsx +0 -33
- package/src/Snackbar/_stories/Snackbar.story.tsx +0 -89
- package/src/Snackbar/_stories/Snackbar.test.story.tsx +0 -139
- /package/build/{Snackbar → Prompt/Snackbar}/SnackbarContext.js +0 -0
- /package/build/{Snackbar → Prompt/Snackbar}/SnackbarContext.mjs +0 -0
- /package/build/{Snackbar → Prompt/Snackbar}/useSnackbar.js +0 -0
- /package/build/{Snackbar → Prompt/Snackbar}/useSnackbar.mjs +0 -0
- /package/src/{Snackbar → Prompt/Snackbar}/useSnackbar.ts +0 -0
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
import { act, render, screen, userEvent } from '../test-utils';
|
|
2
|
-
import Button from '../Button';
|
|
3
|
-
|
|
4
|
-
import { Snackbar, type SnackbarProps } from './Snackbar';
|
|
5
|
-
import { SnackbarConsumer } from './SnackbarContext';
|
|
6
|
-
import SnackbarProvider from './SnackbarProvider';
|
|
7
|
-
|
|
8
|
-
const CSS_TRANSITION_DURATION = 400;
|
|
9
|
-
const TIMEOUT = 4000;
|
|
10
|
-
|
|
11
|
-
const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTimeAsync });
|
|
12
|
-
|
|
13
|
-
const advanceTimers = (duration: number) => {
|
|
14
|
-
act(() => {
|
|
15
|
-
jest.advanceTimersByTime(duration);
|
|
16
|
-
});
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const renderSnackbar = (props: Partial<SnackbarProps> = {}) =>
|
|
20
|
-
render(<Snackbar text="" timeout={TIMEOUT} timestamp={0} {...props} />);
|
|
21
|
-
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
jest.useFakeTimers();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
afterEach(() => {
|
|
27
|
-
jest.clearAllTimers();
|
|
28
|
-
jest.useRealTimers();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
describe('Snackbar', () => {
|
|
32
|
-
it('renders the live region wrapper even while no notification is shown', () => {
|
|
33
|
-
renderSnackbar();
|
|
34
|
-
|
|
35
|
-
expect(screen.getByRole('alert')).toHaveAttribute('aria-live', 'assertive');
|
|
36
|
-
expect(screen.getByRole('alert')).toHaveClass('snackbar');
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('does not show a notification that is already present on mount', () => {
|
|
40
|
-
renderSnackbar({ text: 'Changes saved', timestamp: 1 });
|
|
41
|
-
|
|
42
|
-
expect(screen.queryByText('Changes saved')).not.toBeInTheDocument();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('shows the notification once text arrives after mount', () => {
|
|
46
|
-
const { rerender } = renderSnackbar();
|
|
47
|
-
|
|
48
|
-
rerender(<Snackbar text="Changes saved" timeout={TIMEOUT} timestamp={1} />);
|
|
49
|
-
|
|
50
|
-
expect(screen.getByText('Changes saved')).toBeInTheDocument();
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('renders an action and calls its onClick handler', async () => {
|
|
54
|
-
const onClick = jest.fn();
|
|
55
|
-
const { rerender } = renderSnackbar();
|
|
56
|
-
|
|
57
|
-
rerender(
|
|
58
|
-
<Snackbar
|
|
59
|
-
action={{ label: 'View', onClick }}
|
|
60
|
-
text="Transfer completed"
|
|
61
|
-
timeout={TIMEOUT}
|
|
62
|
-
timestamp={1}
|
|
63
|
-
/>,
|
|
64
|
-
);
|
|
65
|
-
await user.click(screen.getByRole('button', { name: 'View' }));
|
|
66
|
-
|
|
67
|
-
expect(onClick).toHaveBeenCalledTimes(1);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('dismisses the notification once the timeout elapses', () => {
|
|
71
|
-
const { rerender } = renderSnackbar();
|
|
72
|
-
|
|
73
|
-
rerender(<Snackbar text="Changes saved" timeout={TIMEOUT} timestamp={1} />);
|
|
74
|
-
|
|
75
|
-
advanceTimers(TIMEOUT - 1);
|
|
76
|
-
expect(screen.getByText('Changes saved')).toBeInTheDocument();
|
|
77
|
-
|
|
78
|
-
advanceTimers(1);
|
|
79
|
-
advanceTimers(CSS_TRANSITION_DURATION);
|
|
80
|
-
expect(screen.queryByText('Changes saved')).not.toBeInTheDocument();
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('exits the current notification before entering the next one', () => {
|
|
84
|
-
const { rerender } = renderSnackbar();
|
|
85
|
-
|
|
86
|
-
rerender(<Snackbar text="First message" timeout={TIMEOUT} timestamp={1} />);
|
|
87
|
-
rerender(<Snackbar text="Second message" timeout={TIMEOUT} timestamp={2} />);
|
|
88
|
-
|
|
89
|
-
advanceTimers(CSS_TRANSITION_DURATION - 1);
|
|
90
|
-
expect(screen.queryByText('Second message')).not.toBeInTheDocument();
|
|
91
|
-
|
|
92
|
-
advanceTimers(1);
|
|
93
|
-
expect(screen.getByText('Second message')).toBeInTheDocument();
|
|
94
|
-
expect(screen.queryByText('First message')).not.toBeInTheDocument();
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('shows the next notification immediately when the previous one has already been dismissed', () => {
|
|
98
|
-
const { rerender } = renderSnackbar();
|
|
99
|
-
|
|
100
|
-
rerender(<Snackbar text="First message" timeout={TIMEOUT} timestamp={1} />);
|
|
101
|
-
advanceTimers(TIMEOUT + CSS_TRANSITION_DURATION);
|
|
102
|
-
|
|
103
|
-
rerender(<Snackbar text="Second message" timeout={TIMEOUT} timestamp={2} />);
|
|
104
|
-
expect(screen.getByText('Second message')).toBeInTheDocument();
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('restarts the timeout for a replacing notification', () => {
|
|
108
|
-
const { rerender } = renderSnackbar();
|
|
109
|
-
|
|
110
|
-
rerender(<Snackbar text="First message" timeout={TIMEOUT} timestamp={1} />);
|
|
111
|
-
advanceTimers(TIMEOUT - 100);
|
|
112
|
-
|
|
113
|
-
rerender(<Snackbar text="Second message" timeout={TIMEOUT} timestamp={2} />);
|
|
114
|
-
advanceTimers(CSS_TRANSITION_DURATION);
|
|
115
|
-
expect(screen.getByText('Second message')).toBeInTheDocument();
|
|
116
|
-
|
|
117
|
-
advanceTimers(TIMEOUT - 1);
|
|
118
|
-
expect(screen.getByText('Second message')).toBeInTheDocument();
|
|
119
|
-
|
|
120
|
-
advanceTimers(1);
|
|
121
|
-
advanceTimers(CSS_TRANSITION_DURATION);
|
|
122
|
-
expect(screen.queryByText('Second message')).not.toBeInTheDocument();
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('dismisses a delayed replacement on the timeout current when it appears', () => {
|
|
126
|
-
const { rerender } = renderSnackbar();
|
|
127
|
-
|
|
128
|
-
rerender(<Snackbar text="First message" timeout={TIMEOUT} timestamp={1} />);
|
|
129
|
-
rerender(<Snackbar text="Second message" timeout={TIMEOUT} timestamp={2} />);
|
|
130
|
-
// Shortened while the replacement is still waiting out the exit transition.
|
|
131
|
-
rerender(<Snackbar text="Second message" timeout={1000} timestamp={2} />);
|
|
132
|
-
|
|
133
|
-
advanceTimers(CSS_TRANSITION_DURATION);
|
|
134
|
-
expect(screen.getByText('Second message')).toBeInTheDocument();
|
|
135
|
-
|
|
136
|
-
advanceTimers(1000);
|
|
137
|
-
advanceTimers(CSS_TRANSITION_DURATION);
|
|
138
|
-
expect(screen.queryByText('Second message')).not.toBeInTheDocument();
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
// Documents existing behaviour rather than endorsing it: clearing the text leaves the notification
|
|
142
|
-
// on screen indefinitely, because the previous shouldComponentUpdate blocked the dismissal too.
|
|
143
|
-
it('keeps the notification visible for as long as text is empty', () => {
|
|
144
|
-
const { rerender } = renderSnackbar();
|
|
145
|
-
|
|
146
|
-
rerender(<Snackbar text="Changes saved" timeout={TIMEOUT} timestamp={1} />);
|
|
147
|
-
rerender(<Snackbar text="" timeout={TIMEOUT} timestamp={2} />);
|
|
148
|
-
|
|
149
|
-
advanceTimers(TIMEOUT + CSS_TRANSITION_DURATION);
|
|
150
|
-
expect(screen.getByText('Changes saved')).toBeInTheDocument();
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('ignores a repeated timestamp', () => {
|
|
154
|
-
const { rerender } = renderSnackbar();
|
|
155
|
-
|
|
156
|
-
rerender(<Snackbar text="First message" timeout={TIMEOUT} timestamp={1} />);
|
|
157
|
-
rerender(<Snackbar text="Second message" timeout={TIMEOUT} timestamp={1} />);
|
|
158
|
-
|
|
159
|
-
advanceTimers(CSS_TRANSITION_DURATION);
|
|
160
|
-
expect(screen.getByText('First message')).toBeInTheDocument();
|
|
161
|
-
expect(screen.queryByText('Second message')).not.toBeInTheDocument();
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it('clears its pending dismissal timer on unmount', () => {
|
|
165
|
-
const { rerender, unmount } = renderSnackbar();
|
|
166
|
-
|
|
167
|
-
rerender(<Snackbar text="Changes saved" timeout={TIMEOUT} timestamp={1} />);
|
|
168
|
-
const pendingTimers = jest.getTimerCount();
|
|
169
|
-
unmount();
|
|
170
|
-
|
|
171
|
-
// The timer left behind belongs to CSSTransition's enter timeout, not to Snackbar.
|
|
172
|
-
expect(jest.getTimerCount()).toBe(pendingTimers - 1);
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
describe('Snackbar created through SnackbarProvider', () => {
|
|
177
|
-
const renderProvider = () =>
|
|
178
|
-
render(
|
|
179
|
-
<SnackbarProvider timeout={TIMEOUT}>
|
|
180
|
-
<SnackbarConsumer>
|
|
181
|
-
{({ createSnackbar }) => (
|
|
182
|
-
<Button onClick={() => createSnackbar({ text: 'Changes saved' })}>Show</Button>
|
|
183
|
-
)}
|
|
184
|
-
</SnackbarConsumer>
|
|
185
|
-
</SnackbarProvider>,
|
|
186
|
-
);
|
|
187
|
-
|
|
188
|
-
it('shows a notification created through the context', async () => {
|
|
189
|
-
renderProvider();
|
|
190
|
-
|
|
191
|
-
await user.click(screen.getByRole('button', { name: 'Show' }));
|
|
192
|
-
|
|
193
|
-
expect(screen.getByText('Changes saved')).toBeInTheDocument();
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('shows only one notification when the trigger is clicked repeatedly', async () => {
|
|
197
|
-
renderProvider();
|
|
198
|
-
|
|
199
|
-
const trigger = screen.getByRole('button', { name: 'Show' });
|
|
200
|
-
await user.click(trigger);
|
|
201
|
-
await user.click(trigger);
|
|
202
|
-
await user.click(trigger);
|
|
203
|
-
|
|
204
|
-
expect(screen.getAllByRole('alert')).toHaveLength(1);
|
|
205
|
-
});
|
|
206
|
-
});
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
2
|
-
import { CSSTransition } from 'react-transition-group';
|
|
3
|
-
|
|
4
|
-
import Button from '../Button';
|
|
5
|
-
import Body from '../Body';
|
|
6
|
-
import type { ThemeDark, ThemeLight } from '../common';
|
|
7
|
-
import withNextPortal from '../WithNextPortal/WithNextPortal';
|
|
8
|
-
|
|
9
|
-
const CSS_TRANSITION_DURATION = 400;
|
|
10
|
-
|
|
11
|
-
export interface SnackbarProps {
|
|
12
|
-
action?: {
|
|
13
|
-
label: string;
|
|
14
|
-
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
|
15
|
-
};
|
|
16
|
-
text: React.ReactNode;
|
|
17
|
-
/** @deprecated Has no effect. Snackbar theming is based on ThemeProvider. */
|
|
18
|
-
theme?: ThemeLight | ThemeDark;
|
|
19
|
-
timeout: number;
|
|
20
|
-
timestamp: number;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface SnackbarState extends Pick<SnackbarProps, 'action' | 'text'> {
|
|
24
|
-
visible: boolean;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export const Snackbar = (props: SnackbarProps) => {
|
|
28
|
-
const bodyRef = useRef<HTMLSpanElement>(null);
|
|
29
|
-
const leaveTimeout = useRef(0);
|
|
30
|
-
const transitionTimeout = useRef(0);
|
|
31
|
-
/** The props of the last committed render, as `this.props` was in the class component. */
|
|
32
|
-
const committedProps = useRef(props);
|
|
33
|
-
|
|
34
|
-
const [state, setState] = useState<SnackbarState>({
|
|
35
|
-
visible: false,
|
|
36
|
-
text: '',
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
useEffect(
|
|
40
|
-
() => () => {
|
|
41
|
-
window.clearTimeout(leaveTimeout.current);
|
|
42
|
-
window.clearTimeout(transitionTimeout.current);
|
|
43
|
-
},
|
|
44
|
-
[],
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
// Runs after every render to mirror the componentDidUpdate lifecycle this component previously
|
|
48
|
-
// relied on, including the shouldComponentUpdate guard that ignored every update without text.
|
|
49
|
-
// Layout, not passive, so a notification is shown or replaced before the browser paints, as
|
|
50
|
-
// componentDidUpdate did. Both rules below are disabled on purpose: the dependency-less effect
|
|
51
|
-
// and its synchronous state updates are what keep this conversion behaviour-identical.
|
|
52
|
-
/* eslint-disable react-hooks/exhaustive-deps, react-hooks/set-state-in-effect */
|
|
53
|
-
useLayoutEffect(() => {
|
|
54
|
-
const previousProps = committedProps.current;
|
|
55
|
-
committedProps.current = props;
|
|
56
|
-
|
|
57
|
-
const { action, text, timestamp } = props;
|
|
58
|
-
|
|
59
|
-
// shouldComponentUpdate bailed out whenever the incoming text was empty. That also blocked the
|
|
60
|
-
// updates scheduled below, so a Snackbar whose text is cleared stays on screen until it is
|
|
61
|
-
// given text again. Preserved here deliberately to keep this refactor behaviour-identical.
|
|
62
|
-
const updateWhileTextIsPresent = (update: () => void) => {
|
|
63
|
-
if (committedProps.current.text) {
|
|
64
|
-
update();
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const setLeaveTimeout = () => {
|
|
69
|
-
leaveTimeout.current = window.setTimeout(() => {
|
|
70
|
-
updateWhileTextIsPresent(() => setState((current) => ({ ...current, visible: false })));
|
|
71
|
-
}, committedProps.current.timeout);
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
const show = () => {
|
|
75
|
-
setState((current) => ({ ...current, visible: true, action, text }));
|
|
76
|
-
setLeaveTimeout();
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
if (!text) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (!previousProps.text) {
|
|
84
|
-
show();
|
|
85
|
-
} else if (previousProps.timestamp !== timestamp) {
|
|
86
|
-
window.clearTimeout(leaveTimeout.current);
|
|
87
|
-
|
|
88
|
-
if (state.visible) {
|
|
89
|
-
setState((current) => ({ ...current, visible: false }));
|
|
90
|
-
transitionTimeout.current = window.setTimeout(() => {
|
|
91
|
-
updateWhileTextIsPresent(show);
|
|
92
|
-
}, CSS_TRANSITION_DURATION);
|
|
93
|
-
} else {
|
|
94
|
-
show();
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
/* eslint-enable react-hooks/exhaustive-deps, react-hooks/set-state-in-effect */
|
|
99
|
-
|
|
100
|
-
const { action, text, visible } = state;
|
|
101
|
-
const { timeout } = props;
|
|
102
|
-
|
|
103
|
-
return (
|
|
104
|
-
<div className="snackbar" aria-live="assertive" role="alert">
|
|
105
|
-
<CSSTransition
|
|
106
|
-
nodeRef={bodyRef}
|
|
107
|
-
in={visible}
|
|
108
|
-
classNames="snackbar__text-container"
|
|
109
|
-
timeout={{
|
|
110
|
-
appear: 0,
|
|
111
|
-
enter: timeout,
|
|
112
|
-
exit: CSS_TRANSITION_DURATION,
|
|
113
|
-
}}
|
|
114
|
-
unmountOnExit
|
|
115
|
-
>
|
|
116
|
-
<Body ref={bodyRef} as="span" className="snackbar__text">
|
|
117
|
-
{text}
|
|
118
|
-
{action ? (
|
|
119
|
-
<Button className="snackbar__text__action" v2 size="sm" onClick={action.onClick}>
|
|
120
|
-
{action.label}
|
|
121
|
-
</Button>
|
|
122
|
-
) : null}
|
|
123
|
-
</Body>
|
|
124
|
-
</CSSTransition>
|
|
125
|
-
</div>
|
|
126
|
-
);
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
export default withNextPortal(Snackbar);
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { useMemo, useState } from 'react';
|
|
2
|
-
|
|
3
|
-
import SnackbarPortal, { SnackbarProps } from './Snackbar';
|
|
4
|
-
import { SnackbarContext } from './SnackbarContext';
|
|
5
|
-
|
|
6
|
-
export interface SnackbarProviderProps {
|
|
7
|
-
timeout?: number;
|
|
8
|
-
children?: React.ReactNode;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export default function SnackbarProvider({ timeout = 4500, children }: SnackbarProviderProps) {
|
|
12
|
-
const [state, setState] = useState<Omit<SnackbarProps, 'timeout'>>({
|
|
13
|
-
text: '',
|
|
14
|
-
timestamp: 0,
|
|
15
|
-
});
|
|
16
|
-
const { action, text, timestamp } = state;
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<SnackbarContext.Provider
|
|
20
|
-
value={useMemo(
|
|
21
|
-
() => ({
|
|
22
|
-
createSnackbar: ({ action, text }) => {
|
|
23
|
-
setState({ action, text, timestamp: Date.now() });
|
|
24
|
-
},
|
|
25
|
-
}),
|
|
26
|
-
[],
|
|
27
|
-
)}
|
|
28
|
-
>
|
|
29
|
-
<SnackbarPortal action={action} text={text} timestamp={timestamp} timeout={timeout} />
|
|
30
|
-
{children}
|
|
31
|
-
</SnackbarContext.Provider>
|
|
32
|
-
);
|
|
33
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
|
-
import { fn, userEvent, within } from 'storybook/test';
|
|
3
|
-
|
|
4
|
-
import { wait } from '../../test-utils/wait';
|
|
5
|
-
import {
|
|
6
|
-
createSandboxStory,
|
|
7
|
-
globalScope,
|
|
8
|
-
} from '../../../.storybook/components/sandbox/SandboxEditor';
|
|
9
|
-
import Button from '../../Button';
|
|
10
|
-
import { Snackbar } from '../Snackbar';
|
|
11
|
-
import { SnackbarConsumer } from '../SnackbarContext';
|
|
12
|
-
import SnackbarProvider from '../SnackbarProvider';
|
|
13
|
-
|
|
14
|
-
const meta: Meta = {
|
|
15
|
-
component: Snackbar,
|
|
16
|
-
title: 'Prompts/Snackbar',
|
|
17
|
-
};
|
|
18
|
-
export default meta;
|
|
19
|
-
|
|
20
|
-
export const Sandbox = createSandboxStory({
|
|
21
|
-
code: `<SnackbarProvider>
|
|
22
|
-
<SnackbarConsumer>
|
|
23
|
-
{({ createSnackbar }) => (
|
|
24
|
-
<Button v2 size="md"
|
|
25
|
-
onClick={() =>
|
|
26
|
-
createSnackbar({
|
|
27
|
-
text: <span>Transfer completed successfully</span>,
|
|
28
|
-
action: {
|
|
29
|
-
label: 'View',
|
|
30
|
-
onClick: () => console.log('View clicked'),
|
|
31
|
-
},
|
|
32
|
-
})
|
|
33
|
-
}
|
|
34
|
-
>
|
|
35
|
-
Show Snackbar
|
|
36
|
-
</Button>
|
|
37
|
-
)}
|
|
38
|
-
</SnackbarConsumer>
|
|
39
|
-
</SnackbarProvider>`,
|
|
40
|
-
scope: globalScope,
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
export const Basic: StoryObj = {
|
|
44
|
-
parameters: {
|
|
45
|
-
docs: {
|
|
46
|
-
source: {
|
|
47
|
-
type: 'code',
|
|
48
|
-
transform: async (src = '') => {
|
|
49
|
-
const match = /(function Render\(\).*?)(,\s+play:\s+async)/s.exec(src);
|
|
50
|
-
return match?.[1] || src;
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
source: {
|
|
55
|
-
type: 'code',
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
render: function Render() {
|
|
59
|
-
return (
|
|
60
|
-
<SnackbarProvider>
|
|
61
|
-
<SnackbarConsumer>
|
|
62
|
-
{({ createSnackbar }) => {
|
|
63
|
-
return (
|
|
64
|
-
<Button
|
|
65
|
-
block
|
|
66
|
-
onClick={() => {
|
|
67
|
-
createSnackbar({
|
|
68
|
-
action: {
|
|
69
|
-
label: 'View',
|
|
70
|
-
onClick: fn(),
|
|
71
|
-
},
|
|
72
|
-
text: <span>Some text</span>,
|
|
73
|
-
});
|
|
74
|
-
}}
|
|
75
|
-
>
|
|
76
|
-
Show Snackbar
|
|
77
|
-
</Button>
|
|
78
|
-
);
|
|
79
|
-
}}
|
|
80
|
-
</SnackbarConsumer>
|
|
81
|
-
</SnackbarProvider>
|
|
82
|
-
);
|
|
83
|
-
},
|
|
84
|
-
play: async ({ canvasElement }) => {
|
|
85
|
-
await wait(0);
|
|
86
|
-
const canvas = within(canvasElement);
|
|
87
|
-
await userEvent.click(canvas.getByRole('button'));
|
|
88
|
-
},
|
|
89
|
-
};
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import { userEvent, within, expect } from 'storybook/test';
|
|
2
|
-
import { action } from 'storybook/actions';
|
|
3
|
-
import { withVariantConfig } from '../../../.storybook/helpers';
|
|
4
|
-
import { lorem20 } from '../../test-utils';
|
|
5
|
-
import { wait } from '../../test-utils/wait';
|
|
6
|
-
import Button from '../../Button';
|
|
7
|
-
import { Snackbar, type SnackbarProps } from '../Snackbar';
|
|
8
|
-
import { SnackbarConsumer } from '../SnackbarContext';
|
|
9
|
-
import SnackbarProvider from '../SnackbarProvider';
|
|
10
|
-
|
|
11
|
-
export default {
|
|
12
|
-
title: 'Prompts/Snackbar/Tests',
|
|
13
|
-
component: Snackbar,
|
|
14
|
-
argTypes: {},
|
|
15
|
-
tags: ['!autodocs', '!manifest'],
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const launchSnackbar = async (canvasElement: HTMLElement) => {
|
|
19
|
-
await wait(0);
|
|
20
|
-
const canvas = within(canvasElement);
|
|
21
|
-
await userEvent.click(canvas.getByRole('button'));
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
type Args = Partial<SnackbarProps> & {
|
|
25
|
-
extraContext: React.ReactNode;
|
|
26
|
-
timeout: number;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const TIMEOUT = 10000000;
|
|
30
|
-
|
|
31
|
-
const Default = {
|
|
32
|
-
args: {
|
|
33
|
-
timeout: TIMEOUT,
|
|
34
|
-
} as Args,
|
|
35
|
-
play: async ({ canvasElement }: { canvasElement: HTMLElement }) => {
|
|
36
|
-
await launchSnackbar(canvasElement);
|
|
37
|
-
},
|
|
38
|
-
render: (args: Args) => {
|
|
39
|
-
return (
|
|
40
|
-
<SnackbarProvider timeout={args.timeout}>
|
|
41
|
-
<SnackbarConsumer>
|
|
42
|
-
{({ createSnackbar }) => (
|
|
43
|
-
<>
|
|
44
|
-
<Button
|
|
45
|
-
v2
|
|
46
|
-
block
|
|
47
|
-
onClick={() => {
|
|
48
|
-
createSnackbar({
|
|
49
|
-
action: {
|
|
50
|
-
label: 'View',
|
|
51
|
-
onClick: () => action('View button clicked.'),
|
|
52
|
-
},
|
|
53
|
-
text: <span>Some text</span>,
|
|
54
|
-
});
|
|
55
|
-
}}
|
|
56
|
-
>
|
|
57
|
-
Show Snackbar
|
|
58
|
-
</Button>
|
|
59
|
-
{args.extraContext}
|
|
60
|
-
</>
|
|
61
|
-
)}
|
|
62
|
-
</SnackbarConsumer>
|
|
63
|
-
</SnackbarProvider>
|
|
64
|
-
);
|
|
65
|
-
},
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
export const Basic = {
|
|
69
|
-
...Default,
|
|
70
|
-
args: {
|
|
71
|
-
...Default.args,
|
|
72
|
-
},
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
export const Mobile = {
|
|
76
|
-
...Default,
|
|
77
|
-
args: {
|
|
78
|
-
...Default.args,
|
|
79
|
-
},
|
|
80
|
-
...withVariantConfig(['mobile'], Default),
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const switchToDarkMode = async (context: { canvasElement: HTMLElement }) => {
|
|
84
|
-
await Default.play(context);
|
|
85
|
-
|
|
86
|
-
// the snackbar is outside the storybook dom so dark mode doesn't apply
|
|
87
|
-
const body = document.querySelector('div.np-theme-personal');
|
|
88
|
-
if (body) {
|
|
89
|
-
body.setAttribute('class', 'np-theme-personal--dark');
|
|
90
|
-
}
|
|
91
|
-
const snackbar = document.querySelector('div.snackbar');
|
|
92
|
-
if (snackbar) {
|
|
93
|
-
snackbar.setAttribute('class', 'snackbar np-theme-personal--dark');
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
export const DarkMode = {
|
|
98
|
-
...Default,
|
|
99
|
-
args: {
|
|
100
|
-
...Default.args,
|
|
101
|
-
},
|
|
102
|
-
play: switchToDarkMode,
|
|
103
|
-
...withVariantConfig(['dark'], Default),
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
export const Zoom400 = {
|
|
107
|
-
...Default,
|
|
108
|
-
args: {
|
|
109
|
-
...Default.args,
|
|
110
|
-
extraContext: (
|
|
111
|
-
<div className="p-t-3">
|
|
112
|
-
Snackbar isn't transparent (I'm going to make this text very long so that it will
|
|
113
|
-
fill up the entire page and go behind the snackbar. This way, when you look at it you will
|
|
114
|
-
easily be able to tell if the snackbar is transparent and recognize the bug because we
|
|
115
|
-
don't want it to be transparent. Transparent is bad. When we first did rebrand we had a
|
|
116
|
-
lot of problems with transparencies. {lorem20}
|
|
117
|
-
</div>
|
|
118
|
-
),
|
|
119
|
-
},
|
|
120
|
-
...withVariantConfig(['400%'], Default),
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Snackbar is only shown once even if trigger is clicked many times
|
|
125
|
-
*/
|
|
126
|
-
export const MultipleClicks = {
|
|
127
|
-
...Default,
|
|
128
|
-
args: {
|
|
129
|
-
...Default.args,
|
|
130
|
-
},
|
|
131
|
-
play: async ({ canvasElement }: { canvasElement: HTMLElement }) => {
|
|
132
|
-
const canvas = within(canvasElement);
|
|
133
|
-
for (let i = 0; i < 8; i += 1) {
|
|
134
|
-
await userEvent.click(canvas.getByRole('button'));
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
await expect(within(document.body).getAllByRole('alert')).toHaveLength(1);
|
|
138
|
-
},
|
|
139
|
-
};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|