@blotch/dolan 22.2.2 → 22.2.3
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 +32 -10
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/add.test.js +129 -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 +41 -27
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/doctor.test.js +93 -9
- 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 +149 -40
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/init.test.js +285 -26
- 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/update.d.ts +6 -0
- package/dist/commands/update.js +59 -24
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/update.test.js +273 -31
- package/dist/commands/update.test.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/registry.d.ts +8 -0
- package/dist/registry.js +32 -24
- package/dist/registry.js.map +1 -1
- package/dist/registry.test.js +76 -37
- package/dist/registry.test.js.map +1 -1
- package/dist/testing/set-input-signal.d.ts +13 -0
- package/dist/testing/set-input-signal.js +20 -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/action-sheet/action-sheet.test.ts +48 -1
- package/dist/ui/components/action-sheet/action-sheet.ts +37 -9
- package/dist/ui/components/alert-dialog/alert-dialog.test.ts +49 -2
- package/dist/ui/components/alert-dialog/alert-dialog.ts +43 -12
- package/dist/ui/components/area-chart/area-chart-render.test.ts +52 -110
- package/dist/ui/components/area-chart/area-chart.test.ts +58 -123
- 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/bar-chart/bar-chart-render.test.ts +47 -163
- package/dist/ui/components/bar-chart/bar-chart.test.ts +83 -37
- 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 +191 -9
- package/dist/ui/components/bottom-sheet/bottom-sheet.ts +136 -20
- package/dist/ui/components/button/button-render.test.ts +19 -0
- package/dist/ui/components/button/button.ts +18 -2
- package/dist/ui/components/candlestick-chart/candlestick-chart-render.test.ts +48 -163
- package/dist/ui/components/candlestick-chart/candlestick-chart.test.ts +106 -62
- 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/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 +139 -0
- package/dist/ui/components/chart/chart-presets.ts +271 -0
- package/dist/ui/components/chart/chart-render.test.ts +730 -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 +198 -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.ts +921 -0
- package/dist/ui/components/chart/index.ts +24 -0
- package/dist/ui/components/dialog/dialog.test.ts +49 -2
- package/dist/ui/components/dialog/dialog.ts +43 -12
- package/dist/ui/components/icon/icon-theme.test.ts +133 -0
- package/dist/ui/components/icon/icon-theme.ts +204 -0
- package/dist/ui/components/icon/icon.test.ts +51 -2
- package/dist/ui/components/icon/icon.ts +38 -21
- package/dist/ui/components/line-chart/index.ts +2 -3
- package/dist/ui/components/line-chart/line-chart-render.test.ts +69 -107
- package/dist/ui/components/line-chart/line-chart.test.ts +63 -45
- package/dist/ui/components/line-chart/line-chart.ts +83 -241
- package/dist/ui/components/list/list.test.ts +69 -2
- package/dist/ui/components/list/list.ts +21 -15
- package/dist/ui/components/nav-drawer/nav-drawer.test.ts +80 -1
- package/dist/ui/components/nav-drawer/nav-drawer.ts +66 -11
- package/dist/ui/components/pagination/pagination.test.ts +62 -0
- package/dist/ui/components/pagination/pagination.ts +55 -8
- package/dist/ui/components/scatter-chart/index.ts +2 -3
- package/dist/ui/components/scatter-chart/scatter-chart-render.test.ts +51 -144
- package/dist/ui/components/scatter-chart/scatter-chart.test.ts +57 -25
- package/dist/ui/components/scatter-chart/scatter-chart.ts +104 -250
- package/dist/ui/components/scroll-area/scroll-area.test.ts +37 -2
- package/dist/ui/components/scroll-area/scroll-area.ts +8 -8
- package/dist/ui/components/spinner/spinner.test.ts +27 -0
- package/dist/ui/components/spinner/spinner.ts +33 -23
- package/dist/ui/components/toast/toast.test.ts +37 -0
- package/dist/ui/components/toast/toast.ts +20 -4
- package/dist/ui/theme/chart-colors.test.ts +30 -0
- package/dist/ui/theme/dark.css +8 -0
- package/dist/ui/theme/default.css +8 -0
- package/dist/ui/theme/icon-colors.test.ts +30 -0
- package/dist/ui/theme/tailwind-plugin.ts +10 -1
- package/dist/utils/installed.d.ts +17 -0
- package/dist/utils/installed.js +27 -0
- package/dist/utils/installed.js.map +1 -0
- package/dist/utils/installed.test.d.ts +1 -0
- package/dist/utils/installed.test.js +41 -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 +23 -3
- package/dist/utils/resolve-paths.js.map +1 -1
- package/dist/utils/resolve-paths.test.js +34 -2
- package/dist/utils/resolve-paths.test.js.map +1 -1
- package/package.json +5 -1
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.test.js","sourceRoot":"","sources":["../src/registry.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"registry.test.js","sourceRoot":"","sources":["../src/registry.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sets an Angular `input()` / `model()` signal's value from a test.
|
|
3
|
+
*
|
|
4
|
+
* The Vitest JIT harness does not wire up signal inputs for template binding or
|
|
5
|
+
* `ComponentRef.setInput()` (that metadata is only emitted by Angular's AOT
|
|
6
|
+
* compiler), so the usual ways of feeding a value into an `input()` are inert
|
|
7
|
+
* here. Angular's own reactive-node API is the supported escape hatch: reach the
|
|
8
|
+
* input's SIGNAL node and call `applyValueToInputSignal`, exactly as a parent
|
|
9
|
+
* template binding would at runtime. This drives a PUBLIC input to a value — it
|
|
10
|
+
* does not touch a component's private (`#`) state — and mirrors the existing
|
|
11
|
+
* pattern in packages/runtime/src/lib/transition/lynx-transition.test.ts.
|
|
12
|
+
*/
|
|
13
|
+
export declare const setInputSignal: (signalFn: unknown, value: unknown) => void;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sets an Angular `input()` / `model()` signal's value from a test.
|
|
3
|
+
*
|
|
4
|
+
* The Vitest JIT harness does not wire up signal inputs for template binding or
|
|
5
|
+
* `ComponentRef.setInput()` (that metadata is only emitted by Angular's AOT
|
|
6
|
+
* compiler), so the usual ways of feeding a value into an `input()` are inert
|
|
7
|
+
* here. Angular's own reactive-node API is the supported escape hatch: reach the
|
|
8
|
+
* input's SIGNAL node and call `applyValueToInputSignal`, exactly as a parent
|
|
9
|
+
* template binding would at runtime. This drives a PUBLIC input to a value — it
|
|
10
|
+
* does not touch a component's private (`#`) state — and mirrors the existing
|
|
11
|
+
* pattern in packages/runtime/src/lib/transition/lynx-transition.test.ts.
|
|
12
|
+
*/
|
|
13
|
+
export const setInputSignal = (signalFn, value) => {
|
|
14
|
+
const symbols = Object.getOwnPropertySymbols(signalFn);
|
|
15
|
+
const signalSymbol = symbols.find((s) => s.toString() === 'Symbol(SIGNAL)');
|
|
16
|
+
const node = signalFn[signalSymbol];
|
|
17
|
+
const proto = Object.getPrototypeOf(node);
|
|
18
|
+
proto.applyValueToInputSignal(node, value);
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=set-input-signal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"set-input-signal.js","sourceRoot":"","sources":["../../src/testing/set-input-signal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAiB,EAAE,KAAc,EAAQ,EAAE;IACxE,MAAM,OAAO,GAAG,MAAM,CAAC,qBAAqB,CAAC,QAAkB,CAAC,CAAC;IACjE,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,KAAK,gBAAgB,CAAE,CAAC;IAC7E,MAAM,IAAI,GAAI,QAAoC,CAAC,YAAY,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC,IAAI,CAEvC,CAAC;IACF,KAAK,CAAC,uBAAuB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAC7C,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Injector, input, runInInjectionContext } from '@angular/core';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
import { setInputSignal } from './set-input-signal';
|
|
5
|
+
describe('setInputSignal', () => {
|
|
6
|
+
it("sets an input signal's value", () => {
|
|
7
|
+
const count = runInInjectionContext(Injector.create({ providers: [] }), () => input(0));
|
|
8
|
+
setInputSignal(count, 5);
|
|
9
|
+
expect(count()).toBe(5);
|
|
10
|
+
});
|
|
11
|
+
it('is the helper the component tests import from packages/ui', () => {
|
|
12
|
+
// Copied tests import `../../../test-utils/set-input-signal`, which dolan
|
|
13
|
+
// rewrites to this file. A copy that drifted would test differently in an
|
|
14
|
+
// app than in the library.
|
|
15
|
+
const shipped = readFileSync(new URL('./set-input-signal.ts', import.meta.url), 'utf-8');
|
|
16
|
+
const library = readFileSync(new URL('../../../ui/src/test-utils/set-input-signal.ts', import.meta.url), 'utf-8');
|
|
17
|
+
expect(shipped).toBe(library);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
//# sourceMappingURL=set-input-signal.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"set-input-signal.test.js","sourceRoot":"","sources":["../../src/testing/set-input-signal.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,KAAK,GAAG,qBAAqB,CACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,EAClC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CACf,CAAC;QACF,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QACzB,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,0EAA0E;QAC1E,0EAA0E;QAC1E,2BAA2B;QAC3B,MAAM,OAAO,GAAG,YAAY,CAC1B,IAAI,GAAG,CAAC,uBAAuB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EACjD,OAAO,CACR,CAAC;QACF,MAAM,OAAO,GAAG,YAAY,CAC1B,IAAI,GAAG,CACL,gDAAgD,EAChD,MAAM,CAAC,IAAI,CAAC,GAAG,CAChB,EACD,OAAO,CACR,CAAC;QACF,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -2,6 +2,7 @@ import { Component, signal } from '@angular/core';
|
|
|
2
2
|
import { render, waitForUpdate } from '@blotch/angular-lynx-testing-library';
|
|
3
3
|
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';
|
|
4
4
|
import { describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { DURATION } from '../../utils/animate';
|
|
5
6
|
import { setInputSignal } from '../../../test-utils/set-input-signal';
|
|
6
7
|
import {
|
|
7
8
|
UiActionSheet,
|
|
@@ -11,7 +12,6 @@ import {
|
|
|
11
12
|
} from './action-sheet';
|
|
12
13
|
|
|
13
14
|
@Component({
|
|
14
|
-
standalone: true,
|
|
15
15
|
imports: [
|
|
16
16
|
UiActionSheet,
|
|
17
17
|
UiActionSheetTitle,
|
|
@@ -32,6 +32,47 @@ class ActionSheetHost {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
describe('UiActionSheet', () => {
|
|
35
|
+
it('stops its close timers when it is destroyed while closing', async () => {
|
|
36
|
+
const warn = vi.spyOn(console, 'warn');
|
|
37
|
+
const { componentRef, unmount } = await render(UiActionSheet);
|
|
38
|
+
const inst = componentRef.instance as UiActionSheet;
|
|
39
|
+
inst.open.set(true);
|
|
40
|
+
for (let i = 0; i < 5; i++) await waitForUpdate();
|
|
41
|
+
inst.open.set(false);
|
|
42
|
+
await waitForUpdate();
|
|
43
|
+
// Destroyed during the close animation, as an `@if` around the action sheet
|
|
44
|
+
// would do. Its close timer (DURATION.normal + 20) used to fire anyway
|
|
45
|
+
// and emit `closed` from the destroyed output.
|
|
46
|
+
unmount();
|
|
47
|
+
await new Promise((r) => setTimeout(r, DURATION.normal + 50));
|
|
48
|
+
const warnings = warn.mock.calls.map(([message]) => String(message));
|
|
49
|
+
warn.mockRestore();
|
|
50
|
+
expect(warnings.filter((message) => message.includes('NG0953'))).toEqual(
|
|
51
|
+
[],
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('moves a screen reader into the panel and keeps it there while open', async () => {
|
|
56
|
+
const { componentRef, container } = await render(UiActionSheet);
|
|
57
|
+
const inst = componentRef.instance as UiActionSheet;
|
|
58
|
+
const panel = container.querySelector(
|
|
59
|
+
'[accessibility-exclusive-focus]',
|
|
60
|
+
) as Element & { invoke?: unknown };
|
|
61
|
+
expect(panel.getAttribute('accessibility-exclusive-focus')).toBe('false');
|
|
62
|
+
const invoke = vi.fn();
|
|
63
|
+
panel.invoke = invoke;
|
|
64
|
+
(inst as unknown as { backdropRef: unknown }).backdropRef = signal({
|
|
65
|
+
nativeElement: panel,
|
|
66
|
+
});
|
|
67
|
+
(inst as unknown as { panelRef: unknown }).panelRef = signal({
|
|
68
|
+
nativeElement: panel,
|
|
69
|
+
});
|
|
70
|
+
inst.open.set(true);
|
|
71
|
+
for (let i = 0; i < 5; i++) await waitForUpdate();
|
|
72
|
+
expect(invoke).toHaveBeenCalledWith('requestAccessibilityFocus');
|
|
73
|
+
expect(panel.getAttribute('accessibility-exclusive-focus')).toBe('true');
|
|
74
|
+
});
|
|
75
|
+
|
|
35
76
|
const patchRefs = (inst: UiActionSheet, view: Element) => {
|
|
36
77
|
(inst as unknown as { backdropRef: unknown }).backdropRef = signal({
|
|
37
78
|
nativeElement: view,
|
|
@@ -93,12 +134,18 @@ describe('UiActionSheet', () => {
|
|
|
93
134
|
it('open effect bails when backdrop/panel refs are undefined', async () => {
|
|
94
135
|
const { componentRef } = await render(UiActionSheet);
|
|
95
136
|
const inst = componentRef.instance as UiActionSheet;
|
|
137
|
+
const closed = vi.fn();
|
|
138
|
+
inst.closed.subscribe(closed);
|
|
96
139
|
inst.open.set(true);
|
|
97
140
|
await waitForUpdate();
|
|
98
141
|
await waitForUpdate();
|
|
99
142
|
inst.open.set(false);
|
|
100
143
|
await waitForUpdate();
|
|
144
|
+
// Wait out the close sequence, so #animateOut runs (and bails) while the
|
|
145
|
+
// component is still alive. Destroying it clears the sequence's timers.
|
|
146
|
+
await new Promise((r) => setTimeout(r, DURATION.normal + 50));
|
|
101
147
|
expect(inst.open()).toBe(false);
|
|
148
|
+
expect(closed).toHaveBeenCalledOnce();
|
|
102
149
|
});
|
|
103
150
|
|
|
104
151
|
it('applies user class on the panel', async () => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type ElementRef,
|
|
3
|
+
type OnDestroy,
|
|
3
4
|
Component,
|
|
4
5
|
ViewEncapsulation,
|
|
5
6
|
computed,
|
|
@@ -29,7 +30,6 @@ import { cn } from '../../utils/cn';
|
|
|
29
30
|
|
|
30
31
|
@Component({
|
|
31
32
|
selector: 'ui-action-sheet',
|
|
32
|
-
standalone: true,
|
|
33
33
|
imports: [LYNX_ELEMENTS],
|
|
34
34
|
encapsulation: ViewEncapsulation.None,
|
|
35
35
|
template: `
|
|
@@ -49,6 +49,7 @@ import { cn } from '../../utils/cn';
|
|
|
49
49
|
#panel
|
|
50
50
|
[class]="panelClass()"
|
|
51
51
|
[style]="panelPositionStyle()"
|
|
52
|
+
[accessibility-exclusive-focus]="overlayVisible()"
|
|
52
53
|
(catchtap)="onPanelTap()"
|
|
53
54
|
>
|
|
54
55
|
<!-- The elevation shadow is an inline rgba() box-shadow rather than a
|
|
@@ -72,7 +73,7 @@ import { cn } from '../../utils/cn';
|
|
|
72
73
|
</overlay>
|
|
73
74
|
`,
|
|
74
75
|
})
|
|
75
|
-
export class UiActionSheet {
|
|
76
|
+
export class UiActionSheet implements OnDestroy {
|
|
76
77
|
readonly open = model(false);
|
|
77
78
|
readonly userClass = input<string>('', { alias: 'class' });
|
|
78
79
|
|
|
@@ -96,6 +97,9 @@ export class UiActionSheet {
|
|
|
96
97
|
#backdropAnim?: AnimationHandle;
|
|
97
98
|
#panelAnim?: AnimationHandle;
|
|
98
99
|
#hasBeenOpen = false;
|
|
100
|
+
// The open and close sequences' pending timers, so ngOnDestroy can stop
|
|
101
|
+
// them.
|
|
102
|
+
readonly #timers = new Set<ReturnType<typeof setTimeout>>();
|
|
99
103
|
|
|
100
104
|
constructor() {
|
|
101
105
|
// `#hasBeenOpen` prevents the close animation from running on the initial
|
|
@@ -137,15 +141,37 @@ export class UiActionSheet {
|
|
|
137
141
|
*/
|
|
138
142
|
protected onPanelTap(): void {}
|
|
139
143
|
|
|
144
|
+
/**
|
|
145
|
+
* The action sheet can be destroyed while its open or close sequence is still
|
|
146
|
+
* running, for example by an `@if` around it or a route change right after
|
|
147
|
+
* closing it. The rest of the sequence then ran on the destroyed component:
|
|
148
|
+
* it animated detached elements and emitted `closed` from a destroyed
|
|
149
|
+
* output, which Angular warns about in dev builds (NG0953).
|
|
150
|
+
*/
|
|
151
|
+
ngOnDestroy(): void {
|
|
152
|
+
for (const timer of this.#timers) clearTimeout(timer);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* `setTimeout`, tracked so that ngOnDestroy can cancel it.
|
|
157
|
+
*/
|
|
158
|
+
#later(callback: () => void, ms: number): void {
|
|
159
|
+
const timer = setTimeout(() => {
|
|
160
|
+
this.#timers.delete(timer);
|
|
161
|
+
callback();
|
|
162
|
+
}, ms);
|
|
163
|
+
this.#timers.add(timer);
|
|
164
|
+
}
|
|
165
|
+
|
|
140
166
|
/**
|
|
141
167
|
* Two-phase open: make overlay visible first (so native elements exist in
|
|
142
168
|
* the tree), then animate on the next frame. See nav-drawer for the same
|
|
143
169
|
* pattern — Lynx animate() requires elements to be mounted before use.
|
|
144
170
|
*/
|
|
145
171
|
#doOpen(): void {
|
|
146
|
-
|
|
172
|
+
this.#later(() => {
|
|
147
173
|
this.overlayVisible.set(true);
|
|
148
|
-
|
|
174
|
+
this.#later(() => this.#animateIn(), 0);
|
|
149
175
|
}, 0);
|
|
150
176
|
}
|
|
151
177
|
|
|
@@ -154,9 +180,9 @@ export class UiActionSheet {
|
|
|
154
180
|
* causing elements to disappear before the animation finishes.
|
|
155
181
|
*/
|
|
156
182
|
#doClose(): void {
|
|
157
|
-
|
|
183
|
+
this.#later(() => {
|
|
158
184
|
this.#animateOut();
|
|
159
|
-
|
|
185
|
+
this.#later(() => {
|
|
160
186
|
this.overlayVisible.set(false);
|
|
161
187
|
this.closed.emit();
|
|
162
188
|
}, DURATION.normal + 20);
|
|
@@ -172,6 +198,11 @@ export class UiActionSheet {
|
|
|
172
198
|
this.#panelAnim?.cancel();
|
|
173
199
|
|
|
174
200
|
this.#backdropAnim = fadeIn(backdrop, { duration: DURATION.normal });
|
|
201
|
+
// Move a screen reader into the panel. It would otherwise stay on whatever
|
|
202
|
+
// opened it, which is now behind the overlay. With the panel's
|
|
203
|
+
// accessibility-exclusive-focus (see the template), it then can't leave
|
|
204
|
+
// the panel until it closes.
|
|
205
|
+
panel.invoke?.('requestAccessibilityFocus');
|
|
175
206
|
this.#panelAnim = slideIn(panel, 'up', {
|
|
176
207
|
duration: DURATION.slow,
|
|
177
208
|
easing: EASING.sheet,
|
|
@@ -196,7 +227,6 @@ export class UiActionSheet {
|
|
|
196
227
|
|
|
197
228
|
@Component({
|
|
198
229
|
selector: 'ui-action-sheet-title',
|
|
199
|
-
standalone: true,
|
|
200
230
|
imports: [LYNX_ELEMENTS],
|
|
201
231
|
encapsulation: ViewEncapsulation.None,
|
|
202
232
|
template: `
|
|
@@ -219,7 +249,6 @@ export class UiActionSheetTitle {
|
|
|
219
249
|
|
|
220
250
|
@Component({
|
|
221
251
|
selector: 'ui-action-sheet-item',
|
|
222
|
-
standalone: true,
|
|
223
252
|
imports: [LYNX_ELEMENTS],
|
|
224
253
|
encapsulation: ViewEncapsulation.None,
|
|
225
254
|
template: `
|
|
@@ -289,7 +318,6 @@ export class UiActionSheetItem {
|
|
|
289
318
|
|
|
290
319
|
@Component({
|
|
291
320
|
selector: 'ui-action-sheet-cancel',
|
|
292
|
-
standalone: true,
|
|
293
321
|
imports: [LYNX_ELEMENTS],
|
|
294
322
|
encapsulation: ViewEncapsulation.None,
|
|
295
323
|
template: `
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Component, signal } from '@angular/core';
|
|
2
2
|
import { render, waitForUpdate } from '@blotch/angular-lynx-testing-library';
|
|
3
3
|
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';
|
|
4
|
-
import { describe, expect, it } from 'vitest';
|
|
4
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { DURATION } from '../../utils/animate';
|
|
5
6
|
import { setInputSignal } from '../../../test-utils/set-input-signal';
|
|
6
7
|
import {
|
|
7
8
|
UiAlertDialog,
|
|
@@ -12,7 +13,6 @@ import {
|
|
|
12
13
|
} from './alert-dialog';
|
|
13
14
|
|
|
14
15
|
@Component({
|
|
15
|
-
standalone: true,
|
|
16
16
|
imports: [
|
|
17
17
|
UiAlertDialog,
|
|
18
18
|
UiAlertDialogHeader,
|
|
@@ -36,6 +36,47 @@ class AlertDialogHost {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
describe('UiAlertDialog', () => {
|
|
39
|
+
it('stops its close timers when it is destroyed while closing', async () => {
|
|
40
|
+
const warn = vi.spyOn(console, 'warn');
|
|
41
|
+
const { componentRef, unmount } = await render(UiAlertDialog);
|
|
42
|
+
const inst = componentRef.instance as UiAlertDialog;
|
|
43
|
+
inst.open.set(true);
|
|
44
|
+
for (let i = 0; i < 5; i++) await waitForUpdate();
|
|
45
|
+
inst.open.set(false);
|
|
46
|
+
await waitForUpdate();
|
|
47
|
+
// Destroyed during the close animation, as an `@if` around the alert dialog
|
|
48
|
+
// would do. Its close timer (DURATION.normal + 20) used to fire anyway
|
|
49
|
+
// and emit `closed` from the destroyed output.
|
|
50
|
+
unmount();
|
|
51
|
+
await new Promise((r) => setTimeout(r, DURATION.normal + 50));
|
|
52
|
+
const warnings = warn.mock.calls.map(([message]) => String(message));
|
|
53
|
+
warn.mockRestore();
|
|
54
|
+
expect(warnings.filter((message) => message.includes('NG0953'))).toEqual(
|
|
55
|
+
[],
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('moves a screen reader into the panel and keeps it there while open', async () => {
|
|
60
|
+
const { componentRef, container } = await render(UiAlertDialog);
|
|
61
|
+
const inst = componentRef.instance as UiAlertDialog;
|
|
62
|
+
const panel = container.querySelector(
|
|
63
|
+
'[accessibility-exclusive-focus]',
|
|
64
|
+
) as Element & { invoke?: unknown };
|
|
65
|
+
expect(panel.getAttribute('accessibility-exclusive-focus')).toBe('false');
|
|
66
|
+
const invoke = vi.fn();
|
|
67
|
+
panel.invoke = invoke;
|
|
68
|
+
(inst as unknown as { backdropRef: unknown }).backdropRef = signal({
|
|
69
|
+
nativeElement: panel,
|
|
70
|
+
});
|
|
71
|
+
(inst as unknown as { panelRef: unknown }).panelRef = signal({
|
|
72
|
+
nativeElement: panel,
|
|
73
|
+
});
|
|
74
|
+
inst.open.set(true);
|
|
75
|
+
for (let i = 0; i < 5; i++) await waitForUpdate();
|
|
76
|
+
expect(invoke).toHaveBeenCalledWith('requestAccessibilityFocus');
|
|
77
|
+
expect(panel.getAttribute('accessibility-exclusive-focus')).toBe('true');
|
|
78
|
+
});
|
|
79
|
+
|
|
39
80
|
it('renders header, title, description, and footer', async () => {
|
|
40
81
|
const { getByText } = await render(AlertDialogHost);
|
|
41
82
|
expect(getByText('Sure?')).toBeTruthy();
|
|
@@ -79,12 +120,18 @@ describe('UiAlertDialog', () => {
|
|
|
79
120
|
// Covers the early-return branches in #animateIn / #animateOut.
|
|
80
121
|
const { componentRef } = await render(UiAlertDialog);
|
|
81
122
|
const inst = componentRef.instance as UiAlertDialog;
|
|
123
|
+
const closed = vi.fn();
|
|
124
|
+
inst.closed.subscribe(closed);
|
|
82
125
|
inst.open.set(true);
|
|
83
126
|
await waitForUpdate();
|
|
84
127
|
await waitForUpdate();
|
|
85
128
|
inst.open.set(false);
|
|
86
129
|
await waitForUpdate();
|
|
130
|
+
// Wait out the close sequence, so #animateOut runs (and bails) while the
|
|
131
|
+
// component is still alive. Destroying it clears the sequence's timers.
|
|
132
|
+
await new Promise((r) => setTimeout(r, DURATION.normal + 50));
|
|
87
133
|
expect(inst.open()).toBe(false);
|
|
134
|
+
expect(closed).toHaveBeenCalledOnce();
|
|
88
135
|
});
|
|
89
136
|
|
|
90
137
|
it('overlayStyle switches between visible and hidden variants', async () => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type ElementRef,
|
|
3
|
+
type OnDestroy,
|
|
3
4
|
Component,
|
|
4
5
|
ViewEncapsulation,
|
|
5
6
|
computed,
|
|
@@ -26,20 +27,24 @@ import { cn } from '../../utils/cn';
|
|
|
26
27
|
|
|
27
28
|
@Component({
|
|
28
29
|
selector: 'ui-alert-dialog',
|
|
29
|
-
standalone: true,
|
|
30
30
|
imports: [LYNX_ELEMENTS],
|
|
31
31
|
encapsulation: ViewEncapsulation.None,
|
|
32
32
|
template: `
|
|
33
33
|
<overlay [attr.visible]="overlayVisible()" [style]="overlayStyle()">
|
|
34
34
|
<view #backdrop [class]="backdropClass()">
|
|
35
|
-
<view
|
|
35
|
+
<view
|
|
36
|
+
#panel
|
|
37
|
+
[class]="panelClass()"
|
|
38
|
+
[accessibility-exclusive-focus]="overlayVisible()"
|
|
39
|
+
(catchtap)="onPanelTap()"
|
|
40
|
+
>
|
|
36
41
|
<ng-content />
|
|
37
42
|
</view>
|
|
38
43
|
</view>
|
|
39
44
|
</overlay>
|
|
40
45
|
`,
|
|
41
46
|
})
|
|
42
|
-
export class UiAlertDialog {
|
|
47
|
+
export class UiAlertDialog implements OnDestroy {
|
|
43
48
|
readonly open = model(false);
|
|
44
49
|
readonly userClass = input<string>('', { alias: 'class' });
|
|
45
50
|
|
|
@@ -52,6 +57,9 @@ export class UiAlertDialog {
|
|
|
52
57
|
#backdropAnim?: AnimationHandle;
|
|
53
58
|
#panelAnim?: AnimationHandle;
|
|
54
59
|
#hasBeenOpen = false;
|
|
60
|
+
// The open and close sequences' pending timers, so ngOnDestroy can stop
|
|
61
|
+
// them.
|
|
62
|
+
readonly #timers = new Set<ReturnType<typeof setTimeout>>();
|
|
55
63
|
|
|
56
64
|
constructor() {
|
|
57
65
|
// `#hasBeenOpen` prevents the close animation from running on the initial
|
|
@@ -106,15 +114,37 @@ export class UiAlertDialog {
|
|
|
106
114
|
*/
|
|
107
115
|
protected onPanelTap(): void {}
|
|
108
116
|
|
|
117
|
+
/**
|
|
118
|
+
* The alert dialog can be destroyed while its open or close sequence is still
|
|
119
|
+
* running, for example by an `@if` around it or a route change right after
|
|
120
|
+
* closing it. The rest of the sequence then ran on the destroyed component:
|
|
121
|
+
* it animated detached elements and emitted `closed` from a destroyed
|
|
122
|
+
* output, which Angular warns about in dev builds (NG0953).
|
|
123
|
+
*/
|
|
124
|
+
ngOnDestroy(): void {
|
|
125
|
+
for (const timer of this.#timers) clearTimeout(timer);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* `setTimeout`, tracked so that ngOnDestroy can cancel it.
|
|
130
|
+
*/
|
|
131
|
+
#later(callback: () => void, ms: number): void {
|
|
132
|
+
const timer = setTimeout(() => {
|
|
133
|
+
this.#timers.delete(timer);
|
|
134
|
+
callback();
|
|
135
|
+
}, ms);
|
|
136
|
+
this.#timers.add(timer);
|
|
137
|
+
}
|
|
138
|
+
|
|
109
139
|
/**
|
|
110
140
|
* Two-phase open: make overlay visible first (so native elements exist in
|
|
111
141
|
* the tree), then animate on the next frame. See nav-drawer for the same
|
|
112
142
|
* pattern — Lynx animate() requires elements to be mounted before use.
|
|
113
143
|
*/
|
|
114
144
|
#doOpen(): void {
|
|
115
|
-
|
|
145
|
+
this.#later(() => {
|
|
116
146
|
this.overlayVisible.set(true);
|
|
117
|
-
|
|
147
|
+
this.#later(() => this.#animateIn(), 0);
|
|
118
148
|
}, 0);
|
|
119
149
|
}
|
|
120
150
|
|
|
@@ -123,9 +153,9 @@ export class UiAlertDialog {
|
|
|
123
153
|
* causing elements to disappear before the animation finishes.
|
|
124
154
|
*/
|
|
125
155
|
#doClose(): void {
|
|
126
|
-
|
|
156
|
+
this.#later(() => {
|
|
127
157
|
this.#animateOut();
|
|
128
|
-
|
|
158
|
+
this.#later(() => {
|
|
129
159
|
this.overlayVisible.set(false);
|
|
130
160
|
this.closed.emit();
|
|
131
161
|
}, DURATION.normal + 20);
|
|
@@ -141,11 +171,16 @@ export class UiAlertDialog {
|
|
|
141
171
|
this.#panelAnim?.cancel();
|
|
142
172
|
|
|
143
173
|
this.#backdropAnim = fadeIn(backdrop, { duration: DURATION.normal });
|
|
174
|
+
// Move a screen reader into the panel. It would otherwise stay on whatever
|
|
175
|
+
// opened it, which is now behind the overlay. With the panel's
|
|
176
|
+
// accessibility-exclusive-focus (see the template), it then can't leave
|
|
177
|
+
// the panel until it closes.
|
|
178
|
+
panel.invoke?.('requestAccessibilityFocus');
|
|
144
179
|
|
|
145
180
|
// 30ms stagger: let the backdrop fade start first so the panel scale-in
|
|
146
181
|
// feels like it's emerging from behind the dimmed overlay rather than
|
|
147
182
|
// appearing simultaneously with it.
|
|
148
|
-
|
|
183
|
+
this.#later(() => {
|
|
149
184
|
this.#panelAnim = scaleIn(panel, {
|
|
150
185
|
duration: DURATION.slow,
|
|
151
186
|
easing: EASING.springSubtle,
|
|
@@ -176,7 +211,6 @@ export class UiAlertDialog {
|
|
|
176
211
|
|
|
177
212
|
@Component({
|
|
178
213
|
selector: 'ui-alert-dialog-header',
|
|
179
|
-
standalone: true,
|
|
180
214
|
imports: [LYNX_ELEMENTS],
|
|
181
215
|
encapsulation: ViewEncapsulation.None,
|
|
182
216
|
template: `
|
|
@@ -195,7 +229,6 @@ export class UiAlertDialogHeader {
|
|
|
195
229
|
|
|
196
230
|
@Component({
|
|
197
231
|
selector: 'ui-alert-dialog-title',
|
|
198
|
-
standalone: true,
|
|
199
232
|
imports: [LYNX_ELEMENTS],
|
|
200
233
|
encapsulation: ViewEncapsulation.None,
|
|
201
234
|
template: `<text [class]="textClass()"><ng-content /></text>`,
|
|
@@ -210,7 +243,6 @@ export class UiAlertDialogTitle {
|
|
|
210
243
|
|
|
211
244
|
@Component({
|
|
212
245
|
selector: 'ui-alert-dialog-description',
|
|
213
|
-
standalone: true,
|
|
214
246
|
imports: [LYNX_ELEMENTS],
|
|
215
247
|
encapsulation: ViewEncapsulation.None,
|
|
216
248
|
template: `<text [class]="textClass()"><ng-content /></text>`,
|
|
@@ -225,7 +257,6 @@ export class UiAlertDialogDescription {
|
|
|
225
257
|
|
|
226
258
|
@Component({
|
|
227
259
|
selector: 'ui-alert-dialog-footer',
|
|
228
|
-
standalone: true,
|
|
229
260
|
imports: [LYNX_ELEMENTS],
|
|
230
261
|
encapsulation: ViewEncapsulation.None,
|
|
231
262
|
template: `
|