@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,8 +1,11 @@
|
|
|
1
|
-
import { Component } from '@angular/core';
|
|
2
|
-
import {
|
|
1
|
+
import { Component, signal } from '@angular/core';
|
|
2
|
+
import {
|
|
3
|
+
fireEvent,
|
|
4
|
+
render,
|
|
5
|
+
waitForUpdate,
|
|
6
|
+
} from '@blotch/angular-lynx-testing-library';
|
|
3
7
|
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';
|
|
4
8
|
import { describe, expect, it } from 'vitest';
|
|
5
|
-
import { setInputSignal } from '../../../test-utils/set-input-signal';
|
|
6
9
|
import {
|
|
7
10
|
UiCard,
|
|
8
11
|
UiCardContent,
|
|
@@ -13,7 +16,6 @@ import {
|
|
|
13
16
|
} from './card';
|
|
14
17
|
|
|
15
18
|
@Component({
|
|
16
|
-
standalone: true,
|
|
17
19
|
imports: [
|
|
18
20
|
UiCard,
|
|
19
21
|
UiCardHeader,
|
|
@@ -24,20 +26,47 @@ import {
|
|
|
24
26
|
LYNX_ELEMENTS,
|
|
25
27
|
],
|
|
26
28
|
template: `
|
|
27
|
-
<ui-card>
|
|
28
|
-
<ui-card-header>
|
|
29
|
-
<ui-card-title>Title</ui-card-title>
|
|
30
|
-
<ui-card-description>Desc</ui-card-description>
|
|
29
|
+
<ui-card [pressable]="pressable()" class="w-full">
|
|
30
|
+
<ui-card-header class="gap-1">
|
|
31
|
+
<ui-card-title class="text-lg">Title</ui-card-title>
|
|
32
|
+
<ui-card-description class="italic">Desc</ui-card-description>
|
|
31
33
|
</ui-card-header>
|
|
32
|
-
<ui-card-content><text>Body</text></ui-card-content>
|
|
33
|
-
<ui-card-footer><text>Actions</text></ui-card-footer>
|
|
34
|
+
<ui-card-content class="pt-2"><text>Body</text></ui-card-content>
|
|
35
|
+
<ui-card-footer class="justify-end"><text>Actions</text></ui-card-footer>
|
|
34
36
|
</ui-card>
|
|
35
37
|
`,
|
|
36
38
|
})
|
|
37
|
-
class CardHost {
|
|
39
|
+
class CardHost {
|
|
40
|
+
readonly pressable = signal(false);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The card's own view. Each component's host element renders as a view too,
|
|
45
|
+
* so it's found by the card's class rather than by being the first.
|
|
46
|
+
*/
|
|
47
|
+
const cardView = (container: Element) => container.querySelector('.bg-card')!;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Fires a Lynx event `fireEvent` has no shortcut for, as Lynx dispatches it:
|
|
51
|
+
* `mouseleave` is the mouse's `touchcancel`.
|
|
52
|
+
*/
|
|
53
|
+
const fireLynxEvent = (element: Element, name: string) =>
|
|
54
|
+
fireEvent(
|
|
55
|
+
element,
|
|
56
|
+
Object.assign(new Event(`bindEvent:${name}`), {
|
|
57
|
+
eventType: 'bindEvent',
|
|
58
|
+
eventName: name,
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The scale the card's inline style holds, if any.
|
|
64
|
+
*/
|
|
65
|
+
const scaleOf = (view: Element) =>
|
|
66
|
+
(view as HTMLElement).style.getPropertyValue('transform');
|
|
38
67
|
|
|
39
68
|
describe('UiCard', () => {
|
|
40
|
-
it('renders each
|
|
69
|
+
it('renders each part with its projected content', async () => {
|
|
41
70
|
const { getByText } = await render(CardHost);
|
|
42
71
|
expect(getByText('Title')).toBeTruthy();
|
|
43
72
|
expect(getByText('Desc')).toBeTruthy();
|
|
@@ -45,66 +74,57 @@ describe('UiCard', () => {
|
|
|
45
74
|
expect(getByText('Actions')).toBeTruthy();
|
|
46
75
|
});
|
|
47
76
|
|
|
48
|
-
it('
|
|
49
|
-
const { container } = await render(
|
|
50
|
-
expect(container).
|
|
77
|
+
it("merges each part's class with its own", async () => {
|
|
78
|
+
const { container, getByText } = await render(CardHost);
|
|
79
|
+
expect(cardView(container).getAttribute('class')).toContain('bg-card');
|
|
80
|
+
expect(cardView(container).getAttribute('class')).toContain('w-full');
|
|
81
|
+
expect(getByText('Title').getAttribute('class')).toContain('text-lg');
|
|
82
|
+
expect(getByText('Desc').getAttribute('class')).toContain('italic');
|
|
51
83
|
});
|
|
52
84
|
|
|
53
|
-
it('
|
|
54
|
-
const {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
85
|
+
it("doesn't respond to presses unless it's pressable", async () => {
|
|
86
|
+
const { container } = await render(CardHost);
|
|
87
|
+
const view = cardView(container);
|
|
88
|
+
fireEvent.touchstart(view);
|
|
89
|
+
fireEvent.mousedown(view);
|
|
90
|
+
expect(scaleOf(view)).toBe('');
|
|
91
|
+
fireEvent.touchend(view);
|
|
92
|
+
fireEvent.touchcancel(view);
|
|
93
|
+
fireEvent.mouseup(view);
|
|
94
|
+
fireLynxEvent(view, 'mouseleave');
|
|
95
|
+
expect(scaleOf(view)).toBe('');
|
|
63
96
|
});
|
|
64
97
|
|
|
65
|
-
it('
|
|
66
|
-
const { componentRef } = await render(
|
|
67
|
-
|
|
68
|
-
setInputSignal(inst.pressable, true);
|
|
98
|
+
it('scales down while pressed by touch, and back on release', async () => {
|
|
99
|
+
const { container, componentRef } = await render(CardHost);
|
|
100
|
+
(componentRef.instance as CardHost).pressable.set(true);
|
|
69
101
|
await waitForUpdate();
|
|
70
|
-
const
|
|
71
|
-
onPressStart: () => void;
|
|
72
|
-
onPressEnd: () => void;
|
|
73
|
-
onPressCancel: () => void;
|
|
74
|
-
};
|
|
75
|
-
// Run twice so `#pressAnim?.cancel()` gets exercised on the second call.
|
|
76
|
-
cast.onPressStart();
|
|
77
|
-
cast.onPressStart();
|
|
78
|
-
cast.onPressEnd();
|
|
79
|
-
cast.onPressCancel();
|
|
80
|
-
// No assertion — coverage-only paths. Confirm no throw.
|
|
81
|
-
expect(true).toBe(true);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
102
|
+
const view = cardView(container);
|
|
84
103
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
expect(
|
|
89
|
-
});
|
|
104
|
+
fireEvent.touchstart(view);
|
|
105
|
+
expect(scaleOf(view)).toBe('scale(0.985)');
|
|
106
|
+
fireEvent.touchend(view);
|
|
107
|
+
expect(scaleOf(view)).toBe('scale(1)');
|
|
90
108
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
109
|
+
// A scroll that takes the touch over cancels it: the card scales back.
|
|
110
|
+
fireEvent.touchstart(view);
|
|
111
|
+
fireEvent.touchcancel(view);
|
|
112
|
+
expect(scaleOf(view)).toBe('scale(1)');
|
|
94
113
|
});
|
|
95
114
|
|
|
96
|
-
it('
|
|
97
|
-
const { container } = await render(
|
|
98
|
-
|
|
99
|
-
|
|
115
|
+
it('scales down while pressed by mouse, and back on release or leave', async () => {
|
|
116
|
+
const { container, componentRef } = await render(CardHost);
|
|
117
|
+
(componentRef.instance as CardHost).pressable.set(true);
|
|
118
|
+
await waitForUpdate();
|
|
119
|
+
const view = cardView(container);
|
|
100
120
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
121
|
+
fireEvent.mousedown(view);
|
|
122
|
+
expect(scaleOf(view)).toBe('scale(0.985)');
|
|
123
|
+
fireEvent.mouseup(view);
|
|
124
|
+
expect(scaleOf(view)).toBe('scale(1)');
|
|
105
125
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
expect(
|
|
126
|
+
fireEvent.mousedown(view);
|
|
127
|
+
fireLynxEvent(view, 'mouseleave');
|
|
128
|
+
expect(scaleOf(view)).toBe('scale(1)');
|
|
109
129
|
});
|
|
110
130
|
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { createChartRuntime } from '@tanstack/charts/runtime';
|
|
3
|
+
import { scaleLinear } from '@tanstack/charts/scales/linear';
|
|
4
|
+
import type { ChartDefinition, ChartPoint } from '@tanstack/charts/types';
|
|
5
|
+
import { defineChart, dot, lineY } from '@tanstack/charts/universal';
|
|
6
|
+
import { adjacentFocusPoint, createChartFocusModel } from './chart-focus';
|
|
7
|
+
|
|
8
|
+
const data = [
|
|
9
|
+
{ x: 0, y: 1 },
|
|
10
|
+
{ x: 1, y: 3 },
|
|
11
|
+
{ x: 2, y: 2 },
|
|
12
|
+
];
|
|
13
|
+
const scales = {
|
|
14
|
+
x: { scale: () => scaleLinear().domain([0, 2]) },
|
|
15
|
+
y: { scale: () => scaleLinear().domain([0, 4]) },
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const modelFor = (definition: ChartDefinition<any, any, any>) => {
|
|
19
|
+
const scene = createChartRuntime().render(definition, {
|
|
20
|
+
width: 200,
|
|
21
|
+
height: 100,
|
|
22
|
+
});
|
|
23
|
+
return { scene, model: createChartFocusModel(scene, definition) };
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
describe('createChartFocusModel', () => {
|
|
27
|
+
it('resolves a touch to the nearest point', () => {
|
|
28
|
+
const definition = defineChart({
|
|
29
|
+
marks: [dot(data, { x: 'x', y: 'y' })],
|
|
30
|
+
scales,
|
|
31
|
+
});
|
|
32
|
+
const { scene, model } = modelFor(definition);
|
|
33
|
+
const target = scene.points[1]!;
|
|
34
|
+
expect(model.resolve(target.x + 2, target.y - 2)).toEqual([target]);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('resolves nothing out of reach', () => {
|
|
38
|
+
const definition = defineChart({
|
|
39
|
+
marks: [dot(data, { x: 'x', y: 'y' })],
|
|
40
|
+
scales,
|
|
41
|
+
maxFocusDistance: 1,
|
|
42
|
+
});
|
|
43
|
+
const { model } = modelFor(definition);
|
|
44
|
+
expect(model.resolve(-500, -500)).toEqual([]);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("groups a mark's series at one x with the group-x preset", () => {
|
|
48
|
+
const series = [
|
|
49
|
+
...data.map((d) => ({ ...d, s: 'a' })),
|
|
50
|
+
...data.map((d) => ({ ...d, y: d.y + 1, s: 'b' })),
|
|
51
|
+
];
|
|
52
|
+
const definition = defineChart({
|
|
53
|
+
marks: [lineY(series, { x: 'x', y: 'y', z: 's' })],
|
|
54
|
+
scales,
|
|
55
|
+
focus: 'group-x',
|
|
56
|
+
});
|
|
57
|
+
const { scene, model } = modelFor(definition);
|
|
58
|
+
const point = scene.points.find((p) => p.xValue === 1)!;
|
|
59
|
+
const group = model.resolve(point.x, point.y);
|
|
60
|
+
expect(group.length).toBe(2);
|
|
61
|
+
expect(model.group(point).length).toBe(2);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('turns focus off with focus: false', () => {
|
|
65
|
+
const definition = defineChart({
|
|
66
|
+
marks: [dot(data, { x: 'x', y: 'y' })],
|
|
67
|
+
scales,
|
|
68
|
+
focus: false,
|
|
69
|
+
});
|
|
70
|
+
const { scene, model } = modelFor(definition);
|
|
71
|
+
expect(model.resolve(scene.points[0]!.x, scene.points[0]!.y)).toEqual([]);
|
|
72
|
+
expect(model.navigation).toEqual([]);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("uses the definition's spatial index", () => {
|
|
76
|
+
// Typed loosely: the index returns points of whatever datum the chart has.
|
|
77
|
+
const findNearest = vi.fn((): ChartPoint<any, any, any> | null => null);
|
|
78
|
+
const definition = defineChart({
|
|
79
|
+
marks: [dot(data, { x: 'x', y: 'y' })],
|
|
80
|
+
scales,
|
|
81
|
+
spatialIndex: () => ({ findNearest }),
|
|
82
|
+
});
|
|
83
|
+
const { model } = modelFor(definition);
|
|
84
|
+
expect(model.resolve(-500, -500)).toEqual([]);
|
|
85
|
+
expect(findNearest).toHaveBeenCalledWith(-500, -500, 48);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('restores a point from an older scene by identity', () => {
|
|
89
|
+
const definition = defineChart({
|
|
90
|
+
marks: [dot(data, { x: 'x', y: 'y' })],
|
|
91
|
+
scales,
|
|
92
|
+
});
|
|
93
|
+
const first = modelFor(definition);
|
|
94
|
+
const second = modelFor(definition);
|
|
95
|
+
const restored = second.model.restore(first.scene.points[2]!);
|
|
96
|
+
expect(restored).toBe(second.scene.points[2]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('orders points left to right for stepping through them', () => {
|
|
100
|
+
const definition = defineChart({
|
|
101
|
+
marks: [dot([data[2]!, data[0]!, data[1]!], { x: 'x', y: 'y' })],
|
|
102
|
+
scales,
|
|
103
|
+
});
|
|
104
|
+
const { model } = modelFor(definition);
|
|
105
|
+
expect(model.navigation.map((p) => p.xValue)).toEqual([0, 1, 2]);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('adjacentFocusPoint', () => {
|
|
110
|
+
const definition = defineChart({
|
|
111
|
+
marks: [dot(data, { x: 'x', y: 'y' })],
|
|
112
|
+
scales,
|
|
113
|
+
});
|
|
114
|
+
const { model } = modelFor(definition);
|
|
115
|
+
const [first, second, third] = model.navigation;
|
|
116
|
+
|
|
117
|
+
it('starts at either end without a current point', () => {
|
|
118
|
+
expect(adjacentFocusPoint(model, null, 1)).toBe(first);
|
|
119
|
+
expect(adjacentFocusPoint(model, null, -1)).toBe(third);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('steps and stops at the ends', () => {
|
|
123
|
+
expect(adjacentFocusPoint(model, first!, 1)).toBe(second);
|
|
124
|
+
expect(adjacentFocusPoint(model, first!, -1)).toBe(first);
|
|
125
|
+
expect(adjacentFocusPoint(model, third!, 1)).toBe(third);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('returns null when there is nothing to step to', () => {
|
|
129
|
+
const empty = { ...model, navigation: [] };
|
|
130
|
+
expect(adjacentFocusPoint(empty, null, 1)).toBeNull();
|
|
131
|
+
});
|
|
132
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveChartFocusStrategy,
|
|
3
|
+
resolveChartPointerFocus,
|
|
4
|
+
restoreChartFocusPoint,
|
|
5
|
+
sameChartPointIdentity,
|
|
6
|
+
} from '@tanstack/charts/cursor/host';
|
|
7
|
+
import { focusDisabled } from '@tanstack/charts/focus/disabled';
|
|
8
|
+
import {
|
|
9
|
+
findNearestPoint,
|
|
10
|
+
viewportInteractionPoints,
|
|
11
|
+
} from '@tanstack/charts/scene';
|
|
12
|
+
import type {
|
|
13
|
+
ChartDefinition,
|
|
14
|
+
ChartFocusStrategy,
|
|
15
|
+
ChartPoint,
|
|
16
|
+
ChartScene,
|
|
17
|
+
} from '@tanstack/charts/types';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Which data points a touch lands on, and the order a screen reader steps
|
|
21
|
+
* through them.
|
|
22
|
+
*
|
|
23
|
+
* A port of TanStack's React Native focus model: it runs the definition's focus
|
|
24
|
+
* strategy (nearest point, a whole x column for `focus: 'x'`, …) over the
|
|
25
|
+
* scene's points rather than hit-testing painted shapes, which a single
|
|
26
|
+
* rasterized `<svg>` couldn't do anyway. One chart-level touch handler plus this
|
|
27
|
+
* model is also what TanStack recommends for native hosts, instead of a
|
|
28
|
+
* responder per mark.
|
|
29
|
+
*/
|
|
30
|
+
export type ChartFocusModel = {
|
|
31
|
+
/** The focused group for a touch at scene coordinates, or `[]`. */
|
|
32
|
+
resolve(x: number, y: number): readonly ChartPoint[];
|
|
33
|
+
/** The group that focusing `point` shows (its whole x column, say). */
|
|
34
|
+
group(point: ChartPoint): readonly ChartPoint[];
|
|
35
|
+
/** Points in reading order, for stepping with accessibility actions. */
|
|
36
|
+
readonly navigation: readonly ChartPoint[];
|
|
37
|
+
/** The same datum's point in a newer scene, or `null` if it's gone. */
|
|
38
|
+
restore(point: ChartPoint): ChartPoint | null;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/** TanStack's default reach for pointer focus, in scene pixels. */
|
|
42
|
+
const DEFAULT_MAX_FOCUS_DISTANCE = 48;
|
|
43
|
+
|
|
44
|
+
export const createChartFocusModel = (
|
|
45
|
+
scene: ChartScene<any, any, any>,
|
|
46
|
+
definition: ChartDefinition<any, any, any>,
|
|
47
|
+
): ChartFocusModel => {
|
|
48
|
+
const points = viewportInteractionPoints(scene);
|
|
49
|
+
const strategy: ChartFocusStrategy<any, any, any> | undefined =
|
|
50
|
+
definition.focus === false
|
|
51
|
+
? focusDisabled
|
|
52
|
+
: resolveChartFocusStrategy(definition.focus);
|
|
53
|
+
const spatialIndex =
|
|
54
|
+
definition.focus === false
|
|
55
|
+
? undefined
|
|
56
|
+
: definition.spatialIndex?.(points, { scene });
|
|
57
|
+
const maxDistance = definition.maxFocusDistance ?? DEFAULT_MAX_FOCUS_DISTANCE;
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
resolve(x, y) {
|
|
61
|
+
const focused = resolveChartPointerFocus(
|
|
62
|
+
scene,
|
|
63
|
+
strategy,
|
|
64
|
+
x,
|
|
65
|
+
y,
|
|
66
|
+
maxDistance,
|
|
67
|
+
points,
|
|
68
|
+
);
|
|
69
|
+
if (focused) return focused;
|
|
70
|
+
const point = spatialIndex
|
|
71
|
+
? spatialIndex.findNearest(x, y, maxDistance)
|
|
72
|
+
: findNearestPoint(scene, x, y, maxDistance, points);
|
|
73
|
+
const visible = point ? restoreChartFocusPoint(points, point) : null;
|
|
74
|
+
return visible ? [visible] : [];
|
|
75
|
+
},
|
|
76
|
+
group(point) {
|
|
77
|
+
return strategy?.group(points, { point }) ?? [point];
|
|
78
|
+
},
|
|
79
|
+
navigation: strategy?.navigation(points) ?? readingOrder(points),
|
|
80
|
+
restore(point) {
|
|
81
|
+
return restoreChartFocusPoint(points, point);
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/** The point `direction` steps away from `current` in navigation order. */
|
|
87
|
+
export const adjacentFocusPoint = (
|
|
88
|
+
model: ChartFocusModel,
|
|
89
|
+
current: ChartPoint | null,
|
|
90
|
+
direction: -1 | 1,
|
|
91
|
+
): ChartPoint | null => {
|
|
92
|
+
const { navigation } = model;
|
|
93
|
+
const index = current
|
|
94
|
+
? navigation.findIndex((point) => sameChartPointIdentity(point, current))
|
|
95
|
+
: -1;
|
|
96
|
+
const next =
|
|
97
|
+
index < 0
|
|
98
|
+
? direction > 0
|
|
99
|
+
? 0
|
|
100
|
+
: navigation.length - 1
|
|
101
|
+
: Math.max(0, Math.min(navigation.length - 1, index + direction));
|
|
102
|
+
return navigation[next] ?? null;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Left to right, then top to bottom; ties keep scene order.
|
|
107
|
+
*/
|
|
108
|
+
const readingOrder = (points: readonly ChartPoint[]): ChartPoint[] =>
|
|
109
|
+
points
|
|
110
|
+
.map((point, index) => ({ point, index }))
|
|
111
|
+
.sort(
|
|
112
|
+
(a, b) =>
|
|
113
|
+
a.point.x - b.point.x || a.point.y - b.point.y || a.index - b.index,
|
|
114
|
+
)
|
|
115
|
+
.map(({ point }) => point);
|