@blotch/dolan 22.2.3 → 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.js +9 -1
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/add.test.js +36 -3
- package/dist/commands/add.test.js.map +1 -1
- package/dist/commands/doctor.js +187 -7
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/doctor.test.js +280 -7
- package/dist/commands/doctor.test.js.map +1 -1
- package/dist/commands/init.d.ts +2 -2
- package/dist/commands/init.js +38 -18
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/init.test.js +107 -1
- package/dist/commands/init.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 +5 -0
- package/dist/commands/update.js +126 -45
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/update.test.js +314 -2
- package/dist/commands/update.test.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 +6 -0
- package/dist/registry.js +48 -2
- package/dist/registry.js.map +1 -1
- package/dist/registry.test.js +45 -3
- 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 +5 -4
- package/dist/testing/set-input-signal.js +5 -4
- package/dist/testing/set-input-signal.js.map +1 -1
- 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 +272 -187
- package/dist/ui/components/action-sheet/action-sheet.ts +40 -34
- 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 +206 -139
- package/dist/ui/components/alert-dialog/alert-dialog.ts +32 -26
- package/dist/ui/components/area-chart/area-chart.test.ts +81 -1
- 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 +82 -1
- package/dist/ui/components/bottom-sheet/bottom-sheet.test.ts +373 -297
- package/dist/ui/components/bottom-sheet/bottom-sheet.ts +34 -33
- package/dist/ui/components/button/button.test.ts +611 -2
- package/dist/ui/components/button/button.ts +431 -62
- 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 +82 -1
- package/dist/ui/components/card/card.test.ts +82 -62
- package/dist/ui/components/chart/chart-presets.test.ts +197 -1
- package/dist/ui/components/chart/chart-presets.ts +8 -1
- package/dist/ui/components/chart/chart-theme.ts +12 -0
- package/dist/ui/components/chart/chart.test.ts +1017 -0
- package/dist/ui/components/chart/chart.ts +56 -43
- 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 +210 -152
- package/dist/ui/components/dialog/dialog.ts +29 -23
- 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 +36 -0
- package/dist/ui/components/icon/icon-theme.ts +36 -13
- package/dist/ui/components/icon/icon.test.ts +52 -41
- 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/line-chart.test.ts +76 -1
- package/dist/ui/components/list/list.test.ts +80 -49
- 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 +425 -260
- package/dist/ui/components/nav-drawer/nav-drawer.ts +72 -56
- package/dist/ui/components/pagination/pagination.test.ts +136 -86
- 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/scatter-chart.test.ts +74 -1
- package/dist/ui/components/scroll-area/scroll-area.test.ts +72 -36
- 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 +64 -46
- package/dist/ui/components/spinner/spinner.ts +16 -19
- 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 +434 -350
- package/dist/ui/components/toast/toast.ts +42 -51
- package/dist/ui/components/toggle/toggle.test.ts +247 -140
- package/dist/ui/theme/chart-colors.test.ts +8 -3
- package/dist/ui/theme/dark.css +38 -2
- package/dist/ui/theme/default.css +55 -7
- package/dist/ui/theme/icon-colors.test.ts +8 -3
- 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 +93 -29
- 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 +12 -0
- package/dist/utils/installed.js +16 -0
- package/dist/utils/installed.js.map +1 -1
- package/dist/utils/installed.test.js +17 -1
- package/dist/utils/installed.test.js.map +1 -1
- package/dist/utils/resolve-paths.d.ts +4 -4
- package/dist/utils/resolve-paths.js +5 -7
- package/dist/utils/resolve-paths.js.map +1 -1
- package/dist/utils/resolve-paths.test.js +9 -5
- 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 +9 -1
- package/dist/ui/components/area-chart/area-chart-render.test.ts +0 -63
- package/dist/ui/components/bar-chart/bar-chart-render.test.ts +0 -58
- package/dist/ui/components/button/button-render.test.ts +0 -132
- 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 -59
- package/dist/ui/components/chart/chart-render.test.ts +0 -730
- package/dist/ui/components/line-chart/line-chart-render.test.ts +0 -83
- package/dist/ui/components/scatter-chart/scatter-chart-render.test.ts +0 -64
|
@@ -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
|
});
|
|
@@ -1,5 +1,30 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
Injector,
|
|
4
|
+
type Type,
|
|
5
|
+
computed,
|
|
6
|
+
input,
|
|
7
|
+
output,
|
|
8
|
+
signal,
|
|
9
|
+
viewChild,
|
|
10
|
+
} from '@angular/core';
|
|
11
|
+
import {
|
|
12
|
+
fireEvent,
|
|
13
|
+
render,
|
|
14
|
+
waitForUpdate,
|
|
15
|
+
} from '@blotch/angular-lynx-testing-library';
|
|
16
|
+
import { defineChart, dot } from '@tanstack/charts/universal';
|
|
1
17
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import {
|
|
18
|
+
import { UiChart, tooltip } from './chart';
|
|
19
|
+
import {
|
|
20
|
+
type CartesianPresetOptions,
|
|
21
|
+
PRESET_CHART_BINDINGS,
|
|
22
|
+
UiCartesianPreset,
|
|
23
|
+
type XYDatum,
|
|
24
|
+
linearAxis,
|
|
25
|
+
minSpacing,
|
|
26
|
+
presetAxes,
|
|
27
|
+
} from './chart-presets';
|
|
3
28
|
|
|
4
29
|
/**
|
|
5
30
|
* The configured domain of an axis config's scale instance.
|
|
@@ -137,3 +162,174 @@ describe('presetAxes', () => {
|
|
|
137
162
|
expect(domainOf(axes.y)).toEqual([-10, 10]);
|
|
138
163
|
});
|
|
139
164
|
});
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The smallest preset: a dot per datum on the shared axes, built the way the
|
|
168
|
+
* library's presets (`<ui-line-chart>`, …) build theirs.
|
|
169
|
+
*/
|
|
170
|
+
@Component({
|
|
171
|
+
selector: 'test-dot-chart',
|
|
172
|
+
imports: [UiChart],
|
|
173
|
+
template: `
|
|
174
|
+
<ui-chart
|
|
175
|
+
#chart
|
|
176
|
+
[definition]="definition()"
|
|
177
|
+
${PRESET_CHART_BINDINGS}
|
|
178
|
+
(pointSelect)="pointTap.emit(datumOf($event))"
|
|
179
|
+
/>
|
|
180
|
+
`,
|
|
181
|
+
})
|
|
182
|
+
class DotChart extends UiCartesianPreset<XYDatum> {
|
|
183
|
+
readonly data = input.required<readonly XYDatum[]>();
|
|
184
|
+
/** The tapped datum, or `null` for a tap on empty space. */
|
|
185
|
+
readonly pointTap = output<XYDatum | null>();
|
|
186
|
+
|
|
187
|
+
protected readonly definition = computed(() => {
|
|
188
|
+
const options = this.commonOptions();
|
|
189
|
+
const data = this.data();
|
|
190
|
+
return defineChart({
|
|
191
|
+
marks: [dot(data, { x: 'x', y: 'y' })],
|
|
192
|
+
scales: presetAxes(options, {
|
|
193
|
+
x: data.map((d) => d.x),
|
|
194
|
+
y: data.map((d) => d.y),
|
|
195
|
+
}),
|
|
196
|
+
tooltip: options.tooltip === false ? false : tooltip,
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
@Component({
|
|
202
|
+
imports: [DotChart],
|
|
203
|
+
template: `
|
|
204
|
+
<test-dot-chart
|
|
205
|
+
[data]="data"
|
|
206
|
+
[width]="300"
|
|
207
|
+
[height]="160"
|
|
208
|
+
label="Sales"
|
|
209
|
+
class="mt-2"
|
|
210
|
+
[xAxisLabel]="xAxisLabel()"
|
|
211
|
+
yAxisLabel="Units"
|
|
212
|
+
[zoomable]="true"
|
|
213
|
+
zoomAxes="x"
|
|
214
|
+
(pointTap)="tapped.push($event)"
|
|
215
|
+
/>
|
|
216
|
+
`,
|
|
217
|
+
})
|
|
218
|
+
class PresetHost {
|
|
219
|
+
readonly data: readonly XYDatum[] = [
|
|
220
|
+
{ x: 0, y: 100 },
|
|
221
|
+
{ x: 5, y: 300 },
|
|
222
|
+
{ x: 10, y: 500 },
|
|
223
|
+
];
|
|
224
|
+
readonly xAxisLabel = signal('Day');
|
|
225
|
+
/** Every `pointTap`, in order. */
|
|
226
|
+
readonly tapped: (XYDatum | null)[] = [];
|
|
227
|
+
readonly preset = viewChild.required(DotChart);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* The chart's own view, which takes every touch.
|
|
232
|
+
*/
|
|
233
|
+
const chartRoot = (container: Element) =>
|
|
234
|
+
container.querySelector('[accessibility-label]')!;
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Where the chart drew each dot, read from its marks layer.
|
|
238
|
+
*/
|
|
239
|
+
const dotsDrawn = (container: Element) =>
|
|
240
|
+
[
|
|
241
|
+
...container
|
|
242
|
+
.querySelectorAll('svg')[1]!
|
|
243
|
+
.getAttribute('content')!
|
|
244
|
+
.matchAll(/<circle cx="([\d.-]+)" cy="([\d.-]+)"/g),
|
|
245
|
+
].map(([, x, y]) => ({ x: Number(x), y: Number(y) }));
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Touches a spot and lifts the finger right there: a tap.
|
|
249
|
+
*/
|
|
250
|
+
const tap = async (root: Element, at: { x: number; y: number }) => {
|
|
251
|
+
fireEvent.touchstart(root, { touches: [at] });
|
|
252
|
+
fireEvent.touchend(root, { touches: [at] });
|
|
253
|
+
await waitForUpdate();
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
describe('UiCartesianPreset', () => {
|
|
257
|
+
it('starts every preset from the same defaults', () => {
|
|
258
|
+
// Angular builds a preset through the base's factory, with the preset's
|
|
259
|
+
// own type, so no preset runs that factory's default: build the base alone.
|
|
260
|
+
const base = Injector.create({
|
|
261
|
+
providers: [
|
|
262
|
+
{
|
|
263
|
+
provide: UiCartesianPreset,
|
|
264
|
+
useClass: UiCartesianPreset as unknown as Type<
|
|
265
|
+
UiCartesianPreset<XYDatum>
|
|
266
|
+
>,
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
}).get(UiCartesianPreset);
|
|
270
|
+
const options = (
|
|
271
|
+
base as unknown as { commonOptions(): CartesianPresetOptions }
|
|
272
|
+
).commonOptions();
|
|
273
|
+
expect(options).toEqual({
|
|
274
|
+
tickCount: 5,
|
|
275
|
+
showGrid: true,
|
|
276
|
+
showXGrid: false,
|
|
277
|
+
xAxisLabel: '',
|
|
278
|
+
yAxisLabel: '',
|
|
279
|
+
padding: {},
|
|
280
|
+
xMin: undefined,
|
|
281
|
+
xMax: undefined,
|
|
282
|
+
yMin: undefined,
|
|
283
|
+
yMax: undefined,
|
|
284
|
+
xTickFormat: undefined,
|
|
285
|
+
yTickFormat: undefined,
|
|
286
|
+
tooltip: true,
|
|
287
|
+
});
|
|
288
|
+
expect(base.height()).toBe(200);
|
|
289
|
+
expect(base.label()).toBe('Chart');
|
|
290
|
+
expect(base.zoomable()).toBe(false);
|
|
291
|
+
expect(base.zoomAxes()).toBe('xy');
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('passes the shared inputs through to its chart', async () => {
|
|
295
|
+
const { container, componentRef, getByText } = await render(PresetHost);
|
|
296
|
+
const root = chartRoot(container);
|
|
297
|
+
expect(root.getAttribute('accessibility-label')).toBe('Sales');
|
|
298
|
+
expect(root.getAttribute('class')).toContain('mt-2');
|
|
299
|
+
expect(root.getAttribute('style')).toContain('height: 160px');
|
|
300
|
+
expect(getByText('Units')).toBeTruthy();
|
|
301
|
+
expect(getByText('Day')).toBeTruthy();
|
|
302
|
+
|
|
303
|
+
(componentRef.instance as PresetHost).xAxisLabel.set('Week');
|
|
304
|
+
await waitForUpdate();
|
|
305
|
+
expect(getByText('Week')).toBeTruthy();
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('hands a tap over as the datum, or null off the points', async () => {
|
|
309
|
+
const { container, componentRef } = await render(PresetHost);
|
|
310
|
+
const host = componentRef.instance as PresetHost;
|
|
311
|
+
const root = chartRoot(container);
|
|
312
|
+
const middle = dotsDrawn(container)[1]!;
|
|
313
|
+
await tap(root, { x: middle.x + 1, y: middle.y + 1 });
|
|
314
|
+
await tap(root, { x: -100, y: -100 });
|
|
315
|
+
expect(host.tapped).toEqual([host.data[1], null]);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('zooms its chart in and out, and resets it', async () => {
|
|
319
|
+
const { componentRef, queryByText } = await render(PresetHost);
|
|
320
|
+
const preset = (componentRef.instance as PresetHost).preset();
|
|
321
|
+
// The x axis spans 0–10; zoomed in around the middle, its last tick goes.
|
|
322
|
+
expect(queryByText('10')).not.toBeNull();
|
|
323
|
+
preset.zoomIn();
|
|
324
|
+
await waitForUpdate();
|
|
325
|
+
expect(queryByText('10')).toBeNull();
|
|
326
|
+
preset.zoomOut();
|
|
327
|
+
await waitForUpdate();
|
|
328
|
+
expect(queryByText('10')).not.toBeNull();
|
|
329
|
+
|
|
330
|
+
preset.zoomIn();
|
|
331
|
+
preset.resetZoom();
|
|
332
|
+
await waitForUpdate();
|
|
333
|
+
expect(queryByText('10')).not.toBeNull();
|
|
334
|
+
});
|
|
335
|
+
});
|
|
@@ -173,6 +173,13 @@ export const minSpacing = (values: readonly number[]): number => {
|
|
|
173
173
|
return Number.isFinite(spacing) && spacing > 0 ? spacing : 1;
|
|
174
174
|
};
|
|
175
175
|
|
|
176
|
+
/**
|
|
177
|
+
* `UiChart`, for `viewChild()`. A call rather than the class itself: for a
|
|
178
|
+
* class, Angular's test build keeps an arrow function that nothing calls,
|
|
179
|
+
* which an app's coverage report would show as uncovered.
|
|
180
|
+
*/
|
|
181
|
+
const chartType = () => UiChart;
|
|
182
|
+
|
|
176
183
|
/**
|
|
177
184
|
* The inputs and zoom API every cartesian preset shares. A preset extends this
|
|
178
185
|
* and adds its own data and mark inputs, and renders `<ui-chart #chart>`
|
|
@@ -212,7 +219,7 @@ export abstract class UiCartesianPreset<TDatum> {
|
|
|
212
219
|
readonly zoomStep = input(1.4);
|
|
213
220
|
readonly userClass = input<string>('', { alias: 'class' });
|
|
214
221
|
|
|
215
|
-
protected readonly chart = viewChild.required(
|
|
222
|
+
protected readonly chart = viewChild.required(chartType());
|
|
216
223
|
|
|
217
224
|
/**
|
|
218
225
|
* The shared inputs, as the options every preset's definition takes.
|
|
@@ -79,6 +79,12 @@ export const CHART_LIGHT_COLORS: ChartColorTokens = {
|
|
|
79
79
|
'chart-3': 'rgba(39, 71, 84, 1)',
|
|
80
80
|
'chart-4': 'rgba(232, 196, 104, 1)',
|
|
81
81
|
'chart-5': 'rgba(244, 164, 98, 1)',
|
|
82
|
+
'glass-tint': 'rgba(24, 24, 27, 1)',
|
|
83
|
+
'glass-tint-foreground': 'rgba(250, 250, 250, 1)',
|
|
84
|
+
'glass-foreground': 'rgba(9, 9, 11, 1)',
|
|
85
|
+
'glass-muted-foreground': 'rgba(63, 63, 70, 1)',
|
|
86
|
+
'glass-clear-foreground': 'rgba(255, 255, 255, 1)',
|
|
87
|
+
'glass-dim': 'rgba(0, 0, 0, 0.35)',
|
|
82
88
|
};
|
|
83
89
|
|
|
84
90
|
export const CHART_DARK_COLORS: ChartColorTokens = {
|
|
@@ -106,6 +112,12 @@ export const CHART_DARK_COLORS: ChartColorTokens = {
|
|
|
106
112
|
'chart-3': 'rgba(232, 140, 48, 1)',
|
|
107
113
|
'chart-4': 'rgba(175, 87, 219, 1)',
|
|
108
114
|
'chart-5': 'rgba(226, 54, 112, 1)',
|
|
115
|
+
'glass-tint': 'rgba(250, 250, 250, 1)',
|
|
116
|
+
'glass-tint-foreground': 'rgba(24, 24, 27, 1)',
|
|
117
|
+
'glass-foreground': 'rgba(250, 250, 250, 1)',
|
|
118
|
+
'glass-muted-foreground': 'rgba(212, 212, 216, 1)',
|
|
119
|
+
'glass-clear-foreground': 'rgba(255, 255, 255, 1)',
|
|
120
|
+
'glass-dim': 'rgba(0, 0, 0, 0.35)',
|
|
109
121
|
};
|
|
110
122
|
|
|
111
123
|
/**
|