@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.
Files changed (91) hide show
  1. package/dist/commands/install-packages.test.js +7 -2
  2. package/dist/commands/install-packages.test.js.map +1 -1
  3. package/dist/registry.js +8 -2
  4. package/dist/registry.js.map +1 -1
  5. package/dist/registry.test.js +1 -0
  6. package/dist/registry.test.js.map +1 -1
  7. package/dist/ui/components/accordion/accordion.test.ts +24 -7
  8. package/dist/ui/components/accordion/accordion.ts +34 -44
  9. package/dist/ui/components/action-sheet/action-sheet.test.ts +18 -1
  10. package/dist/ui/components/action-sheet/action-sheet.ts +55 -60
  11. package/dist/ui/components/alert/alert.test.ts +17 -0
  12. package/dist/ui/components/alert/alert.ts +23 -14
  13. package/dist/ui/components/alert-dialog/alert-dialog.test.ts +19 -4
  14. package/dist/ui/components/alert-dialog/alert-dialog.ts +31 -20
  15. package/dist/ui/components/aspect-ratio/aspect-ratio.test.ts +38 -2
  16. package/dist/ui/components/aspect-ratio/aspect-ratio.ts +11 -8
  17. package/dist/ui/components/avatar/avatar.test.ts +7 -0
  18. package/dist/ui/components/avatar/avatar.ts +35 -30
  19. package/dist/ui/components/badge/badge.test.ts +9 -0
  20. package/dist/ui/components/badge/badge.ts +12 -10
  21. package/dist/ui/components/bottom-sheet/bottom-sheet.test.ts +7 -0
  22. package/dist/ui/components/bottom-sheet/bottom-sheet.ts +5 -0
  23. package/dist/ui/components/button/button.test.ts +29 -3
  24. package/dist/ui/components/button/button.ts +67 -62
  25. package/dist/ui/components/button-group/button-group.test.ts +37 -4
  26. package/dist/ui/components/button-group/button-group.ts +60 -51
  27. package/dist/ui/components/card/card.test.ts +35 -2
  28. package/dist/ui/components/card/card.ts +41 -53
  29. package/dist/ui/components/chart/chart-presets.test.ts +16 -2
  30. package/dist/ui/components/chart/chart-presets.ts +22 -3
  31. package/dist/ui/components/chart/chart.test.ts +35 -6
  32. package/dist/ui/components/chart/chart.ts +14 -3
  33. package/dist/ui/components/checkbox/checkbox.test.ts +11 -4
  34. package/dist/ui/components/checkbox/checkbox.ts +21 -14
  35. package/dist/ui/components/collapsible/collapsible.test.ts +20 -10
  36. package/dist/ui/components/collapsible/collapsible.ts +29 -40
  37. package/dist/ui/components/dialog/dialog.test.ts +17 -1
  38. package/dist/ui/components/dialog/dialog.ts +24 -17
  39. package/dist/ui/components/dock/dock.test.ts +30 -5
  40. package/dist/ui/components/dock/dock.ts +29 -27
  41. package/dist/ui/components/empty-state/empty-state.test.ts +9 -0
  42. package/dist/ui/components/empty-state/empty-state.ts +20 -11
  43. package/dist/ui/components/icon/icon.test.ts +17 -0
  44. package/dist/ui/components/icon/icon.ts +19 -17
  45. package/dist/ui/components/input/input.test.ts +55 -3
  46. package/dist/ui/components/input/input.ts +91 -65
  47. package/dist/ui/components/label/label.test.ts +15 -0
  48. package/dist/ui/components/label/label.ts +12 -4
  49. package/dist/ui/components/list/list.test.ts +13 -3
  50. package/dist/ui/components/list/list.ts +14 -2
  51. package/dist/ui/components/menu/menu.test.ts +8 -0
  52. package/dist/ui/components/menu/menu.ts +4 -0
  53. package/dist/ui/components/nav-drawer/nav-drawer.test.ts +34 -21
  54. package/dist/ui/components/nav-drawer/nav-drawer.ts +60 -68
  55. package/dist/ui/components/navigation-bar/navigation-bar.test.ts +13 -0
  56. package/dist/ui/components/navigation-bar/navigation-bar.ts +5 -0
  57. package/dist/ui/components/pagination/pagination.test.ts +9 -0
  58. package/dist/ui/components/pagination/pagination.ts +52 -49
  59. package/dist/ui/components/popover/popover.test.ts +3 -0
  60. package/dist/ui/components/progress/progress.test.ts +7 -0
  61. package/dist/ui/components/progress/progress.ts +8 -6
  62. package/dist/ui/components/radio-group/radio-group.test.ts +8 -0
  63. package/dist/ui/components/radio-group/radio-group.ts +22 -19
  64. package/dist/ui/components/scroll-area/scroll-area.test.ts +14 -2
  65. package/dist/ui/components/scroll-area/scroll-area.ts +15 -3
  66. package/dist/ui/components/search-field/search-field.test.ts +25 -0
  67. package/dist/ui/components/search-field/search-field.ts +7 -1
  68. package/dist/ui/components/select/select.test.ts +32 -5
  69. package/dist/ui/components/select/select.ts +32 -11
  70. package/dist/ui/components/separator/separator.test.ts +3 -0
  71. package/dist/ui/components/sheet/sheet.test.ts +18 -0
  72. package/dist/ui/components/sheet/sheet.ts +24 -24
  73. package/dist/ui/components/skeleton/skeleton.test.ts +8 -0
  74. package/dist/ui/components/skeleton/skeleton.ts +13 -8
  75. package/dist/ui/components/slider/slider.test.ts +13 -1
  76. package/dist/ui/components/slider/slider.ts +16 -3
  77. package/dist/ui/components/spinner/spinner.test.ts +16 -1
  78. package/dist/ui/components/spinner/spinner.ts +15 -7
  79. package/dist/ui/components/switch/switch.test.ts +13 -2
  80. package/dist/ui/components/switch/switch.ts +15 -1
  81. package/dist/ui/components/tab-bar/tab-bar.test.ts +32 -6
  82. package/dist/ui/components/tab-bar/tab-bar.ts +28 -24
  83. package/dist/ui/components/tabs/tabs.test.ts +34 -32
  84. package/dist/ui/components/tabs/tabs.ts +56 -73
  85. package/dist/ui/components/textarea/textarea.test.ts +11 -1
  86. package/dist/ui/components/textarea/textarea.ts +77 -71
  87. package/dist/ui/components/toggle/toggle.test.ts +47 -16
  88. package/dist/ui/components/toggle/toggle.ts +78 -80
  89. package/dist/ui/components/typography/typography.test.ts +16 -0
  90. package/dist/ui/components/typography/typography.ts +12 -4
  91. 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
