@blotch/dolan 22.2.5 → 22.2.7
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/install-packages.test.js +7 -2
- package/dist/commands/install-packages.test.js.map +1 -1
- package/dist/registry.js +8 -2
- package/dist/registry.js.map +1 -1
- package/dist/registry.test.js +1 -0
- package/dist/registry.test.js.map +1 -1
- package/dist/ui/components/accordion/accordion.test.ts +24 -7
- package/dist/ui/components/accordion/accordion.ts +34 -44
- package/dist/ui/components/action-sheet/action-sheet.test.ts +18 -1
- package/dist/ui/components/action-sheet/action-sheet.ts +55 -60
- package/dist/ui/components/alert/alert.test.ts +17 -0
- package/dist/ui/components/alert/alert.ts +23 -14
- package/dist/ui/components/alert-dialog/alert-dialog.test.ts +19 -4
- package/dist/ui/components/alert-dialog/alert-dialog.ts +31 -20
- package/dist/ui/components/aspect-ratio/aspect-ratio.test.ts +38 -2
- package/dist/ui/components/aspect-ratio/aspect-ratio.ts +11 -8
- package/dist/ui/components/avatar/avatar.test.ts +7 -0
- package/dist/ui/components/avatar/avatar.ts +35 -30
- package/dist/ui/components/badge/badge.test.ts +9 -0
- package/dist/ui/components/badge/badge.ts +12 -10
- package/dist/ui/components/bottom-sheet/bottom-sheet.test.ts +7 -0
- package/dist/ui/components/bottom-sheet/bottom-sheet.ts +5 -0
- package/dist/ui/components/button/button.test.ts +29 -3
- package/dist/ui/components/button/button.ts +67 -62
- package/dist/ui/components/button-group/button-group.test.ts +37 -4
- package/dist/ui/components/button-group/button-group.ts +60 -51
- package/dist/ui/components/card/card.test.ts +35 -2
- package/dist/ui/components/card/card.ts +41 -53
- package/dist/ui/components/chart/chart-presets.test.ts +16 -2
- package/dist/ui/components/chart/chart-presets.ts +22 -3
- package/dist/ui/components/chart/chart.test.ts +35 -6
- package/dist/ui/components/chart/chart.ts +14 -3
- package/dist/ui/components/checkbox/checkbox.test.ts +11 -4
- package/dist/ui/components/checkbox/checkbox.ts +21 -14
- package/dist/ui/components/collapsible/collapsible.test.ts +20 -10
- package/dist/ui/components/collapsible/collapsible.ts +29 -40
- package/dist/ui/components/dialog/dialog.test.ts +17 -1
- package/dist/ui/components/dialog/dialog.ts +24 -17
- package/dist/ui/components/dock/dock.test.ts +30 -5
- package/dist/ui/components/dock/dock.ts +29 -27
- package/dist/ui/components/empty-state/empty-state.test.ts +9 -0
- package/dist/ui/components/empty-state/empty-state.ts +20 -11
- package/dist/ui/components/icon/icon.test.ts +17 -0
- package/dist/ui/components/icon/icon.ts +19 -17
- package/dist/ui/components/input/input.test.ts +55 -3
- package/dist/ui/components/input/input.ts +91 -65
- package/dist/ui/components/label/label.test.ts +15 -0
- package/dist/ui/components/label/label.ts +12 -4
- package/dist/ui/components/list/list.test.ts +13 -3
- package/dist/ui/components/list/list.ts +14 -2
- package/dist/ui/components/menu/menu.test.ts +8 -0
- package/dist/ui/components/menu/menu.ts +4 -0
- package/dist/ui/components/nav-drawer/nav-drawer.test.ts +34 -21
- package/dist/ui/components/nav-drawer/nav-drawer.ts +60 -68
- package/dist/ui/components/navigation-bar/navigation-bar.test.ts +13 -0
- package/dist/ui/components/navigation-bar/navigation-bar.ts +5 -0
- package/dist/ui/components/pagination/pagination.test.ts +9 -0
- package/dist/ui/components/pagination/pagination.ts +52 -49
- package/dist/ui/components/popover/popover.test.ts +3 -0
- package/dist/ui/components/progress/progress.test.ts +7 -0
- package/dist/ui/components/progress/progress.ts +8 -6
- package/dist/ui/components/radio-group/radio-group.test.ts +8 -0
- package/dist/ui/components/radio-group/radio-group.ts +22 -19
- package/dist/ui/components/scroll-area/scroll-area.test.ts +14 -2
- package/dist/ui/components/scroll-area/scroll-area.ts +15 -3
- package/dist/ui/components/search-field/search-field.test.ts +25 -0
- package/dist/ui/components/search-field/search-field.ts +7 -1
- package/dist/ui/components/select/select.test.ts +32 -5
- package/dist/ui/components/select/select.ts +32 -11
- package/dist/ui/components/separator/separator.test.ts +3 -0
- package/dist/ui/components/sheet/sheet.test.ts +18 -0
- package/dist/ui/components/sheet/sheet.ts +24 -24
- package/dist/ui/components/skeleton/skeleton.test.ts +8 -0
- package/dist/ui/components/skeleton/skeleton.ts +13 -8
- package/dist/ui/components/slider/slider.test.ts +13 -1
- package/dist/ui/components/slider/slider.ts +16 -3
- package/dist/ui/components/spinner/spinner.test.ts +16 -1
- package/dist/ui/components/spinner/spinner.ts +15 -7
- package/dist/ui/components/switch/switch.test.ts +13 -2
- package/dist/ui/components/switch/switch.ts +15 -1
- package/dist/ui/components/tab-bar/tab-bar.test.ts +32 -6
- package/dist/ui/components/tab-bar/tab-bar.ts +28 -24
- package/dist/ui/components/tabs/tabs.test.ts +34 -32
- package/dist/ui/components/tabs/tabs.ts +56 -73
- package/dist/ui/components/textarea/textarea.test.ts +11 -1
- package/dist/ui/components/textarea/textarea.ts +77 -71
- package/dist/ui/components/toggle/toggle.test.ts +47 -16
- package/dist/ui/components/toggle/toggle.ts +78 -80
- package/dist/ui/components/typography/typography.test.ts +16 -0
- package/dist/ui/components/typography/typography.ts +12 -4
- package/package.json +1 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type ElementRef,
|
|
3
2
|
Component,
|
|
3
|
+
ElementRef,
|
|
4
4
|
ViewEncapsulation,
|
|
5
5
|
computed,
|
|
6
6
|
effect,
|
|
7
|
+
inject,
|
|
7
8
|
input,
|
|
8
|
-
viewChild,
|
|
9
9
|
} from '@angular/core';
|
|
10
10
|
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';
|
|
11
11
|
|
|
@@ -20,15 +20,23 @@ const SIZE_MAP = { xs: 16, sm: 20, md: 24, lg: 32 } as const;
|
|
|
20
20
|
selector: 'ui-spinner',
|
|
21
21
|
imports: [LYNX_ELEMENTS],
|
|
22
22
|
encapsulation: ViewEncapsulation.None,
|
|
23
|
+
// The host is the box that turns, sized and styled through `host`, with the
|
|
24
|
+
// drawing inside. Angular gives a component's host every static attribute
|
|
25
|
+
// the app writes on it, so a class on <ui-spinner> lands on the host
|
|
26
|
+
// whatever the component does: with a styled box inside as well, a margin
|
|
27
|
+
// applied twice.
|
|
28
|
+
//
|
|
23
29
|
// SVG markup is passed via [attr.content] rather than as child elements.
|
|
24
30
|
// Lynx's native <svg> element can't contain child nodes in the flat element
|
|
25
31
|
// model — the renderer doesn't support nested SVG elements. Passing the full
|
|
26
32
|
// SVG markup string as the `content` attribute is the Lynx-native way to
|
|
27
33
|
// embed vector graphics.
|
|
34
|
+
host: {
|
|
35
|
+
'[class]': 'containerClass()',
|
|
36
|
+
'[style]': 'sizeStyle()',
|
|
37
|
+
},
|
|
28
38
|
template: `
|
|
29
|
-
<
|
|
30
|
-
<svg [attr.content]="svgContent()" style="width: 100%; height: 100%;" />
|
|
31
|
-
</view>
|
|
39
|
+
<svg [attr.content]="svgContent()" style="width: 100%; height: 100%;" />
|
|
32
40
|
`,
|
|
33
41
|
})
|
|
34
42
|
export class UiSpinner {
|
|
@@ -43,7 +51,7 @@ export class UiSpinner {
|
|
|
43
51
|
|
|
44
52
|
readonly #resolveColor = injectIconColor();
|
|
45
53
|
|
|
46
|
-
readonly
|
|
54
|
+
readonly #host = inject(ElementRef).nativeElement;
|
|
47
55
|
|
|
48
56
|
/**
|
|
49
57
|
* Turns the spinner for as long as it's shown. A field rather than a
|
|
@@ -51,7 +59,7 @@ export class UiSpinner {
|
|
|
51
59
|
* nothing calls, which an app's coverage report would show as uncovered.
|
|
52
60
|
*/
|
|
53
61
|
protected readonly rotation = effect(() => {
|
|
54
|
-
this.
|
|
62
|
+
this.#host.animate(
|
|
55
63
|
[{ transform: 'rotate(0deg)' }, { transform: 'rotate(360deg)' }],
|
|
56
64
|
{
|
|
57
65
|
duration: 1600,
|
|
@@ -119,10 +119,21 @@ describe('UiSwitch', () => {
|
|
|
119
119
|
settings.reduceTransparency.set(false);
|
|
120
120
|
});
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
// Static attributes land on a component's host too (issue #181): the
|
|
123
|
+
// margin applies once, and only the switch itself is named Wi-Fi.
|
|
124
|
+
it('puts its class and accessibility label on one element each', async () => {
|
|
125
|
+
const { container, track, getAllByAccessibilityLabel } =
|
|
126
|
+
await renderSwitch();
|
|
127
|
+
expect([...container.querySelectorAll('.ml-2')]).toEqual([
|
|
128
|
+
track().parentElement,
|
|
129
|
+
]);
|
|
130
|
+
expect(getAllByAccessibilityLabel('Wi-Fi')).toEqual([track()]);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('renders off, with the thumb at the left, in a host with the given class', async () => {
|
|
123
134
|
const { track, fill, thumb } = await renderSwitch();
|
|
124
135
|
expect(fill().classList).toContain('bg-input');
|
|
125
|
-
expect(track().classList).toContain('ml-2');
|
|
136
|
+
expect(track().parentElement!.classList).toContain('ml-2');
|
|
126
137
|
expect(style(track(), 'width')).toBe('44px');
|
|
127
138
|
expect(thumb().classList).toContain('bg-background');
|
|
128
139
|
// Centered 12px in: 2px from the edge for a 20px thumb.
|
|
@@ -264,6 +264,19 @@ type Press = {
|
|
|
264
264
|
selector: 'ui-switch',
|
|
265
265
|
imports: [LYNX_ELEMENTS, UiGlass],
|
|
266
266
|
encapsulation: ViewEncapsulation.None,
|
|
267
|
+
// The host holds the switch, with the app's class. Angular gives a
|
|
268
|
+
// component's host every static attribute the app writes on it, so a class
|
|
269
|
+
// on <ui-switch> lands on the host whatever the component does: passed on
|
|
270
|
+
// to the root as well, a margin applied twice. The root stays a view of its
|
|
271
|
+
// own: Lynx reads `consume-slide-event` as an array and
|
|
272
|
+
// `accessibility-element` as a boolean, which only a property binding on a
|
|
273
|
+
// Lynx element passes (a host binding would make them strings). Its
|
|
274
|
+
// accessibility-label is the root's, so the host drops its copy: two
|
|
275
|
+
// elements named the same would each be read.
|
|
276
|
+
host: {
|
|
277
|
+
'[class]': 'hostClass()',
|
|
278
|
+
'[attr.accessibility-label]': 'null',
|
|
279
|
+
},
|
|
267
280
|
// Only the root takes touches: what's inside ignores them, so every touch
|
|
268
281
|
// and its `x` belong to the switch.
|
|
269
282
|
//
|
|
@@ -406,12 +419,13 @@ export class UiSwitch implements FormCheckboxControl {
|
|
|
406
419
|
() => this.#systemDrawsGlass || !this.#settings.reduceTransparency(),
|
|
407
420
|
);
|
|
408
421
|
|
|
422
|
+
protected readonly hostClass = computed(() => cn(this.userClass()));
|
|
423
|
+
|
|
409
424
|
protected readonly rootClass = computed(() =>
|
|
410
425
|
cn(
|
|
411
426
|
'relative',
|
|
412
427
|
this.disabled() &&
|
|
413
428
|
(this.look() === 'material' ? 'opacity-[0.38]' : 'opacity-50'),
|
|
414
|
-
this.userClass(),
|
|
415
429
|
),
|
|
416
430
|
);
|
|
417
431
|
|
|
@@ -71,6 +71,16 @@ class TabBarHost {
|
|
|
71
71
|
readonly items = viewChildren(UiTabBarItem);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
@Component({
|
|
75
|
+
imports: [UiTabBar, UiTabBarItem],
|
|
76
|
+
template: `
|
|
77
|
+
<ui-tab-bar class="mt-1">
|
|
78
|
+
<ui-tab-bar-item route="/" icon="home" label="Home" class="pt-4" />
|
|
79
|
+
</ui-tab-bar>
|
|
80
|
+
`,
|
|
81
|
+
})
|
|
82
|
+
class StaticClassHost {}
|
|
83
|
+
|
|
74
84
|
/** The theme's light colors, as icon-theme.ts resolves them for an SVG. */
|
|
75
85
|
const PRIMARY = 'rgba(24, 24, 27, 1)';
|
|
76
86
|
const MUTED_FOREGROUND = 'rgba(113, 113, 122, 1)';
|
|
@@ -132,7 +142,9 @@ describe('UiTabBar', () => {
|
|
|
132
142
|
|
|
133
143
|
it('lays the bar out as a row above the bottom safe area', async () => {
|
|
134
144
|
const { container, componentRef } = await render(UiTabBar);
|
|
135
|
-
|
|
145
|
+
// Rendered on its own, the bar's host is the root the test renders into.
|
|
146
|
+
const bar = container;
|
|
147
|
+
expect(bar.getAttribute('accessibility-traits')).toBe('tabbar');
|
|
136
148
|
expect(bar.classList).toContain('flex-row');
|
|
137
149
|
expect(bar.classList).toContain('pb-safe');
|
|
138
150
|
expect(bar.classList).toContain('border-t');
|
|
@@ -267,14 +279,28 @@ describe('UiTabBar', () => {
|
|
|
267
279
|
expect(scale()).toBe('scale(1)');
|
|
268
280
|
});
|
|
269
281
|
|
|
270
|
-
it("merges an item's class
|
|
282
|
+
it("merges an item's class with its own, the given class winning", async () => {
|
|
271
283
|
const { host, tab } = await renderTabBar();
|
|
272
|
-
|
|
273
|
-
expect(content().classList).toContain('pt-2');
|
|
284
|
+
expect(tab('Inbox').classList).toContain('pt-2');
|
|
274
285
|
|
|
275
286
|
host.inboxClass.set('pt-3');
|
|
276
287
|
await waitForUpdate();
|
|
277
|
-
expect(
|
|
278
|
-
expect(
|
|
288
|
+
expect(tab('Inbox').classList).toContain('pt-3');
|
|
289
|
+
expect(tab('Inbox').classList).not.toContain('pt-2');
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// Angular gives a component's host every static attribute written on it,
|
|
293
|
+
// so a bar or item that also styled a view inside its host applied the
|
|
294
|
+
// class twice (issue #181).
|
|
295
|
+
it('puts a class written on the bar or an item on that one element', async () => {
|
|
296
|
+
const { container } = await render(StaticClassHost, {
|
|
297
|
+
providers: [provideRouter(routes)],
|
|
298
|
+
});
|
|
299
|
+
expect([...container.querySelectorAll('.mt-1')]).toEqual([
|
|
300
|
+
container.querySelector('[accessibility-traits="tabbar"]'),
|
|
301
|
+
]);
|
|
302
|
+
expect([...container.querySelectorAll('.pt-4')]).toEqual([
|
|
303
|
+
container.querySelector('[accessibility-label="Home"]'),
|
|
304
|
+
]);
|
|
279
305
|
});
|
|
280
306
|
});
|
|
@@ -27,8 +27,14 @@ import { type IconName, ICONS } from '../icon/icons';
|
|
|
27
27
|
*/
|
|
28
28
|
@Component({
|
|
29
29
|
selector: 'ui-tab-bar',
|
|
30
|
-
imports: [LYNX_ELEMENTS],
|
|
31
30
|
encapsulation: ViewEncapsulation.None,
|
|
31
|
+
// The bar and each item are their host elements, styled through `host`,
|
|
32
|
+
// rather than a view inside the host. Angular gives a component's host
|
|
33
|
+
// every static attribute the app writes on it, so `<ui-tab-bar
|
|
34
|
+
// class="pt-2">` puts `pt-2` on the host whatever the component does with
|
|
35
|
+
// its `class` input: with a styled view inside as well, the padding applied
|
|
36
|
+
// twice.
|
|
37
|
+
//
|
|
32
38
|
// `pb-safe` pads the bar by the bottom safe-area inset, so the items sit
|
|
33
39
|
// above the iPhone's home indicator while the bar's background still runs
|
|
34
40
|
// to the bottom edge, as a native tab bar's does. Lynx reports no insets on
|
|
@@ -38,11 +44,11 @@ import { type IconName, ICONS } from '../icon/icons';
|
|
|
38
44
|
// native tab bar's container has: it tells VoiceOver that the buttons in it
|
|
39
45
|
// are tabs. Android reads a single trait and has no tab bar trait, so there
|
|
40
46
|
// the row stays a plain container.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
host: {
|
|
48
|
+
'accessibility-traits': 'tabbar',
|
|
49
|
+
'[class]': 'barClass()',
|
|
50
|
+
},
|
|
51
|
+
template: '<ng-content />',
|
|
46
52
|
})
|
|
47
53
|
export class UiTabBar {
|
|
48
54
|
readonly userClass = input<string>('', { alias: 'class' });
|
|
@@ -97,7 +103,7 @@ export class UiTabBar {
|
|
|
97
103
|
// `flex-1` has to be on the host, the row's real child: on an inner view
|
|
98
104
|
// it would share out the host's width instead (same lesson as
|
|
99
105
|
// ui-nav-drawer-content).
|
|
100
|
-
class: '
|
|
106
|
+
'[class]': 'contentClass()',
|
|
101
107
|
// One accessibility element per tab, named by its label, so a screen
|
|
102
108
|
// reader reads "Inbox, 3, button" instead of an unlabeled SVG and a word.
|
|
103
109
|
// LynxRouterLinkActive adds `selected` to `button` while the route is
|
|
@@ -122,25 +128,23 @@ export class UiTabBar {
|
|
|
122
128
|
'(bindtap)': 'pressed.emit()',
|
|
123
129
|
},
|
|
124
130
|
template: `
|
|
125
|
-
<view
|
|
126
|
-
<
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
<view class="relative">
|
|
132
|
+
<svg [attr.content]="iconSvg()" style="width: 24px; height: 24px;" />
|
|
133
|
+
@if (badgeText()) {
|
|
134
|
+
<!-- Hangs off the icon's top-right corner, as on a native tab bar.
|
|
135
|
+
min-w-4 keeps a one-digit badge round; longer ones grow into
|
|
136
|
+
a pill. -->
|
|
137
|
+
<view
|
|
138
|
+
class="h-4 min-w-4 items-center rounded-full bg-destructive px-1 flex absolute -right-2.5 -top-1 justify-center"
|
|
139
|
+
>
|
|
140
|
+
<text
|
|
141
|
+
class="text-[10px] font-semibold leading-4 text-destructive-foreground"
|
|
142
|
+
>{{ badgeText() }}</text
|
|
134
143
|
>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
>{{ badgeText() }}</text
|
|
138
|
-
>
|
|
139
|
-
</view>
|
|
140
|
-
}
|
|
141
|
-
</view>
|
|
142
|
-
<text [class]="labelClass()">{{ label() }}</text>
|
|
144
|
+
</view>
|
|
145
|
+
}
|
|
143
146
|
</view>
|
|
147
|
+
<text [class]="labelClass()">{{ label() }}</text>
|
|
144
148
|
`,
|
|
145
149
|
})
|
|
146
150
|
export class UiTabBarItem {
|
|
@@ -57,11 +57,11 @@ class TabsHost {
|
|
|
57
57
|
const renderTabs = async () => {
|
|
58
58
|
const result = await render(TabsHost);
|
|
59
59
|
/**
|
|
60
|
-
* A trigger
|
|
60
|
+
* A trigger: its host, which holds the tap listener, around its label.
|
|
61
61
|
*/
|
|
62
62
|
const trigger = (label: string) => result.getByText(label).parentElement!;
|
|
63
63
|
/**
|
|
64
|
-
* A panel
|
|
64
|
+
* A panel: its host, around its projected text.
|
|
65
65
|
*/
|
|
66
66
|
const panel = (text: string) => result.getByText(text).parentElement!;
|
|
67
67
|
return {
|
|
@@ -69,9 +69,8 @@ const renderTabs = async () => {
|
|
|
69
69
|
host: result.componentRef.instance as TabsHost,
|
|
70
70
|
trigger,
|
|
71
71
|
panel,
|
|
72
|
-
/** The display of a panel
|
|
73
|
-
displayOf: (text: string) =>
|
|
74
|
-
(panel(text).parentElement as HTMLElement).style.display,
|
|
72
|
+
/** The display of a panel, which shows or hides it. */
|
|
73
|
+
displayOf: (text: string) => panel(text).style.display,
|
|
75
74
|
tap: async (label: string) => {
|
|
76
75
|
fireEvent.tap(trigger(label));
|
|
77
76
|
await waitForUpdate();
|
|
@@ -87,8 +86,6 @@ const CANCEL = 3;
|
|
|
87
86
|
describe('UiTabs', () => {
|
|
88
87
|
it("merges each part's class with its own", async () => {
|
|
89
88
|
const { container, trigger, panel } = await renderTabs();
|
|
90
|
-
// Each host element carries the given class too, so look for the view
|
|
91
|
-
// that has both its own and the given class.
|
|
92
89
|
expect(container.querySelector('.flex-col.w-80')).not.toBeNull();
|
|
93
90
|
expect(container.querySelector('.rounded-lg.mb-2')).not.toBeNull();
|
|
94
91
|
expect(
|
|
@@ -100,6 +97,21 @@ describe('UiTabs', () => {
|
|
|
100
97
|
expect(panel('Account panel').classList).toContain('gap-2');
|
|
101
98
|
});
|
|
102
99
|
|
|
100
|
+
// Angular gives a component's host every static attribute written on it,
|
|
101
|
+
// so a part that also styled a view inside its host applied the class
|
|
102
|
+
// twice (issue #181).
|
|
103
|
+
it('puts a class written on a part on that one element', async () => {
|
|
104
|
+
const { container, trigger, panel } = await renderTabs();
|
|
105
|
+
expect(container.querySelectorAll('.w-80')).toHaveLength(1);
|
|
106
|
+
expect(container.querySelectorAll('.mb-2')).toHaveLength(1);
|
|
107
|
+
expect([...container.querySelectorAll('.px-4')]).toEqual([
|
|
108
|
+
trigger('Account'),
|
|
109
|
+
]);
|
|
110
|
+
expect([...container.querySelectorAll('.gap-2')]).toEqual([
|
|
111
|
+
panel('Account panel'),
|
|
112
|
+
]);
|
|
113
|
+
});
|
|
114
|
+
|
|
103
115
|
it("shows only the selected tab's panel, and highlights its trigger", async () => {
|
|
104
116
|
const { getByText, trigger, displayOf } = await renderTabs();
|
|
105
117
|
expect(displayOf('Account panel')).toBe('flex');
|
|
@@ -140,9 +152,8 @@ describe('UiTabs', () => {
|
|
|
140
152
|
|
|
141
153
|
it('clips a hidden panel, whose text iOS would draw from its corner', async () => {
|
|
142
154
|
const { panel } = await renderTabs();
|
|
143
|
-
|
|
144
|
-
expect(
|
|
145
|
-
expect(hostOf('Account panel').style.overflow).toBe('');
|
|
155
|
+
expect(panel('Billing panel').style.overflow).toBe('hidden');
|
|
156
|
+
expect(panel('Account panel').style.overflow).toBe('');
|
|
146
157
|
});
|
|
147
158
|
|
|
148
159
|
it('shows the tab its value is set to, without reporting a change', async () => {
|
|
@@ -287,10 +298,7 @@ describe('UiTabs', () => {
|
|
|
287
298
|
await waitForUpdate();
|
|
288
299
|
expect(animate).not.toHaveBeenCalled();
|
|
289
300
|
expect(
|
|
290
|
-
(
|
|
291
|
-
result.getByText('Billing panel').parentElement!
|
|
292
|
-
.parentElement as HTMLElement
|
|
293
|
-
).style.display,
|
|
301
|
+
result.getByText('Billing panel').parentElement!.style.display,
|
|
294
302
|
).toBe('flex');
|
|
295
303
|
});
|
|
296
304
|
|
|
@@ -385,14 +393,10 @@ describe('UiTabsList, segmented', () => {
|
|
|
385
393
|
providers: [{ provide: LynxAccessibilitySettings, useValue: settings }],
|
|
386
394
|
});
|
|
387
395
|
/**
|
|
388
|
-
* A trigger's
|
|
396
|
+
* A trigger: its host, the track's flex item, around its label.
|
|
389
397
|
*/
|
|
390
398
|
const trigger = (label: string) => result.getByText(label).parentElement!;
|
|
391
|
-
|
|
392
|
-
* A trigger's host, the track's flex item.
|
|
393
|
-
*/
|
|
394
|
-
const slot = (label: string) => trigger(label).parentElement!;
|
|
395
|
-
const track = () => slot('Photos').parentElement!;
|
|
399
|
+
const track = () => trigger('Photos').parentElement!;
|
|
396
400
|
/**
|
|
397
401
|
* The indicator's frame, which moves between the tabs.
|
|
398
402
|
*/
|
|
@@ -402,7 +406,6 @@ describe('UiTabsList, segmented', () => {
|
|
|
402
406
|
...result,
|
|
403
407
|
host: result.componentRef.instance as SegmentedTabsHost,
|
|
404
408
|
trigger,
|
|
405
|
-
slot,
|
|
406
409
|
track,
|
|
407
410
|
frame,
|
|
408
411
|
measure: async () => {
|
|
@@ -411,23 +414,22 @@ describe('UiTabsList, segmented', () => {
|
|
|
411
414
|
['Albums', 90],
|
|
412
415
|
['People', 180],
|
|
413
416
|
] as const) {
|
|
414
|
-
fireEvent.layoutchange(
|
|
417
|
+
fireEvent.layoutchange(trigger(label), { detail: { width } });
|
|
415
418
|
}
|
|
416
419
|
await waitForUpdate();
|
|
417
420
|
},
|
|
418
|
-
/** The display of a panel
|
|
421
|
+
/** The display of a panel, which shows or hides it. */
|
|
419
422
|
displayOf: (text: string) =>
|
|
420
|
-
|
|
421
|
-
.style.display,
|
|
423
|
+
result.getByText(text).parentElement!.style.display,
|
|
422
424
|
};
|
|
423
425
|
};
|
|
424
426
|
|
|
425
427
|
it("draws the platform's segmented control, filling its width, its tabs sharing it", async () => {
|
|
426
|
-
const { track,
|
|
428
|
+
const { track, trigger, frame, getByText } = await renderSegmented();
|
|
427
429
|
expect(track().classList).toContain('rounded-full');
|
|
428
430
|
expect(track().classList).not.toContain('self-start');
|
|
429
|
-
expect(
|
|
430
|
-
expect(
|
|
431
|
+
expect(trigger('Albums').classList).toContain('flex-1');
|
|
432
|
+
expect(trigger('People').classList).toContain('flex-[2]');
|
|
431
433
|
expect(trigger('Photos').classList).toContain('justify-center');
|
|
432
434
|
expect(getByText('Photos').classList).toContain('font-medium');
|
|
433
435
|
expect(getByText('Albums').classList).not.toContain('font-medium');
|
|
@@ -508,18 +510,18 @@ describe('UiTabsList, segmented', () => {
|
|
|
508
510
|
});
|
|
509
511
|
|
|
510
512
|
it('goes back to the default tabs when the variant changes', async () => {
|
|
511
|
-
const { host, track,
|
|
513
|
+
const { host, track, trigger, frame } = await renderSegmented();
|
|
512
514
|
host.variant.set('default');
|
|
513
515
|
await waitForUpdate();
|
|
514
516
|
expect(track().classList).toContain('self-start');
|
|
515
|
-
expect(
|
|
517
|
+
expect(trigger('Photos').classList).not.toContain('flex-1');
|
|
516
518
|
expect(frame()).toBeNull();
|
|
517
519
|
});
|
|
518
520
|
|
|
519
521
|
it('lets a trigger outside a list take touches as the default tabs do', async () => {
|
|
520
522
|
const result = await render(LoneTriggerHost);
|
|
521
523
|
const trigger = result.getByText('Alone').parentElement!;
|
|
522
|
-
fireEvent.layoutchange(trigger
|
|
524
|
+
fireEvent.layoutchange(trigger, { detail: { width: 80 } });
|
|
523
525
|
fireEvent.touchstart(trigger, { touches: [{ pageX: 10 }] });
|
|
524
526
|
fireEvent.touchmove(trigger, { touches: [{ pageX: 60 }] });
|
|
525
527
|
fireEvent.touchend(trigger);
|
|
@@ -528,6 +530,6 @@ describe('UiTabsList, segmented', () => {
|
|
|
528
530
|
fireEvent.tap(trigger);
|
|
529
531
|
await waitForUpdate();
|
|
530
532
|
expect(trigger.classList).toContain('bg-background');
|
|
531
|
-
expect(trigger.
|
|
533
|
+
expect(trigger.classList).not.toContain('flex-1');
|
|
532
534
|
});
|
|
533
535
|
});
|