@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,69 +1,113 @@
|
|
|
1
|
-
import { Component } from '@angular/core';
|
|
1
|
+
import { Component, signal } from '@angular/core';
|
|
2
2
|
import { render, waitForUpdate } from '@blotch/angular-lynx-testing-library';
|
|
3
|
-
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';
|
|
4
3
|
import { describe, expect, it } from 'vitest';
|
|
5
|
-
import {
|
|
4
|
+
import { fireLynxEvent } from '../../../test-utils/fire-lynx-event';
|
|
6
5
|
import { UiAvatar } from './avatar';
|
|
7
6
|
|
|
7
|
+
type AvatarSize = 'default' | 'sm' | 'lg' | 'xl' | '2xl';
|
|
8
|
+
|
|
8
9
|
@Component({
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
imports: [UiAvatar],
|
|
11
|
+
template: `
|
|
12
|
+
<ui-avatar [src]="src()" fallback="AB" [size]="size()" class="border" />
|
|
13
|
+
`,
|
|
12
14
|
})
|
|
13
15
|
class AvatarHost {
|
|
14
|
-
src = '';
|
|
15
|
-
|
|
16
|
+
readonly src = signal('');
|
|
17
|
+
readonly size = signal<AvatarSize>('default');
|
|
16
18
|
}
|
|
17
19
|
|
|
20
|
+
const renderAvatar = async (src = '') => {
|
|
21
|
+
const result = await render(AvatarHost);
|
|
22
|
+
const host = result.componentRef.instance as AvatarHost;
|
|
23
|
+
host.src.set(src);
|
|
24
|
+
await waitForUpdate();
|
|
25
|
+
return {
|
|
26
|
+
...result,
|
|
27
|
+
host,
|
|
28
|
+
avatar: () => result.container.querySelector('.overflow-hidden')!,
|
|
29
|
+
image: () => result.container.querySelector('image'),
|
|
30
|
+
/** The pulsing placeholder over the image while it loads. */
|
|
31
|
+
skeleton: () => result.container.querySelector('.inset-0'),
|
|
32
|
+
initials: () => result.queryByText('AB'),
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
18
36
|
describe('UiAvatar', () => {
|
|
19
|
-
it('
|
|
20
|
-
const {
|
|
21
|
-
expect(
|
|
37
|
+
it('shows the initials in a muted circle without an image', async () => {
|
|
38
|
+
const { avatar, image, initials } = await renderAvatar();
|
|
39
|
+
expect(image()).toBeNull();
|
|
40
|
+
expect(initials()?.parentElement?.classList).toContain('bg-muted');
|
|
41
|
+
expect(avatar().classList).toContain('border');
|
|
22
42
|
});
|
|
23
43
|
|
|
24
|
-
it('
|
|
25
|
-
const {
|
|
26
|
-
|
|
44
|
+
it('covers the image with a placeholder while it loads', async () => {
|
|
45
|
+
const { image, skeleton, initials } = await renderAvatar(
|
|
46
|
+
'https://example.com/avatar.png',
|
|
47
|
+
);
|
|
48
|
+
expect(image()?.getAttribute('src')).toBe('https://example.com/avatar.png');
|
|
49
|
+
expect(skeleton()).not.toBeNull();
|
|
50
|
+
expect(initials()).toBeNull();
|
|
27
51
|
});
|
|
28
52
|
|
|
29
|
-
it('
|
|
30
|
-
|
|
31
|
-
// the @if(src()) branch fires and the image + skeleton render.
|
|
32
|
-
const { componentRef, container } = await render(UiAvatar);
|
|
33
|
-
setInputSignal(
|
|
34
|
-
(componentRef.instance as UiAvatar).src,
|
|
53
|
+
it('reveals the image once it loads', async () => {
|
|
54
|
+
const { image, skeleton, initials } = await renderAvatar(
|
|
35
55
|
'https://example.com/avatar.png',
|
|
36
56
|
);
|
|
57
|
+
fireLynxEvent(image()!, 'load');
|
|
37
58
|
await waitForUpdate();
|
|
38
|
-
expect(
|
|
59
|
+
expect(skeleton()).toBeNull();
|
|
60
|
+
expect(image()).not.toBeNull();
|
|
61
|
+
|
|
62
|
+
// Lynx can dispatch `load` again as the placeholder's animation stops:
|
|
63
|
+
// the avatar stays as it is.
|
|
64
|
+
fireLynxEvent(image()!, 'load');
|
|
65
|
+
await waitForUpdate();
|
|
66
|
+
expect(skeleton()).toBeNull();
|
|
67
|
+
expect(image()).not.toBeNull();
|
|
68
|
+
expect(initials()).toBeNull();
|
|
39
69
|
});
|
|
40
70
|
|
|
41
|
-
it('
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
expect(inst.loaded()).toBe(true);
|
|
71
|
+
it('falls back to the initials when the image fails to load', async () => {
|
|
72
|
+
const { image, skeleton, initials } = await renderAvatar(
|
|
73
|
+
'https://example.com/missing.png',
|
|
74
|
+
);
|
|
75
|
+
// Lynx can dispatch the event again before the avatar updates, as the
|
|
76
|
+
// placeholder's animation stops: the second one changes nothing.
|
|
77
|
+
fireLynxEvent(image()!, 'error', { detail: { errMsg: 'Not found' } });
|
|
78
|
+
fireLynxEvent(image()!, 'error', { detail: { errMsg: 'Not found' } });
|
|
79
|
+
await waitForUpdate();
|
|
80
|
+
expect(image()).toBeNull();
|
|
81
|
+
expect(skeleton()).toBeNull();
|
|
82
|
+
expect(initials()).not.toBeNull();
|
|
83
|
+
});
|
|
55
84
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
85
|
+
it.each<[AvatarSize, string, string]>([
|
|
86
|
+
['sm', 'h-8', 'text-sm'],
|
|
87
|
+
['default', 'h-10', 'text-sm'],
|
|
88
|
+
['lg', 'h-14', 'text-lg'],
|
|
89
|
+
['xl', 'h-20', 'text-2xl'],
|
|
90
|
+
['2xl', 'h-28', 'text-3xl'],
|
|
91
|
+
])('sizes a %s avatar and its initials', async (size, height, text) => {
|
|
92
|
+
const { host, avatar, initials } = await renderAvatar();
|
|
93
|
+
host.size.set(size);
|
|
94
|
+
await waitForUpdate();
|
|
95
|
+
expect(avatar().classList).toContain(height);
|
|
96
|
+
expect(initials()?.classList).toContain(text);
|
|
59
97
|
});
|
|
60
98
|
|
|
61
|
-
it
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
99
|
+
it.each<[AvatarSize, string]>([
|
|
100
|
+
['sm', 'h-8'],
|
|
101
|
+
['default', 'h-10'],
|
|
102
|
+
['lg', 'h-14'],
|
|
103
|
+
['xl', 'h-20'],
|
|
104
|
+
['2xl', 'h-28'],
|
|
105
|
+
])('sizes the image of a %s avatar', async (size, height) => {
|
|
106
|
+
const { host, image } = await renderAvatar(
|
|
107
|
+
'https://example.com/avatar.png',
|
|
108
|
+
);
|
|
109
|
+
host.size.set(size);
|
|
110
|
+
await waitForUpdate();
|
|
111
|
+
expect(image()?.classList).toContain(height);
|
|
68
112
|
});
|
|
69
113
|
});
|
|
@@ -1,25 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { describe, expect, it } from 'vitest';
|
|
4
|
-
import {
|
|
5
|
-
import { UiBadge, badgeVariants } from './badge';
|
|
1
|
+
import { Component, signal } from '@angular/core';
|
|
2
|
+
import { render, waitForUpdate } from '@blotch/angular-lynx-testing-library';
|
|
3
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { lynxMainThread } from '../../../test-utils/lynx-main-thread';
|
|
5
|
+
import { type BadgeVariant, UiBadge, badgeVariants } from './badge';
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
* Host wrappers project content and bind inputs the way real app templates do.
|
|
9
|
-
* Required-input components would need this; UiBadge has none but the pattern
|
|
10
|
-
* keeps content projection (<ng-content>) exercised.
|
|
11
|
-
*/
|
|
12
7
|
@Component({
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
8
|
+
imports: [UiBadge],
|
|
9
|
+
template: `
|
|
10
|
+
<ui-badge [variant]="variant()" [animated]="animated()" class="ml-1">
|
|
11
|
+
Status
|
|
12
|
+
</ui-badge>
|
|
13
|
+
`,
|
|
18
14
|
})
|
|
19
15
|
class BadgeHost {
|
|
20
|
-
variant
|
|
16
|
+
readonly variant = signal<BadgeVariant>('default');
|
|
17
|
+
readonly animated = signal(true);
|
|
21
18
|
}
|
|
22
19
|
|
|
20
|
+
/**
|
|
21
|
+
* The keyframes of the animations started on `element`.
|
|
22
|
+
*/
|
|
23
|
+
const keyframesOn = (calls: [Element, unknown[]][], element: Element) =>
|
|
24
|
+
calls
|
|
25
|
+
.filter(([target, [operation]]) => target === element && operation === 0)
|
|
26
|
+
.map(([, args]) => args[2]);
|
|
27
|
+
|
|
23
28
|
describe('badgeVariants', () => {
|
|
24
29
|
it('returns default variant classes', () => {
|
|
25
30
|
expect(badgeVariants()).toContain('bg-primary');
|
|
@@ -35,15 +40,44 @@ describe('badgeVariants', () => {
|
|
|
35
40
|
});
|
|
36
41
|
|
|
37
42
|
describe('UiBadge', () => {
|
|
38
|
-
it('renders
|
|
43
|
+
it('renders its label in the variant colors', async () => {
|
|
44
|
+
const { componentRef, getByText } = await render(BadgeHost);
|
|
45
|
+
const label = getByText('Status');
|
|
46
|
+
expect(label.classList).toContain('text-primary-foreground');
|
|
47
|
+
expect(label.parentElement?.classList).toContain('bg-primary');
|
|
48
|
+
expect(label.parentElement?.classList).toContain('ml-1');
|
|
49
|
+
|
|
50
|
+
(componentRef.instance as BadgeHost).variant.set('outline');
|
|
51
|
+
await waitForUpdate();
|
|
52
|
+
expect(label.classList).toContain('text-foreground');
|
|
53
|
+
expect(label.parentElement?.classList).toContain('border-border');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('pops in on first render', async () => {
|
|
57
|
+
const animate = vi.spyOn(lynxMainThread(), '__ElementAnimate');
|
|
39
58
|
const { getByText } = await render(BadgeHost);
|
|
40
|
-
|
|
59
|
+
const badge = getByText('Status').parentElement!;
|
|
60
|
+
expect(keyframesOn(animate.mock.calls, badge)).toEqual([
|
|
61
|
+
[
|
|
62
|
+
{ transform: 'scale(0.6)', opacity: 0 },
|
|
63
|
+
{ transform: 'scale(1)', opacity: 1 },
|
|
64
|
+
],
|
|
65
|
+
]);
|
|
66
|
+
animate.mockRestore();
|
|
41
67
|
});
|
|
42
68
|
|
|
43
|
-
it('
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
69
|
+
it("doesn't animate when animated is off", async () => {
|
|
70
|
+
@Component({
|
|
71
|
+
imports: [UiBadge],
|
|
72
|
+
template: `<ui-badge [animated]="false">Status</ui-badge>`,
|
|
73
|
+
})
|
|
74
|
+
class StillBadgeHost {}
|
|
75
|
+
|
|
76
|
+
const animate = vi.spyOn(lynxMainThread(), '__ElementAnimate');
|
|
77
|
+
const { getByText } = await render(StillBadgeHost);
|
|
78
|
+
expect(
|
|
79
|
+
keyframesOn(animate.mock.calls, getByText('Status').parentElement!),
|
|
80
|
+
).toEqual([]);
|
|
81
|
+
animate.mockRestore();
|
|
48
82
|
});
|
|
49
83
|
});
|
|
@@ -71,21 +71,19 @@ export class UiBadge {
|
|
|
71
71
|
readonly animated = input(true);
|
|
72
72
|
readonly userClass = input<string>('', { alias: 'class' });
|
|
73
73
|
|
|
74
|
-
readonly containerRef = viewChild<ElementRef>('container');
|
|
74
|
+
readonly containerRef = viewChild.required<ElementRef>('container');
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
});
|
|
88
|
-
}
|
|
76
|
+
/**
|
|
77
|
+
* Pops the badge in on first render, unless `animated` is off. A field
|
|
78
|
+
* rather than a constructor: Angular's test build keeps a function for a
|
|
79
|
+
* constructor that nothing calls, which an app's coverage report would show
|
|
80
|
+
* as uncovered.
|
|
81
|
+
*/
|
|
82
|
+
protected readonly entrance = effect(() => {
|
|
83
|
+
if (this.animated()) {
|
|
84
|
+
popIn(this.containerRef().nativeElement, { duration: 200 });
|
|
85
|
+
}
|
|
86
|
+
});
|
|
89
87
|
|
|
90
88
|
protected readonly containerClass = computed(() =>
|
|
91
89
|
cn(badgeVariants({ variant: this.variant() }), this.userClass()),
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import { Component, signal } from '@angular/core';
|
|
2
|
+
import {
|
|
3
|
+
fireEvent,
|
|
4
|
+
render,
|
|
5
|
+
waitForUpdate,
|
|
6
|
+
} from '@blotch/angular-lynx-testing-library';
|
|
1
7
|
import { createChartRuntime } from '@tanstack/charts/runtime';
|
|
2
8
|
import type {
|
|
3
9
|
ChartDefinition,
|
|
@@ -5,7 +11,7 @@ import type {
|
|
|
5
11
|
SceneRect,
|
|
6
12
|
} from '@tanstack/charts/types';
|
|
7
13
|
import { describe, expect, it } from 'vitest';
|
|
8
|
-
import { barChartDefinition } from './bar-chart';
|
|
14
|
+
import { type BarDatum, UiBarChart, barChartDefinition } from './bar-chart';
|
|
9
15
|
|
|
10
16
|
/**
|
|
11
17
|
* Every bar in a definition's scene.
|
|
@@ -95,3 +101,78 @@ describe('barChartDefinition', () => {
|
|
|
95
101
|
expect(found.map((bar) => bar.style?.fill)).toEqual(['red', 'blue', 'red']);
|
|
96
102
|
});
|
|
97
103
|
});
|
|
104
|
+
|
|
105
|
+
@Component({
|
|
106
|
+
imports: [UiBarChart],
|
|
107
|
+
template: `
|
|
108
|
+
<ui-bar-chart
|
|
109
|
+
[data]="data"
|
|
110
|
+
[width]="300"
|
|
111
|
+
[color]="color()"
|
|
112
|
+
[radius]="4"
|
|
113
|
+
(barTap)="tapped.push($event)"
|
|
114
|
+
/>
|
|
115
|
+
`,
|
|
116
|
+
})
|
|
117
|
+
class BarChartHost {
|
|
118
|
+
readonly data: readonly BarDatum[] = [
|
|
119
|
+
{ x: 'Mon', y: 5 },
|
|
120
|
+
{ x: 'Tue', y: 8 },
|
|
121
|
+
];
|
|
122
|
+
readonly color = signal('red');
|
|
123
|
+
/** Every `barTap`, in order. */
|
|
124
|
+
readonly tapped: BarDatum[] = [];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The SVG the chart's marks layer draws.
|
|
129
|
+
*/
|
|
130
|
+
const marksSvg = (container: Element) =>
|
|
131
|
+
container.querySelectorAll('svg')[1]!.getAttribute('content')!;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The middle of each bar the chart drew.
|
|
135
|
+
*/
|
|
136
|
+
const barsDrawn = (container: Element) =>
|
|
137
|
+
[
|
|
138
|
+
...marksSvg(container).matchAll(
|
|
139
|
+
/<rect x="([\d.-]+)" y="([\d.-]+)" width="([\d.-]+)" height="([\d.-]+)"[^>]*fill=/g,
|
|
140
|
+
),
|
|
141
|
+
].map(([, x, y, width, height]) => ({
|
|
142
|
+
x: Number(x) + Number(width) / 2,
|
|
143
|
+
y: Number(y) + Number(height) / 2,
|
|
144
|
+
}));
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Touches a spot of the chart and lifts the finger right there: a tap.
|
|
148
|
+
*/
|
|
149
|
+
const tap = async (container: Element, at: { x: number; y: number }) => {
|
|
150
|
+
const root = container.querySelector('[accessibility-label]')!;
|
|
151
|
+
fireEvent.touchstart(root, { touches: [at] });
|
|
152
|
+
fireEvent.touchend(root, { touches: [at] });
|
|
153
|
+
await waitForUpdate();
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
describe('UiBarChart', () => {
|
|
157
|
+
it('draws a bar per datum with its inputs', async () => {
|
|
158
|
+
const { componentRef, container, getByText } = await render(BarChartHost);
|
|
159
|
+
expect(barsDrawn(container)).toHaveLength(2);
|
|
160
|
+
expect(marksSvg(container)).toContain('fill="red"');
|
|
161
|
+
expect(marksSvg(container)).toContain('rx="4"');
|
|
162
|
+
expect(getByText('Mon')).toBeTruthy();
|
|
163
|
+
|
|
164
|
+
(componentRef.instance as BarChartHost).color.set('blue');
|
|
165
|
+
await waitForUpdate();
|
|
166
|
+
expect(marksSvg(container)).toContain('fill="blue"');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("emits a tapped bar's datum, and nothing for a tap off the bars", async () => {
|
|
170
|
+
const { componentRef, container } = await render(BarChartHost);
|
|
171
|
+
const host = componentRef.instance as BarChartHost;
|
|
172
|
+
await tap(container, barsDrawn(container)[1]!);
|
|
173
|
+
expect(host.tapped).toEqual([host.data[1]]);
|
|
174
|
+
|
|
175
|
+
await tap(container, { x: -100, y: -100 });
|
|
176
|
+
expect(host.tapped).toEqual([host.data[1]]);
|
|
177
|
+
});
|
|
178
|
+
});
|