- <view #spinner [class]="containerClass()" [style]="sizeStyle()">
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 spinnerRef = viewChild.required<ElementRef>('spinner');
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.spinnerRef().nativeElement.animate(
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
- it('renders off, with the thumb at the left and the given class', async () => {
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
- const bar = container.querySelector('[accessibility-traits="tabbar"]')!;
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 into its content, the given class winning", async () => {
282
+ it("merges an item's class with its own, the given class winning", async () => {
271
283
  const { host, tab } = await renderTabBar();
272
- const content = () => tab('Inbox').querySelector('view')!;
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(content().classList).toContain('pt-3');
278
- expect(content().classList).not.toContain('pt-2');
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
- template: `
42
- <view accessibility-traits="tabbar" [class]="barClass()">
43
- <ng-content />
44
- </view>
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: 'flex-1 flex-col flex',
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 [class]="contentClass()">
126
- <view class="relative">
127
- <svg [attr.content]="iconSvg()" style="width: 24px; height: 24px;" />
128
- @if (badgeText()) {
129
- <!-- Hangs off the icon's top-right corner, as on a native tab bar.
130
- min-w-4 keeps a one-digit badge round; longer ones grow into
131
- a pill. -->
132
- <view
133
- class="h-4 min-w-4 items-center rounded-full bg-destructive px-1 flex absolute -right-2.5 -top-1 justify-center"
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
- <text
136
- class="text-[10px] font-semibold leading-4 text-destructive-foreground"
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's own view, which holds the tap listener, around its label.
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's own view, which wraps its projected text.
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's host element, which shows or hides it. */
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
- const hostOf = (text: string) => panel(text).parentElement as HTMLElement;
144
- expect(hostOf('Billing panel').style.overflow).toBe('hidden');
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 own view, around its label.
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(slot(label), { detail: { width } });
417
+ fireEvent.layoutchange(trigger(label), { detail: { width } });
415
418
  }
416
419
  await waitForUpdate();
417
420
  },
418
- /** The display of a panel's host element, which shows or hides it. */
421
+ /** The display of a panel, which shows or hides it. */
419
422
  displayOf: (text: string) =>
420
- (result.getByText(text).parentElement!.parentElement as HTMLElement)
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, slot, trigger, frame, getByText } = await renderSegmented();
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(slot('Albums').classList).toContain('flex-1');
430
- expect(slot('People').classList).toContain('flex-[2]');
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, slot, frame } = await renderSegmented();
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(slot('Photos').classList).not.toContain('flex-1');
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.parentElement!, { detail: { width: 80 } });
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.parentElement!.classList).not.toContain('flex-1');
533
+ expect(trigger.classList).not.toContain('flex-1');
532
534
  });
533
535
  });