@blotch/dolan 22.2.2 → 22.2.4
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/dist/commands/add.d.ts +6 -2
- package/dist/commands/add.js +41 -11
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/add.test.js +162 -9
- package/dist/commands/add.test.js.map +1 -1
- package/dist/commands/diff.d.ts +2 -1
- package/dist/commands/diff.js +38 -25
- package/dist/commands/diff.js.map +1 -1
- package/dist/commands/diff.test.js +72 -11
- package/dist/commands/diff.test.js.map +1 -1
- package/dist/commands/doctor.js +228 -34
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/doctor.test.js +367 -10
- package/dist/commands/doctor.test.js.map +1 -1
- package/dist/commands/eject.js +7 -0
- package/dist/commands/eject.js.map +1 -1
- package/dist/commands/eject.test.js +39 -0
- package/dist/commands/eject.test.js.map +1 -1
- package/dist/commands/info.js +3 -2
- package/dist/commands/info.js.map +1 -1
- package/dist/commands/init.d.ts +37 -1
- package/dist/commands/init.js +169 -40
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/init.test.js +392 -27
- package/dist/commands/init.test.js.map +1 -1
- package/dist/commands/install-packages.d.ts +6 -0
- package/dist/commands/install-packages.js +20 -0
- package/dist/commands/install-packages.js.map +1 -0
- package/dist/commands/install-packages.test.d.ts +1 -0
- package/dist/commands/install-packages.test.js +55 -0
- package/dist/commands/install-packages.test.js.map +1 -0
- package/dist/commands/list.js +21 -14
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/list.test.js +62 -5
- package/dist/commands/list.test.js.map +1 -1
- package/dist/commands/outdated.js +11 -10
- package/dist/commands/outdated.js.map +1 -1
- package/dist/commands/outdated.test.js +20 -5
- package/dist/commands/outdated.test.js.map +1 -1
- package/dist/commands/remove.d.ts +4 -0
- package/dist/commands/remove.js +17 -4
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/remove.test.js +53 -2
- package/dist/commands/remove.test.js.map +1 -1
- package/dist/commands/theme.js +1 -1
- package/dist/commands/theme.js.map +1 -1
- package/dist/commands/update.d.ts +11 -0
- package/dist/commands/update.js +174 -58
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/update.test.js +587 -33
- package/dist/commands/update.test.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/lockfile.d.ts +7 -0
- package/dist/lockfile.js +36 -0
- package/dist/lockfile.js.map +1 -1
- package/dist/lockfile.test.d.ts +1 -0
- package/dist/lockfile.test.js +25 -0
- package/dist/lockfile.test.js.map +1 -0
- package/dist/registry.d.ts +14 -0
- package/dist/registry.js +78 -24
- package/dist/registry.js.map +1 -1
- package/dist/registry.test.js +119 -38
- package/dist/registry.test.js.map +1 -1
- package/dist/test-utils.d.ts +5 -0
- package/dist/test-utils.js +6 -1
- package/dist/test-utils.js.map +1 -1
- package/dist/testing/fire-lynx-event.d.ts +14 -0
- package/dist/testing/fire-lynx-event.js +19 -0
- package/dist/testing/fire-lynx-event.js.map +1 -0
- package/dist/testing/fire-lynx-event.test.d.ts +1 -0
- package/dist/testing/fire-lynx-event.test.js +44 -0
- package/dist/testing/fire-lynx-event.test.js.map +1 -0
- package/dist/testing/lynx-main-thread.d.ts +22 -0
- package/dist/testing/lynx-main-thread.js +11 -0
- package/dist/testing/lynx-main-thread.js.map +1 -0
- package/dist/testing/lynx-main-thread.test.d.ts +1 -0
- package/dist/testing/lynx-main-thread.test.js +24 -0
- package/dist/testing/lynx-main-thread.test.js.map +1 -0
- package/dist/testing/set-input-signal.d.ts +14 -0
- package/dist/testing/set-input-signal.js +21 -0
- package/dist/testing/set-input-signal.js.map +1 -0
- package/dist/testing/set-input-signal.test.d.ts +1 -0
- package/dist/testing/set-input-signal.test.js +20 -0
- package/dist/testing/set-input-signal.test.js.map +1 -0
- package/dist/ui/components/accordion/accordion.test.ts +158 -144
- package/dist/ui/components/accordion/accordion.ts +19 -14
- package/dist/ui/components/action-sheet/action-sheet.test.ts +283 -151
- package/dist/ui/components/action-sheet/action-sheet.ts +77 -43
- package/dist/ui/components/alert/alert.test.ts +67 -19
- package/dist/ui/components/alert/alert.ts +14 -16
- package/dist/ui/components/alert-dialog/alert-dialog.test.ts +208 -94
- package/dist/ui/components/alert-dialog/alert-dialog.ts +75 -38
- package/dist/ui/components/area-chart/area-chart.test.ts +134 -119
- package/dist/ui/components/area-chart/area-chart.ts +100 -315
- package/dist/ui/components/area-chart/index.ts +2 -3
- package/dist/ui/components/avatar/avatar.test.ts +89 -45
- package/dist/ui/components/badge/badge.test.ts +57 -23
- package/dist/ui/components/badge/badge.ts +12 -14
- package/dist/ui/components/bar-chart/bar-chart.test.ts +172 -45
- package/dist/ui/components/bar-chart/bar-chart.ts +140 -294
- package/dist/ui/components/bar-chart/index.ts +3 -3
- package/dist/ui/components/bottom-sheet/bottom-sheet.test.ts +386 -128
- package/dist/ui/components/bottom-sheet/bottom-sheet.ts +170 -53
- package/dist/ui/components/button/button.test.ts +611 -2
- package/dist/ui/components/button/button.ts +434 -49
- package/dist/ui/components/button/index.ts +2 -0
- package/dist/ui/components/button-group/button-group.test.ts +484 -2
- package/dist/ui/components/button-group/button-group.ts +380 -68
- package/dist/ui/components/button-group/index.ts +3 -0
- package/dist/ui/components/candlestick-chart/candlestick-chart.test.ts +183 -58
- package/dist/ui/components/candlestick-chart/candlestick-chart.ts +150 -323
- package/dist/ui/components/candlestick-chart/index.ts +3 -4
- package/dist/ui/components/card/card.test.ts +82 -62
- package/dist/ui/components/chart/chart-focus.test.ts +132 -0
- package/dist/ui/components/chart/chart-focus.ts +115 -0
- package/dist/ui/components/chart/chart-presets.test.ts +335 -0
- package/dist/ui/components/chart/chart-presets.ts +278 -0
- package/dist/ui/components/chart/chart-scene.test.ts +589 -0
- package/dist/ui/components/chart/chart-scene.ts +483 -0
- package/dist/ui/components/chart/chart-theme.test.ts +85 -0
- package/dist/ui/components/chart/chart-theme.ts +210 -0
- package/dist/ui/components/chart/chart-zoom.test.ts +159 -0
- package/dist/ui/components/chart/chart-zoom.ts +162 -0
- package/dist/ui/components/chart/chart.test.ts +1017 -0
- package/dist/ui/components/chart/chart.ts +934 -0
- package/dist/ui/components/chart/index.ts +24 -0
- package/dist/ui/components/checkbox/checkbox.test.ts +124 -42
- package/dist/ui/components/checkbox/checkbox.ts +23 -23
- package/dist/ui/components/collapsible/collapsible.test.ts +158 -81
- package/dist/ui/components/collapsible/collapsible.ts +19 -14
- package/dist/ui/components/context-menu/context-menu.test.ts +224 -0
- package/dist/ui/components/context-menu/context-menu.ts +110 -0
- package/dist/ui/components/context-menu/index.ts +1 -0
- package/dist/ui/components/dialog/dialog.test.ts +213 -108
- package/dist/ui/components/dialog/dialog.ts +72 -35
- package/dist/ui/components/dock/dock-scroll.test.ts +130 -0
- package/dist/ui/components/dock/dock-scroll.ts +133 -0
- package/dist/ui/components/dock/dock.test.ts +749 -0
- package/dist/ui/components/dock/dock.ts +960 -0
- package/dist/ui/components/dock/index.ts +2 -0
- package/dist/ui/components/empty-state/empty-state.test.ts +28 -39
- package/dist/ui/components/glass/concentric-radius.test.ts +13 -0
- package/dist/ui/components/glass/concentric-radius.ts +17 -0
- package/dist/ui/components/glass/glass-design.test.ts +78 -0
- package/dist/ui/components/glass/glass-design.ts +119 -0
- package/dist/ui/components/glass/glass.test.ts +338 -0
- package/dist/ui/components/glass/glass.ts +360 -0
- package/dist/ui/components/glass/index.ts +18 -0
- package/dist/ui/components/icon/icon-theme.test.ts +169 -0
- package/dist/ui/components/icon/icon-theme.ts +227 -0
- package/dist/ui/components/icon/icon.test.ts +85 -25
- package/dist/ui/components/icon/icon.ts +38 -21
- package/dist/ui/components/input/input.test.ts +178 -101
- package/dist/ui/components/input/input.ts +15 -12
- package/dist/ui/components/label/label.test.ts +11 -24
- package/dist/ui/components/line-chart/index.ts +2 -3
- package/dist/ui/components/line-chart/line-chart.test.ts +149 -56
- package/dist/ui/components/line-chart/line-chart.ts +83 -241
- package/dist/ui/components/list/list.test.ts +119 -21
- package/dist/ui/components/list/list.ts +21 -15
- package/dist/ui/components/menu/index.ts +25 -0
- package/dist/ui/components/menu/menu-placement.test.ts +153 -0
- package/dist/ui/components/menu/menu-placement.ts +159 -0
- package/dist/ui/components/menu/menu-popup.test.ts +632 -0
- package/dist/ui/components/menu/menu-popup.ts +544 -0
- package/dist/ui/components/menu/menu.test.ts +429 -0
- package/dist/ui/components/menu/menu.ts +415 -0
- package/dist/ui/components/nav-drawer/nav-drawer.test.ts +435 -191
- package/dist/ui/components/nav-drawer/nav-drawer.ts +127 -56
- package/dist/ui/components/pagination/pagination.test.ts +177 -65
- package/dist/ui/components/pagination/pagination.ts +55 -8
- package/dist/ui/components/progress/progress.test.ts +102 -46
- package/dist/ui/components/progress/progress.ts +52 -54
- package/dist/ui/components/radio-group/radio-group.test.ts +128 -83
- package/dist/ui/components/radio-group/radio-group.ts +24 -24
- package/dist/ui/components/scatter-chart/index.ts +2 -3
- package/dist/ui/components/scatter-chart/scatter-chart.test.ts +131 -26
- package/dist/ui/components/scatter-chart/scatter-chart.ts +104 -250
- package/dist/ui/components/scroll-area/scroll-area.test.ts +95 -24
- package/dist/ui/components/scroll-area/scroll-area.ts +8 -8
- package/dist/ui/components/select/select.test.ts +138 -78
- package/dist/ui/components/select/select.ts +17 -11
- package/dist/ui/components/separator/separator.test.ts +18 -17
- package/dist/ui/components/sheet/sheet.test.ts +64 -11
- package/dist/ui/components/sheet/sheet.ts +9 -1
- package/dist/ui/components/skeleton/skeleton.test.ts +24 -7
- package/dist/ui/components/skeleton/skeleton.ts +10 -16
- package/dist/ui/components/spinner/spinner.test.ts +67 -22
- package/dist/ui/components/spinner/spinner.ts +48 -41
- package/dist/ui/components/switch/switch.test.ts +116 -46
- package/dist/ui/components/switch/switch.ts +8 -13
- package/dist/ui/components/tab-bar/index.ts +1 -0
- package/dist/ui/components/tab-bar/tab-bar.test.ts +280 -0
- package/dist/ui/components/tab-bar/tab-bar.ts +230 -0
- package/dist/ui/components/tabs/tabs.test.ts +215 -157
- package/dist/ui/components/tabs/tabs.ts +110 -118
- package/dist/ui/components/textarea/textarea.test.ts +206 -100
- package/dist/ui/components/textarea/textarea.ts +14 -12
- package/dist/ui/components/toast/toast.test.ts +436 -315
- package/dist/ui/components/toast/toast.ts +62 -55
- package/dist/ui/components/toggle/toggle.test.ts +247 -140
- package/dist/ui/theme/chart-colors.test.ts +35 -0
- package/dist/ui/theme/dark.css +46 -2
- package/dist/ui/theme/default.css +63 -7
- package/dist/ui/theme/icon-colors.test.ts +35 -0
- package/dist/ui/theme/rgb-channels.test.ts +84 -0
- package/dist/ui/theme/tailwind-build.test.ts +114 -0
- package/dist/ui/theme/tailwind-css.test.ts +83 -0
- package/dist/ui/theme/tailwind-plugin.test.ts +42 -2
- package/dist/ui/theme/tailwind-plugin.ts +98 -25
- package/dist/ui/theme/tailwind.css +113 -0
- package/dist/ui/utils/animate.test.ts +13 -1
- package/dist/utils/analyze.d.ts +14 -0
- package/dist/utils/analyze.js +15 -0
- package/dist/utils/analyze.js.map +1 -1
- package/dist/utils/analyze.test.js +14 -1
- package/dist/utils/analyze.test.js.map +1 -1
- package/dist/utils/installed.d.ts +29 -0
- package/dist/utils/installed.js +43 -0
- package/dist/utils/installed.js.map +1 -0
- package/dist/utils/installed.test.d.ts +1 -0
- package/dist/utils/installed.test.js +57 -0
- package/dist/utils/installed.test.js.map +1 -0
- package/dist/utils/interactive.d.ts +9 -0
- package/dist/utils/interactive.js +10 -0
- package/dist/utils/interactive.js.map +1 -0
- package/dist/utils/interactive.test.d.ts +1 -0
- package/dist/utils/interactive.test.js +24 -0
- package/dist/utils/interactive.test.js.map +1 -0
- package/dist/utils/packages.d.ts +36 -0
- package/dist/utils/packages.js +70 -0
- package/dist/utils/packages.js.map +1 -0
- package/dist/utils/packages.test.d.ts +1 -0
- package/dist/utils/packages.test.js +89 -0
- package/dist/utils/packages.test.js.map +1 -0
- package/dist/utils/resolve-paths.d.ts +17 -1
- package/dist/utils/resolve-paths.js +21 -3
- package/dist/utils/resolve-paths.js.map +1 -1
- package/dist/utils/resolve-paths.test.js +38 -2
- package/dist/utils/resolve-paths.test.js.map +1 -1
- package/dist/utils/tailwind.d.ts +25 -0
- package/dist/utils/tailwind.js +51 -0
- package/dist/utils/tailwind.js.map +1 -0
- package/dist/utils/tailwind.test.d.ts +1 -0
- package/dist/utils/tailwind.test.js +71 -0
- package/dist/utils/tailwind.test.js.map +1 -0
- package/package.json +13 -1
- package/dist/ui/components/area-chart/area-chart-render.test.ts +0 -121
- package/dist/ui/components/bar-chart/bar-chart-render.test.ts +0 -174
- package/dist/ui/components/button/button-render.test.ts +0 -113
- package/dist/ui/components/button-group/button-group-render.test.ts +0 -146
- package/dist/ui/components/candlestick-chart/candlestick-chart-render.test.ts +0 -174
- package/dist/ui/components/cartesian-chart/cartesian-chart-render.test.ts +0 -188
- package/dist/ui/components/cartesian-chart/cartesian-chart.test.ts +0 -525
- package/dist/ui/components/cartesian-chart/cartesian-chart.ts +0 -1140
- package/dist/ui/components/cartesian-chart/index.ts +0 -18
- package/dist/ui/components/line-chart/line-chart-render.test.ts +0 -121
- package/dist/ui/components/scatter-chart/scatter-chart-render.test.ts +0 -157
|
@@ -1,342 +1,463 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
fireEvent,
|
|
3
|
+
render,
|
|
4
|
+
waitForUpdate,
|
|
5
|
+
} from '@blotch/angular-lynx-testing-library';
|
|
6
|
+
import {
|
|
7
|
+
type MockInstance,
|
|
8
|
+
afterEach,
|
|
9
|
+
beforeEach,
|
|
10
|
+
describe,
|
|
11
|
+
expect,
|
|
12
|
+
it,
|
|
13
|
+
vi,
|
|
14
|
+
} from 'vitest';
|
|
15
|
+
import {
|
|
16
|
+
type LynxMainThread,
|
|
17
|
+
lynxMainThread,
|
|
18
|
+
} from '../../../test-utils/lynx-main-thread';
|
|
19
|
+
import { DURATION } from '../../utils/animate';
|
|
20
|
+
import { UiToaster } from './toast';
|
|
21
|
+
import { type ToastOptions, dismissToast, toast, toasts } from './toast-state';
|
|
22
|
+
|
|
23
|
+
const sleep = (ms: number) =>
|
|
24
|
+
new Promise<void>((resolve) => setTimeout(resolve, ms));
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Waits out a toast's exit animation, after which the toaster drops it from
|
|
28
|
+
* the queue, and renders that.
|
|
29
|
+
*/
|
|
30
|
+
const waitForExit = async () => {
|
|
31
|
+
await sleep(DURATION.fast + 40);
|
|
32
|
+
await waitForUpdate();
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const renderToaster = async () => {
|
|
36
|
+
const result = await render(UiToaster);
|
|
37
|
+
/**
|
|
38
|
+
* A toast's card: its host element, around the view holding its texts.
|
|
39
|
+
*/
|
|
40
|
+
const card = (title: string) =>
|
|
41
|
+
result.getByText(title).parentElement!.parentElement!;
|
|
42
|
+
return {
|
|
43
|
+
...result,
|
|
44
|
+
card,
|
|
45
|
+
/** One property of a toast card's inline style. */
|
|
46
|
+
styleOf: (title: string, property: string) =>
|
|
47
|
+
(card(title) as HTMLElement).style.getPropertyValue(property),
|
|
48
|
+
/**
|
|
49
|
+
* Raises a toast and lets it mount, then start its entrance, which waits
|
|
50
|
+
* a macrotask so the card is committed to Lynx's tree first.
|
|
51
|
+
*/
|
|
52
|
+
raise: async (options: ToastOptions) => {
|
|
53
|
+
const id = toast(options);
|
|
54
|
+
await waitForUpdate();
|
|
55
|
+
await waitForUpdate();
|
|
56
|
+
return id;
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/** `__ElementAnimate`'s first argument for starting an animation. */
|
|
62
|
+
const START = 0;
|
|
63
|
+
|
|
64
|
+
/** Where the front toast rests. */
|
|
65
|
+
const FRONT = 'translateY(0px) scale(1)';
|
|
66
|
+
/** Where the toast behind the front one rests: lifted, and a little smaller. */
|
|
67
|
+
const SECOND = 'translateY(-14px) scale(0.95)';
|
|
7
68
|
|
|
8
69
|
describe('UiToaster', () => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
});
|
|
70
|
+
// The queue is module state, so each test starts it empty.
|
|
71
|
+
beforeEach(() => toasts.set([]));
|
|
12
72
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
73
|
+
// Watched per test: the testing library resets the main thread after each.
|
|
74
|
+
let animate: MockInstance<LynxMainThread['__ElementAnimate']>;
|
|
75
|
+
beforeEach(() => {
|
|
76
|
+
animate = vi.spyOn(lynxMainThread(), '__ElementAnimate');
|
|
16
77
|
});
|
|
78
|
+
afterEach(() => animate.mockRestore());
|
|
17
79
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
80
|
+
/**
|
|
81
|
+
* The keyframes of the animations started on an element.
|
|
82
|
+
*/
|
|
83
|
+
const keyframesOn = (element: Element) =>
|
|
84
|
+
animate.mock.calls
|
|
85
|
+
.filter(
|
|
86
|
+
([target, [operation]]) => target === element && operation === START,
|
|
87
|
+
)
|
|
88
|
+
.map(([, [, , keyframes]]) => keyframes);
|
|
27
89
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
})
|
|
90
|
+
const entrance = [
|
|
91
|
+
{ transform: 'translateY(100%) scale(0.95)', opacity: 0 },
|
|
92
|
+
{ transform: FRONT, opacity: 1 },
|
|
93
|
+
];
|
|
94
|
+
const exit = (fromY: number) => [
|
|
95
|
+
{ transform: `translateY(${fromY}px) scale(1)`, opacity: 1 },
|
|
96
|
+
{ transform: 'translateY(300px) scale(0.95)', opacity: 0 },
|
|
97
|
+
];
|
|
98
|
+
const snapBack = (fromY: number) => [
|
|
99
|
+
{ transform: `translateY(${fromY}px) scale(1)` },
|
|
100
|
+
{ transform: FRONT },
|
|
101
|
+
];
|
|
38
102
|
|
|
39
|
-
describe('
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
description: 'World',
|
|
44
|
-
variant: 'default',
|
|
45
|
-
duration: 60_000,
|
|
46
|
-
...over,
|
|
47
|
-
});
|
|
103
|
+
describe('showing toasts', () => {
|
|
104
|
+
it('shows a toast once one is raised', async () => {
|
|
105
|
+
const { queryByText, getByText, raise } = await renderToaster();
|
|
106
|
+
expect(queryByText('Saved')).toBeNull();
|
|
48
107
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
setInputSignal(inst.depth, 0);
|
|
54
|
-
await waitForUpdate();
|
|
55
|
-
// JIT + input.required races the initial CD: even with the signal set, the
|
|
56
|
-
// template DOM may not be materialized in this synthetic bootstrap. The
|
|
57
|
-
// variant computed is exercised elsewhere; asserting on the container ref
|
|
58
|
-
// proves the render didn't throw.
|
|
59
|
-
expect(container).toBeTruthy();
|
|
60
|
-
});
|
|
108
|
+
await raise({ title: 'Saved', description: 'Your changes are live.' });
|
|
109
|
+
expect(getByText('Saved')).toBeTruthy();
|
|
110
|
+
expect(getByText('Your changes are live.')).toBeTruthy();
|
|
111
|
+
});
|
|
61
112
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
titleClass: () => string;
|
|
72
|
-
descriptionClass: () => string;
|
|
73
|
-
actionClass: () => string;
|
|
74
|
-
actionTextClass: () => string;
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
setInputSignal(inst.data, baseData());
|
|
78
|
-
setInputSignal(inst.depth, 0);
|
|
79
|
-
await waitForUpdate();
|
|
80
|
-
expect(cast.hostStyle()).toContain('translateY');
|
|
81
|
-
expect(cast.cardClass()).toContain('bg-background');
|
|
82
|
-
expect(cast.titleClass()).toContain('text-foreground');
|
|
83
|
-
expect(cast.descriptionClass()).toContain('text-muted-foreground');
|
|
84
|
-
expect(cast.actionClass()).toContain('border-border');
|
|
85
|
-
expect(cast.actionTextClass()).toContain('text-foreground');
|
|
86
|
-
|
|
87
|
-
setInputSignal(inst.data, baseData({ variant: 'destructive' }));
|
|
88
|
-
setInputSignal(inst.depth, 2);
|
|
89
|
-
await waitForUpdate();
|
|
90
|
-
expect(cast.cardClass()).toContain('bg-destructive');
|
|
91
|
-
expect(cast.titleClass()).toContain('text-destructive-foreground');
|
|
92
|
-
expect(cast.descriptionClass()).toContain('text-destructive-foreground');
|
|
93
|
-
expect(cast.actionClass()).toContain('border-destructive-foreground');
|
|
94
|
-
expect(cast.actionTextClass()).toContain('text-destructive-foreground');
|
|
95
|
-
expect(cast.hostStyle()).toContain('translateY');
|
|
96
|
-
});
|
|
113
|
+
it("leaves out a title or description it doesn't have", async () => {
|
|
114
|
+
const { getByText, raise } = await renderToaster();
|
|
115
|
+
await raise({ title: 'Only a title' });
|
|
116
|
+
await raise({ title: '', description: 'Only a description' });
|
|
117
|
+
const textsBeside = (text: string) =>
|
|
118
|
+
getByText(text).parentElement!.querySelectorAll('text');
|
|
119
|
+
expect(textsBeside('Only a title')).toHaveLength(1);
|
|
120
|
+
expect(textsBeside('Only a description')).toHaveLength(1);
|
|
121
|
+
});
|
|
97
122
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
(inst as unknown as { onAction: () => void }).onAction();
|
|
110
|
-
expect(cb).toHaveBeenCalledTimes(1);
|
|
111
|
-
});
|
|
123
|
+
it('styles a toast by its variant', async () => {
|
|
124
|
+
const { card, getByText, raise } = await renderToaster();
|
|
125
|
+
const action = { label: 'Undo', onAction: () => {} };
|
|
126
|
+
await raise({ title: 'Saved', description: 'Done.', action });
|
|
127
|
+
expect(card('Saved').classList).toContain('bg-background');
|
|
128
|
+
expect(getByText('Saved').classList).toContain('text-foreground');
|
|
129
|
+
expect(getByText('Done.').classList).toContain('text-muted-foreground');
|
|
130
|
+
expect(getByText('Undo').classList).toContain('text-foreground');
|
|
131
|
+
expect(getByText('Undo').parentElement!.classList).toContain(
|
|
132
|
+
'border-border',
|
|
133
|
+
);
|
|
112
134
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
(
|
|
121
|
-
|
|
122
|
-
|
|
135
|
+
await raise({
|
|
136
|
+
title: 'Failed',
|
|
137
|
+
description: 'Try again.',
|
|
138
|
+
variant: 'destructive',
|
|
139
|
+
action: { label: 'Retry', onAction: () => {} },
|
|
140
|
+
});
|
|
141
|
+
expect(card('Failed').classList).toContain('bg-destructive');
|
|
142
|
+
for (const text of ['Failed', 'Try again.', 'Retry']) {
|
|
143
|
+
expect(getByText(text).classList).toContain(
|
|
144
|
+
'text-destructive-foreground',
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
expect(getByText('Retry').parentElement!.classList).toContain(
|
|
148
|
+
'border-destructive-foreground',
|
|
149
|
+
);
|
|
150
|
+
});
|
|
123
151
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
(
|
|
132
|
-
).not.toThrow();
|
|
133
|
-
|
|
134
|
-
setInputSignal(inst.depth, 0);
|
|
135
|
-
await waitForUpdate();
|
|
136
|
-
(inst as unknown as { onTap: () => void }).onTap();
|
|
137
|
-
});
|
|
152
|
+
it('stays hidden until it slides up into view', async () => {
|
|
153
|
+
const { card, styleOf } = await renderToaster();
|
|
154
|
+
toast({ title: 'Saved' });
|
|
155
|
+
// Mounted, but its entrance waits a macrotask: until then it's hidden,
|
|
156
|
+
// or it would flash at its resting place first.
|
|
157
|
+
await waitForUpdate();
|
|
158
|
+
expect(styleOf('Saved', 'opacity')).toBe('0');
|
|
159
|
+
expect(keyframesOn(card('Saved'))).toEqual([]);
|
|
138
160
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
inst.dismissed.subscribe(dismissed);
|
|
144
|
-
setInputSignal(inst.data, baseData({ duration: 30 }));
|
|
145
|
-
setInputSignal(inst.depth, 0);
|
|
146
|
-
await waitForUpdate();
|
|
147
|
-
// Manually call ngOnInit after data is set so the auto-dismiss timer
|
|
148
|
-
// registers (JIT's initial CD ran ngOnInit before the required-input's
|
|
149
|
-
// value was available, so the timer was skipped).
|
|
150
|
-
inst.ngOnInit();
|
|
151
|
-
// Wait long enough for both the auto-dismiss (30ms) and the dismissed
|
|
152
|
-
// emit setTimeout(DURATION.fast + 20 = 170ms) to fire.
|
|
153
|
-
await new Promise((r) => setTimeout(r, 400));
|
|
154
|
-
expect(dismissed).toHaveBeenCalled();
|
|
155
|
-
});
|
|
161
|
+
await waitForUpdate();
|
|
162
|
+
expect(styleOf('Saved', 'opacity')).toBe('1');
|
|
163
|
+
expect(keyframesOn(card('Saved'))).toEqual([entrance]);
|
|
164
|
+
});
|
|
156
165
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
inst.ngOnInit();
|
|
166
|
-
await new Promise((r) => setTimeout(r, 10));
|
|
167
|
-
|
|
168
|
-
const cast = inst as unknown as {
|
|
169
|
-
onTouchStart: (e: { touches?: { clientY: number }[] }) => void;
|
|
170
|
-
onTouchMove: (e: { touches?: { clientY: number }[] }) => void;
|
|
171
|
-
onTouchEnd: () => void;
|
|
172
|
-
};
|
|
173
|
-
// Below threshold → snap back.
|
|
174
|
-
cast.onTouchStart({ touches: [{ clientY: 100 }] });
|
|
175
|
-
cast.onTouchMove({ touches: [{ clientY: 130 }] });
|
|
176
|
-
cast.onTouchEnd();
|
|
177
|
-
await waitForUpdate();
|
|
178
|
-
|
|
179
|
-
// Beyond threshold → dismiss.
|
|
180
|
-
cast.onTouchStart({ touches: [{ clientY: 100 }] });
|
|
181
|
-
cast.onTouchMove({ touches: [{ clientY: 400 }] });
|
|
182
|
-
cast.onTouchEnd();
|
|
183
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
184
|
-
});
|
|
166
|
+
it('stacks the newest three, newest in front, and queues the rest', async () => {
|
|
167
|
+
const { queryByText, styleOf, raise } = await renderToaster();
|
|
168
|
+
for (const title of ['First', 'Second', 'Third', 'Fourth']) {
|
|
169
|
+
await raise({ title });
|
|
170
|
+
}
|
|
171
|
+
// The oldest waits its turn, off screen.
|
|
172
|
+
expect(queryByText('First')).toBeNull();
|
|
173
|
+
expect(toasts()).toHaveLength(4);
|
|
185
174
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
onTouchMove: (e: { touches?: { clientY: number }[] }) => void;
|
|
196
|
-
onTouchEnd: () => void;
|
|
197
|
-
};
|
|
198
|
-
cast.onTouchStart({ touches: [{ clientY: 100 }] });
|
|
199
|
-
cast.onTouchMove({ touches: [{ clientY: 200 }] }); // not dragging → no-op
|
|
200
|
-
cast.onTouchEnd(); // not dragging → no-op
|
|
201
|
-
|
|
202
|
-
setInputSignal(inst.depth, 0);
|
|
203
|
-
await waitForUpdate();
|
|
204
|
-
// Empty touches array → no-op
|
|
205
|
-
cast.onTouchStart({ touches: [] });
|
|
206
|
-
cast.onTouchMove({ touches: [] });
|
|
207
|
-
// No prior touchstart with real coords, so onTouchEnd is a no-op.
|
|
208
|
-
cast.onTouchEnd();
|
|
209
|
-
});
|
|
175
|
+
expect(styleOf('Fourth', 'z-index')).toBe('100');
|
|
176
|
+
expect(styleOf('Fourth', 'transform')).toBe(FRONT);
|
|
177
|
+
expect(styleOf('Third', 'z-index')).toBe('99');
|
|
178
|
+
expect(styleOf('Third', 'transform')).toBe(SECOND);
|
|
179
|
+
expect(styleOf('Second', 'z-index')).toBe('98');
|
|
180
|
+
expect(styleOf('Second', 'transform')).toBe(
|
|
181
|
+
'translateY(-28px) scale(0.9)',
|
|
182
|
+
);
|
|
183
|
+
});
|
|
210
184
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
onTouchStart: (e: { touches?: { clientY: number }[] }) => void;
|
|
220
|
-
onTouchMove: (e: { touches?: { clientY: number }[] }) => void;
|
|
221
|
-
};
|
|
222
|
-
// Start a real drag (sets #isDragging = true).
|
|
223
|
-
cast.onTouchStart({ touches: [{ clientY: 50 }] });
|
|
224
|
-
// A subsequent move without touches must fall through the `y == null` guard
|
|
225
|
-
// in onTouchMove (line 257) — this is the "finger left the surface without
|
|
226
|
-
// a touchend" native quirk we defend against.
|
|
227
|
-
cast.onTouchMove({ touches: [] });
|
|
228
|
-
cast.onTouchMove({}); // touches undefined
|
|
229
|
-
});
|
|
185
|
+
it('moves the stack back for a new toast, and forward when the front one goes', async () => {
|
|
186
|
+
const { card, raise } = await renderToaster();
|
|
187
|
+
await raise({ title: 'Older' });
|
|
188
|
+
await raise({ title: 'Newer' });
|
|
189
|
+
expect(keyframesOn(card('Older'))).toEqual([
|
|
190
|
+
entrance,
|
|
191
|
+
[{ transform: FRONT }, { transform: SECOND }],
|
|
192
|
+
]);
|
|
230
193
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const cast = inst as unknown as {
|
|
239
|
-
onTouchStart: (e: { touches?: { clientY: number }[] }) => void;
|
|
240
|
-
onTouchMove: (e: { touches?: { clientY: number }[] }) => void;
|
|
241
|
-
onTouchEnd: () => void;
|
|
242
|
-
};
|
|
243
|
-
// Start a drag.
|
|
244
|
-
cast.onTouchStart({ touches: [{ clientY: 100 }] });
|
|
245
|
-
cast.onTouchMove({ touches: [{ clientY: 200 }] });
|
|
246
|
-
// Race the dismiss(): tapping or the timer already started tearing down.
|
|
247
|
-
inst.dismiss();
|
|
248
|
-
// touchend must observe #isDismissing and bail out (line 271).
|
|
249
|
-
cast.onTouchEnd();
|
|
250
|
-
});
|
|
194
|
+
fireEvent.tap(card('Newer'));
|
|
195
|
+
await waitForExit();
|
|
196
|
+
expect(keyframesOn(card('Older')).at(-1)).toEqual([
|
|
197
|
+
{ transform: SECOND },
|
|
198
|
+
{ transform: FRONT },
|
|
199
|
+
]);
|
|
200
|
+
});
|
|
251
201
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
cast.onTouchStart({ touches: [{ clientY: 100 }] });
|
|
270
|
-
cast.onTouchMove({ touches: [{ clientY: 120 }] });
|
|
271
|
-
cast.onTouchEnd();
|
|
272
|
-
// Wait long enough for the rescheduled dismiss (30ms) + dismissed emit
|
|
273
|
-
// (DURATION.fast + 20 ≈ 170ms) to fire.
|
|
274
|
-
await new Promise((r) => setTimeout(r, 400));
|
|
275
|
-
expect(dismissed).toHaveBeenCalled();
|
|
202
|
+
it('brings a queued toast into the back of the stack once there is room', async () => {
|
|
203
|
+
const { card, raise } = await renderToaster();
|
|
204
|
+
for (const title of ['First', 'Second', 'Third', 'Fourth']) {
|
|
205
|
+
await raise({ title });
|
|
206
|
+
}
|
|
207
|
+
fireEvent.tap(card('Fourth'));
|
|
208
|
+
await waitForExit();
|
|
209
|
+
// It mounts there, so it rises into the back slot rather than sliding
|
|
210
|
+
// up past the others.
|
|
211
|
+
await waitForUpdate();
|
|
212
|
+
expect(keyframesOn(card('First'))).toEqual([
|
|
213
|
+
[
|
|
214
|
+
{ transform: 'translateY(-20px) scale(0.864)', opacity: 0 },
|
|
215
|
+
{ transform: 'translateY(-28px) scale(0.9)', opacity: 1 },
|
|
216
|
+
],
|
|
217
|
+
]);
|
|
218
|
+
});
|
|
276
219
|
});
|
|
277
220
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
setInputSignal(inst.depth, 1); // triggers restack animation
|
|
285
|
-
await waitForUpdate();
|
|
286
|
-
setInputSignal(inst.depth, 2);
|
|
287
|
-
await waitForUpdate();
|
|
288
|
-
});
|
|
221
|
+
describe('dismissing', () => {
|
|
222
|
+
it('slides the front toast away when tapped', async () => {
|
|
223
|
+
const { card, queryByText, raise } = await renderToaster();
|
|
224
|
+
await raise({ title: 'Saved' });
|
|
225
|
+
fireEvent.tap(card('Saved'));
|
|
226
|
+
expect(keyframesOn(card('Saved')).at(-1)).toEqual(exit(0));
|
|
289
227
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
setInputSignal(inst.depth, 0);
|
|
295
|
-
await waitForUpdate();
|
|
296
|
-
const cast = inst as unknown as { hostStyle: () => string };
|
|
297
|
-
// Before ngOnInit → #hasEntered = false → opacity: 0.
|
|
298
|
-
expect(cast.hostStyle()).toContain('opacity: 0');
|
|
299
|
-
inst.ngOnInit();
|
|
300
|
-
// Wait past the setTimeout(0) that runs #animateIn, which sets #hasEntered.
|
|
301
|
-
await new Promise((r) => setTimeout(r, 30));
|
|
302
|
-
// After animateIn → #hasEntered = true → opacity: 1 (the ternary's truthy arm).
|
|
303
|
-
expect(cast.hostStyle()).toContain('opacity: 1');
|
|
304
|
-
});
|
|
228
|
+
await waitForExit();
|
|
229
|
+
expect(queryByText('Saved')).toBeNull();
|
|
230
|
+
expect(toasts()).toEqual([]);
|
|
231
|
+
});
|
|
305
232
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
233
|
+
it('slides away only once, however often it is tapped', async () => {
|
|
234
|
+
const { card, queryByText, raise } = await renderToaster();
|
|
235
|
+
await raise({ title: 'Saved' });
|
|
236
|
+
fireEvent.tap(card('Saved'));
|
|
237
|
+
fireEvent.tap(card('Saved'));
|
|
238
|
+
expect(keyframesOn(card('Saved'))).toEqual([entrance, exit(0)]);
|
|
239
|
+
|
|
240
|
+
await waitForExit();
|
|
241
|
+
expect(queryByText('Saved')).toBeNull();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('ignores taps on a toast behind the front one', async () => {
|
|
245
|
+
const { card, getByText, raise } = await renderToaster();
|
|
246
|
+
await raise({ title: 'Older' });
|
|
247
|
+
await raise({ title: 'Newer' });
|
|
248
|
+
animate.mockClear();
|
|
249
|
+
|
|
250
|
+
fireEvent.tap(card('Older'));
|
|
251
|
+
await waitForExit();
|
|
252
|
+
expect(animate).not.toHaveBeenCalled();
|
|
253
|
+
expect(getByText('Older')).toBeTruthy();
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('runs its action, then goes', async () => {
|
|
257
|
+
const onAction = vi.fn();
|
|
258
|
+
const { getByText, queryByText, raise } = await renderToaster();
|
|
259
|
+
await raise({ title: 'Deleted', action: { label: 'Undo', onAction } });
|
|
260
|
+
|
|
261
|
+
// The action catches its tap, so the card doesn't see it as well.
|
|
262
|
+
fireEvent.tap(getByText('Undo').parentElement!, {
|
|
263
|
+
eventType: 'catchEvent',
|
|
264
|
+
});
|
|
265
|
+
expect(onAction).toHaveBeenCalledTimes(1);
|
|
319
266
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
267
|
+
await waitForExit();
|
|
268
|
+
expect(queryByText('Deleted')).toBeNull();
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('goes by itself once its duration is up', async () => {
|
|
272
|
+
const { queryByText, raise } = await renderToaster();
|
|
273
|
+
await raise({ title: 'Saved', duration: 30 });
|
|
274
|
+
expect(queryByText('Saved')).not.toBeNull();
|
|
275
|
+
|
|
276
|
+
await sleep(30);
|
|
277
|
+
await waitForExit();
|
|
278
|
+
expect(queryByText('Saved')).toBeNull();
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('fades a toast behind the front one away in place when it expires', async () => {
|
|
282
|
+
const { card, queryByText, raise } = await renderToaster();
|
|
283
|
+
// Raised together, so the older one is behind from the start.
|
|
284
|
+
toast({ title: 'Older', duration: 30 });
|
|
285
|
+
await raise({ title: 'Newer' });
|
|
286
|
+
|
|
287
|
+
await sleep(30);
|
|
288
|
+
await waitForUpdate();
|
|
289
|
+
expect(keyframesOn(card('Older')).at(-1)).toEqual([
|
|
290
|
+
{ transform: SECOND, opacity: 1 },
|
|
291
|
+
{ transform: 'translateY(-14px) scale(0.855)', opacity: 0 },
|
|
292
|
+
]);
|
|
293
|
+
|
|
294
|
+
await waitForExit();
|
|
295
|
+
expect(queryByText('Older')).toBeNull();
|
|
296
|
+
expect(queryByText('Newer')).not.toBeNull();
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("lets a toast that's going finish, rather than moving back for a new one", async () => {
|
|
300
|
+
const { card, raise } = await renderToaster();
|
|
301
|
+
await raise({ title: 'Older' });
|
|
302
|
+
fireEvent.tap(card('Older'));
|
|
303
|
+
await raise({ title: 'Newer' });
|
|
304
|
+
expect(keyframesOn(card('Older'))).toEqual([entrance, exit(0)]);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
describe('removed before its timers fire', () => {
|
|
308
|
+
// A destroyed output warns (NG0953) if something still emits on it.
|
|
309
|
+
let warn: MockInstance<typeof console.warn>;
|
|
310
|
+
beforeEach(() => {
|
|
311
|
+
warn = vi.spyOn(console, 'warn');
|
|
312
|
+
});
|
|
313
|
+
afterEach(() => warn.mockRestore());
|
|
314
|
+
|
|
315
|
+
it("doesn't expire once it's gone", async () => {
|
|
316
|
+
const { card, queryByText, raise } = await renderToaster();
|
|
317
|
+
const id = await raise({ title: 'Saved', duration: 100 });
|
|
318
|
+
const removed = card('Saved');
|
|
319
|
+
dismissToast(id);
|
|
320
|
+
await waitForUpdate();
|
|
321
|
+
expect(queryByText('Saved')).toBeNull();
|
|
322
|
+
|
|
323
|
+
await sleep(100);
|
|
324
|
+
await waitForExit();
|
|
325
|
+
expect(keyframesOn(removed)).toEqual([entrance]);
|
|
326
|
+
expect(warn).not.toHaveBeenCalledWith(
|
|
327
|
+
expect.stringContaining('NG0953'),
|
|
328
|
+
);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it("doesn't report finishing its exit once it's gone", async () => {
|
|
332
|
+
const { card, queryByText, raise } = await renderToaster();
|
|
333
|
+
const id = await raise({ title: 'Saved' });
|
|
334
|
+
fireEvent.tap(card('Saved'));
|
|
335
|
+
dismissToast(id);
|
|
336
|
+
await waitForUpdate();
|
|
337
|
+
expect(queryByText('Saved')).toBeNull();
|
|
338
|
+
|
|
339
|
+
await waitForExit();
|
|
340
|
+
expect(warn).not.toHaveBeenCalledWith(
|
|
341
|
+
expect.stringContaining('NG0953'),
|
|
342
|
+
);
|
|
343
|
+
});
|
|
344
|
+
});
|
|
328
345
|
});
|
|
329
346
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
347
|
+
describe('dragging', () => {
|
|
348
|
+
/**
|
|
349
|
+
* Fires a touch event the card catches, at a height on screen.
|
|
350
|
+
*/
|
|
351
|
+
const touch = (
|
|
352
|
+
name: 'touchstart' | 'touchmove' | 'touchend' | 'touchcancel',
|
|
353
|
+
element: Element,
|
|
354
|
+
clientY?: number,
|
|
355
|
+
) =>
|
|
356
|
+
fireEvent[name](element, {
|
|
357
|
+
eventType: 'catchEvent',
|
|
358
|
+
...(clientY === undefined ? {} : { touches: [{ clientY }] }),
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('follows the finger down, and springs back if let go early', async () => {
|
|
362
|
+
const { card, styleOf, raise } = await renderToaster();
|
|
363
|
+
await raise({ title: 'Saved' });
|
|
364
|
+
|
|
365
|
+
touch('touchstart', card('Saved'), 100);
|
|
366
|
+
touch('touchmove', card('Saved'), 130);
|
|
367
|
+
await waitForUpdate();
|
|
368
|
+
expect(styleOf('Saved', 'transform')).toBe('translateY(30px) scale(1)');
|
|
369
|
+
|
|
370
|
+
// Docked at the bottom, it only moves down.
|
|
371
|
+
touch('touchmove', card('Saved'), 60);
|
|
372
|
+
await waitForUpdate();
|
|
373
|
+
expect(styleOf('Saved', 'transform')).toBe(FRONT);
|
|
374
|
+
|
|
375
|
+
touch('touchmove', card('Saved'), 150);
|
|
376
|
+
touch('touchend', card('Saved'));
|
|
377
|
+
await waitForUpdate();
|
|
378
|
+
expect(keyframesOn(card('Saved')).at(-1)).toEqual(snapBack(50));
|
|
379
|
+
expect(styleOf('Saved', 'transform')).toBe(FRONT);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it('springs back when the touch is canceled', async () => {
|
|
383
|
+
const { card, raise } = await renderToaster();
|
|
384
|
+
await raise({ title: 'Saved' });
|
|
385
|
+
touch('touchstart', card('Saved'), 100);
|
|
386
|
+
touch('touchmove', card('Saved'), 140);
|
|
387
|
+
touch('touchcancel', card('Saved'));
|
|
388
|
+
expect(keyframesOn(card('Saved')).at(-1)).toEqual(snapBack(40));
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it('slides away from the finger when dragged far enough', async () => {
|
|
392
|
+
const { card, queryByText, raise } = await renderToaster();
|
|
393
|
+
await raise({ title: 'Saved' });
|
|
394
|
+
touch('touchstart', card('Saved'), 100);
|
|
395
|
+
touch('touchmove', card('Saved'), 220);
|
|
396
|
+
touch('touchend', card('Saved'));
|
|
397
|
+
expect(keyframesOn(card('Saved')).at(-1)).toEqual(exit(120));
|
|
398
|
+
|
|
399
|
+
await waitForExit();
|
|
400
|
+
expect(queryByText('Saved')).toBeNull();
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it("doesn't expire while held, and gets its full time again once let go", async () => {
|
|
404
|
+
const { card, queryByText, raise } = await renderToaster();
|
|
405
|
+
await raise({ title: 'Saved', duration: 100 });
|
|
406
|
+
touch('touchstart', card('Saved'), 100);
|
|
407
|
+
|
|
408
|
+
await sleep(100);
|
|
409
|
+
await waitForExit();
|
|
410
|
+
expect(queryByText('Saved')).not.toBeNull();
|
|
411
|
+
|
|
412
|
+
touch('touchend', card('Saved'));
|
|
413
|
+
await sleep(100);
|
|
414
|
+
await waitForExit();
|
|
415
|
+
expect(queryByText('Saved')).toBeNull();
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it('ignores touches on a toast behind the front one', async () => {
|
|
419
|
+
const { card, styleOf, raise } = await renderToaster();
|
|
420
|
+
await raise({ title: 'Older' });
|
|
421
|
+
await raise({ title: 'Newer' });
|
|
422
|
+
animate.mockClear();
|
|
423
|
+
|
|
424
|
+
touch('touchstart', card('Older'), 100);
|
|
425
|
+
touch('touchmove', card('Older'), 200);
|
|
426
|
+
touch('touchend', card('Older'));
|
|
427
|
+
await waitForUpdate();
|
|
428
|
+
expect(styleOf('Older', 'transform')).toBe(SECOND);
|
|
429
|
+
expect(animate).not.toHaveBeenCalled();
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
it('ignores touches without a position', async () => {
|
|
433
|
+
const { card, styleOf, raise } = await renderToaster();
|
|
434
|
+
await raise({ title: 'Saved' });
|
|
435
|
+
|
|
436
|
+
// Without a starting point, there's no drag to follow.
|
|
437
|
+
touch('touchstart', card('Saved'));
|
|
438
|
+
touch('touchmove', card('Saved'), 200);
|
|
439
|
+
await waitForUpdate();
|
|
440
|
+
expect(styleOf('Saved', 'transform')).toBe(FRONT);
|
|
441
|
+
|
|
442
|
+
// A move without a position leaves the toast where it was.
|
|
443
|
+
touch('touchstart', card('Saved'), 100);
|
|
444
|
+
touch('touchmove', card('Saved'), 140);
|
|
445
|
+
touch('touchmove', card('Saved'));
|
|
446
|
+
await waitForUpdate();
|
|
447
|
+
expect(styleOf('Saved', 'transform')).toBe('translateY(40px) scale(1)');
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
it("doesn't spring back when a tap dismissed it mid-drag", async () => {
|
|
451
|
+
const { card, queryByText, raise } = await renderToaster();
|
|
452
|
+
await raise({ title: 'Saved' });
|
|
453
|
+
touch('touchstart', card('Saved'), 100);
|
|
454
|
+
touch('touchmove', card('Saved'), 120);
|
|
455
|
+
fireEvent.tap(card('Saved'));
|
|
456
|
+
touch('touchend', card('Saved'));
|
|
457
|
+
expect(keyframesOn(card('Saved'))).toEqual([entrance, exit(20)]);
|
|
458
|
+
|
|
459
|
+
await waitForExit();
|
|
460
|
+
expect(queryByText('Saved')).toBeNull();
|
|
461
|
+
});
|
|
340
462
|
});
|
|
341
463
|
});
|
|
342
|
-
void ElementRef;
|