@gjsify/adwaita-web 0.31.0 → 0.32.0

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 (176) hide show
  1. package/dist/adwaita-web.css +786 -272
  2. package/dist/adwaita-web.css.map +1 -1
  3. package/lib/types/adw-about-dialog.spec.d.ts +1 -0
  4. package/lib/types/adw-action-rows.spec.d.ts +1 -0
  5. package/lib/types/adw-avatar.spec.d.ts +1 -0
  6. package/lib/types/adw-banner.spec.d.ts +1 -0
  7. package/lib/types/adw-button-content.spec.d.ts +1 -0
  8. package/lib/types/adw-carousel.spec.d.ts +1 -0
  9. package/lib/types/adw-checks.spec.d.ts +1 -0
  10. package/lib/types/adw-header-bar.spec.d.ts +1 -0
  11. package/lib/types/adw-icon.spec.d.ts +1 -0
  12. package/lib/types/adw-progress-bar.spec.d.ts +1 -0
  13. package/lib/types/adw-sidebar.spec.d.ts +1 -0
  14. package/lib/types/adw-switch.spec.d.ts +1 -0
  15. package/lib/types/adw-wrap-box.spec.d.ts +1 -0
  16. package/lib/types/bottom-sheet.spec.d.ts +1 -0
  17. package/lib/types/breakpoints.d.ts +22 -0
  18. package/lib/types/breakpoints.spec.d.ts +1 -0
  19. package/lib/types/chrome.spec.d.ts +1 -0
  20. package/lib/types/elements/adw-about-dialog.d.ts +47 -8
  21. package/lib/types/elements/adw-action-row.d.ts +35 -0
  22. package/lib/types/elements/adw-avatar.d.ts +11 -0
  23. package/lib/types/elements/adw-bottom-sheet.d.ts +33 -4
  24. package/lib/types/elements/adw-button-content.d.ts +12 -0
  25. package/lib/types/elements/adw-button-row.d.ts +10 -1
  26. package/lib/types/elements/adw-carousel.d.ts +111 -8
  27. package/lib/types/elements/adw-checks.d.ts +57 -0
  28. package/lib/types/elements/adw-clamp.d.ts +4 -1
  29. package/lib/types/elements/adw-data-grid.d.ts +11 -31
  30. package/lib/types/elements/adw-drop-down.d.ts +17 -20
  31. package/lib/types/elements/adw-entry-row.d.ts +93 -3
  32. package/lib/types/elements/adw-expander-row.d.ts +7 -1
  33. package/lib/types/elements/adw-header-bar.d.ts +15 -0
  34. package/lib/types/elements/adw-icon.d.ts +25 -0
  35. package/lib/types/elements/adw-inline-view-switcher.d.ts +37 -11
  36. package/lib/types/elements/adw-menu-button.d.ts +6 -6
  37. package/lib/types/elements/adw-navigation-split-view.d.ts +6 -0
  38. package/lib/types/elements/adw-navigation-view.d.ts +63 -20
  39. package/lib/types/elements/adw-overlay-split-view.d.ts +38 -0
  40. package/lib/types/elements/adw-password-entry-row.d.ts +19 -10
  41. package/lib/types/elements/adw-popover.d.ts +58 -0
  42. package/lib/types/elements/adw-preferences-dialog.d.ts +57 -2
  43. package/lib/types/elements/adw-preferences-group.d.ts +31 -0
  44. package/lib/types/elements/adw-progress-bar.d.ts +38 -0
  45. package/lib/types/elements/adw-sidebar.d.ts +37 -5
  46. package/lib/types/elements/adw-split-button.d.ts +53 -7
  47. package/lib/types/elements/adw-switch-row.d.ts +11 -2
  48. package/lib/types/elements/adw-switch.d.ts +15 -0
  49. package/lib/types/elements/adw-tab-view.d.ts +129 -12
  50. package/lib/types/elements/adw-toolbar-view.d.ts +5 -0
  51. package/lib/types/elements/adw-view-stack.d.ts +27 -19
  52. package/lib/types/elements/adw-view-switcher-bar.d.ts +23 -7
  53. package/lib/types/elements/adw-view-switcher.d.ts +22 -11
  54. package/lib/types/elements/adw-window-title.d.ts +3 -1
  55. package/lib/types/elements/view-switcher-dom.d.ts +44 -0
  56. package/lib/types/entry-rows.spec.d.ts +1 -0
  57. package/lib/types/index.d.ts +7 -0
  58. package/lib/types/navigation-view.spec.d.ts +1 -0
  59. package/lib/types/preferences-search.d.ts +20 -0
  60. package/lib/types/preferences.spec.d.ts +1 -0
  61. package/lib/types/split-button.spec.d.ts +1 -0
  62. package/lib/types/split-views.spec.d.ts +1 -0
  63. package/lib/types/styles.generated.d.ts +1 -1
  64. package/lib/types/tab-view.spec.d.ts +1 -0
  65. package/lib/types/view-stack.spec.d.ts +1 -0
  66. package/lib/types/view-switcher.spec.d.ts +1 -0
  67. package/package.json +6 -6
  68. package/scss/_banner.scss +13 -0
  69. package/scss/_bottom_sheet.scss +5 -6
  70. package/scss/_button.scss +3 -26
  71. package/scss/_button_content.scss +28 -0
  72. package/scss/_checks.scss +228 -0
  73. package/scss/_clamp.scss +9 -3
  74. package/scss/_data_grid.scss +15 -7
  75. package/scss/_drop_down.scss +12 -55
  76. package/scss/_expander_row.scss +5 -50
  77. package/scss/_icon.scss +51 -0
  78. package/scss/_icons.generated.scss +6 -0
  79. package/scss/_inline_view_switcher.scss +34 -0
  80. package/scss/_labels.scss +135 -0
  81. package/scss/_menu_button.scss +10 -70
  82. package/scss/_popover.scss +186 -0
  83. package/scss/_preferences.scss +29 -0
  84. package/scss/_preferences_dialog.scss +17 -0
  85. package/scss/_progress_bar.scss +161 -0
  86. package/scss/_reset.scss +18 -7
  87. package/scss/_spinner.scss +7 -3
  88. package/scss/_split_button.scss +6 -50
  89. package/scss/_switch.scss +90 -0
  90. package/scss/_switch_row.scss +6 -52
  91. package/scss/_tab_view.scss +64 -0
  92. package/scss/_theme.scss +10 -0
  93. package/scss/_toolbar_view.scss +74 -0
  94. package/scss/_variables.scss +34 -0
  95. package/scss/_view_switcher.scss +40 -0
  96. package/scss/_view_switcher_bar.scss +37 -0
  97. package/scss/_wrap_box.scss +56 -6
  98. package/scss/adwaita-skin.scss +23 -0
  99. package/src/adw-about-dialog.spec.ts +350 -0
  100. package/src/adw-action-rows.spec.ts +342 -0
  101. package/src/adw-avatar.spec.ts +139 -0
  102. package/src/adw-banner.spec.ts +152 -0
  103. package/src/adw-button-content.spec.ts +183 -0
  104. package/src/adw-button-row.spec.ts +12 -6
  105. package/src/adw-carousel.spec.ts +389 -0
  106. package/src/adw-checks.spec.ts +368 -0
  107. package/src/adw-drop-down.spec.ts +123 -0
  108. package/src/adw-header-bar.spec.ts +83 -0
  109. package/src/adw-icon.spec.ts +167 -0
  110. package/src/adw-progress-bar.spec.ts +173 -0
  111. package/src/adw-sidebar.spec.ts +332 -0
  112. package/src/adw-switch.spec.ts +242 -0
  113. package/src/adw-tab-view.spec.ts +12 -3
  114. package/src/adw-view-switcher-bar.spec.ts +61 -13
  115. package/src/adw-wrap-box.spec.ts +194 -0
  116. package/src/bottom-sheet.spec.ts +325 -0
  117. package/src/breakpoints.spec.ts +147 -0
  118. package/src/breakpoints.ts +67 -0
  119. package/src/chrome.spec.ts +301 -0
  120. package/src/elements/adw-about-dialog.ts +223 -163
  121. package/src/elements/adw-action-row.ts +120 -10
  122. package/src/elements/adw-avatar.ts +48 -54
  123. package/src/elements/adw-banner.ts +46 -10
  124. package/src/elements/adw-bottom-sheet.ts +145 -60
  125. package/src/elements/adw-button-content.ts +107 -33
  126. package/src/elements/adw-button-row.ts +61 -37
  127. package/src/elements/adw-button.ts +5 -7
  128. package/src/elements/adw-carousel.ts +467 -105
  129. package/src/elements/adw-checks.ts +299 -0
  130. package/src/elements/adw-clamp.ts +80 -10
  131. package/src/elements/adw-combo-row.ts +15 -7
  132. package/src/elements/adw-data-grid.ts +43 -91
  133. package/src/elements/adw-drop-down.ts +117 -128
  134. package/src/elements/adw-entry-row.ts +404 -36
  135. package/src/elements/adw-expander-row.ts +61 -29
  136. package/src/elements/adw-header-bar.ts +31 -6
  137. package/src/elements/adw-icon.ts +148 -0
  138. package/src/elements/adw-inline-view-switcher.ts +240 -129
  139. package/src/elements/adw-menu-button.ts +93 -70
  140. package/src/elements/adw-navigation-split-view.ts +48 -7
  141. package/src/elements/adw-navigation-view.ts +286 -148
  142. package/src/elements/adw-overlay-split-view.ts +164 -14
  143. package/src/elements/adw-password-entry-row.ts +111 -89
  144. package/src/elements/adw-popover.ts +283 -0
  145. package/src/elements/adw-preferences-dialog.ts +120 -21
  146. package/src/elements/adw-preferences-group.ts +128 -22
  147. package/src/elements/adw-progress-bar.ts +189 -0
  148. package/src/elements/adw-sidebar.ts +301 -142
  149. package/src/elements/adw-spin-row.ts +11 -5
  150. package/src/elements/adw-spinner.ts +23 -8
  151. package/src/elements/adw-split-button.ts +371 -92
  152. package/src/elements/adw-status-page.ts +9 -9
  153. package/src/elements/adw-switch-row.ts +100 -30
  154. package/src/elements/adw-switch.ts +129 -0
  155. package/src/elements/adw-tab-view.ts +682 -129
  156. package/src/elements/adw-toggle-group.ts +5 -7
  157. package/src/elements/adw-toolbar-view.ts +84 -21
  158. package/src/elements/adw-view-stack.ts +162 -85
  159. package/src/elements/adw-view-switcher-bar.ts +163 -81
  160. package/src/elements/adw-view-switcher.ts +231 -112
  161. package/src/elements/adw-window-title.ts +44 -11
  162. package/src/elements/adw-wrap-box.ts +131 -35
  163. package/src/elements/view-switcher-dom.ts +108 -0
  164. package/src/entry-rows.spec.ts +391 -0
  165. package/src/index.ts +23 -0
  166. package/src/navigation-view.spec.ts +435 -0
  167. package/src/preferences-search.ts +135 -0
  168. package/src/preferences.spec.ts +250 -0
  169. package/src/source-view/adw-source-view.ts +2 -5
  170. package/src/split-button.spec.ts +470 -0
  171. package/src/split-views.spec.ts +309 -0
  172. package/src/styles.generated.ts +1 -1
  173. package/src/tab-view.spec.ts +451 -0
  174. package/src/test.browser.mts +49 -0
  175. package/src/view-stack.spec.ts +272 -0
  176. package/src/view-switcher.spec.ts +446 -0
@@ -0,0 +1,272 @@
1
+ // DOM-level conformance tests for <adw-view-stack>, driven by the SAME vectors
2
+ // the core suite and the NativeScript renderer assert against
3
+ // (`@gjsify/adwaita-core/conformance`).
4
+ //
5
+ // The two renderers used to carry independent copies of the selection logic —
6
+ // the same four-clause index guard, the same `findIndex` by name, the same
7
+ // `?? ''` fallbacks — and both had drifted away from libadwaita in the same
8
+ // ways: `visibleChildIndex = 1.5` passed the `Number.isFinite` guard and blanked
9
+ // the entire stack, the per-page `visible` flag did not exist so the auto-pick
10
+ // took the first page ADDED instead of the first VISIBLE one, an empty
11
+ // `visible-child-name` was treated as "no name" by the declarative path but as a
12
+ // legal lookup by the property path, and the auto-pick notified nothing so a
13
+ // bound switcher needed a manual `refresh()`. Nothing failed, because nothing
14
+ // compared them. This suite is that comparison.
15
+ import { describe, expect, it } from '@gjsify/unit';
16
+
17
+ import type { ViewStackVectorChange, ViewStackVectorOp, ViewStackVectorPage } from '@gjsify/adwaita-core/conformance';
18
+ import {
19
+ VIEW_STACK_ICON_NAME_VECTORS,
20
+ VIEW_STACK_PAGE_VECTORS,
21
+ VIEW_STACK_VECTORS,
22
+ } from '@gjsify/adwaita-core/conformance';
23
+
24
+ import type { AdwViewStack } from './elements/adw-view-stack.js';
25
+
26
+ /** A stack mounted from markup, with every `notify::visible-child` recorded. */
27
+ interface MountedStack {
28
+ stack: AdwViewStack;
29
+ host: HTMLElement;
30
+ changes: ViewStackVectorChange[];
31
+ }
32
+
33
+ /**
34
+ * Mount a stack whose pages are DECLARED as markup, driving the attribute path.
35
+ *
36
+ * Attributes are set through `setAttribute` rather than an HTML string on
37
+ * purpose: several vectors hinge on an exact name — the empty string, the NFD
38
+ * spelling of `Übersicht` — which an attribute value in parsed markup is not a
39
+ * reliable carrier for. The change listener is attached BEFORE the element is
40
+ * connected so the auto-pick notification is observable, which is the whole
41
+ * point of the fix that made it fire at all.
42
+ */
43
+ function mountStack(pages: readonly ViewStackVectorPage[], visibleChildName?: string): MountedStack {
44
+ const host = document.createElement('div');
45
+ document.body.appendChild(host);
46
+
47
+ const stack = document.createElement('adw-view-stack') as AdwViewStack;
48
+ if (visibleChildName !== undefined) stack.setAttribute('visible-child-name', visibleChildName);
49
+
50
+ for (const page of pages) {
51
+ const pageEl = document.createElement('adw-view-stack-page');
52
+ pageEl.setAttribute('name', page.name);
53
+ if (page.title !== undefined) pageEl.setAttribute('title', page.title);
54
+ if (page.icon !== undefined) pageEl.setAttribute('icon-name', page.icon);
55
+ if (page.visible === false) pageEl.setAttribute('hidden', '');
56
+ const body = document.createElement('p');
57
+ body.className = 'page-body';
58
+ body.textContent = page.name;
59
+ pageEl.appendChild(body);
60
+ stack.appendChild(pageEl);
61
+ }
62
+
63
+ const changes: ViewStackVectorChange[] = [];
64
+ stack.addEventListener('notify::visible-child', (event) => {
65
+ changes.push((event as CustomEvent<ViewStackVectorChange>).detail);
66
+ });
67
+
68
+ host.appendChild(stack);
69
+ return { stack, host, changes };
70
+ }
71
+
72
+ /** Apply one vector op through the element's own public surface. */
73
+ function applyOp(stack: AdwViewStack, op: ViewStackVectorOp): void {
74
+ switch (op.kind) {
75
+ case 'selectIndex':
76
+ stack.visibleChildIndex = op.index;
77
+ break;
78
+ case 'selectName':
79
+ stack.visibleChildName = op.name;
80
+ break;
81
+ case 'setPageVisible':
82
+ stack.setPageVisible(op.name, op.visible);
83
+ break;
84
+ case 'removePage':
85
+ stack.removePage(op.name);
86
+ break;
87
+ }
88
+ }
89
+
90
+ /** Which page wrappers are actually SHOWN — what the user sees, not what the model says. */
91
+ function shownPages(stack: AdwViewStack): boolean[] {
92
+ const wrappers = Array.from(stack.querySelectorAll(':scope > .adw-view-stack-child')) as HTMLElement[];
93
+ return wrappers.map((wrapper) => !wrapper.hidden);
94
+ }
95
+
96
+ export const AdwViewStackTest = async () => {
97
+ await describe('adw-view-stack selection (libadwaita conformance vectors)', async () => {
98
+ for (const vector of VIEW_STACK_VECTORS) {
99
+ await it(vector.rule, () => {
100
+ const { stack, host, changes } = mountStack(vector.pages);
101
+
102
+ expect(changes).toStrictEqual([...vector.setupChanges]);
103
+
104
+ changes.length = 0;
105
+ for (const op of vector.ops) applyOp(stack, op);
106
+
107
+ expect(changes).toStrictEqual([...vector.changes]);
108
+ expect(stack.visibleChildIndex).toBe(vector.visibleIndex);
109
+ expect(stack.visibleChildName).toBe(vector.visibleName);
110
+ expect(stack.pages.length).toBe(vector.count);
111
+ expect(stack.pages[stack.visibleChildIndex]?.title ?? '').toBe(vector.visibleTitle);
112
+
113
+ // The DOM must agree with the model: exactly the selected page is
114
+ // shown, and nothing at all when the selection is -1. A stack that
115
+ // stored an unreachable index (the 1.5 bug) showed NOTHING while
116
+ // still reporting a selection.
117
+ const expectedShown = vector.pages.map(() => false).slice(0, vector.count);
118
+ if (vector.visibleIndex >= 0) expectedShown[vector.visibleIndex] = true;
119
+ expect(shownPages(stack)).toStrictEqual(expectedShown);
120
+ expect(stack.visibleChild).toBe(
121
+ vector.visibleIndex >= 0 ? (stack.pages[vector.visibleIndex]?.content ?? null) : null,
122
+ );
123
+
124
+ host.remove();
125
+ });
126
+ }
127
+ });
128
+
129
+ await describe('adw-view-stack page descriptors (libadwaita conformance vectors)', async () => {
130
+ for (const vector of VIEW_STACK_PAGE_VECTORS) {
131
+ await it(vector.rule, () => {
132
+ const { stack, host } = mountStack([vector.page]);
133
+ const page = stack.pages[0]!;
134
+ expect(page.name).toBe(vector.page.name);
135
+ expect(page.title).toBe(vector.title);
136
+ expect(page.icon).toBe(vector.icon);
137
+ expect(page.visible).toBe(vector.visible);
138
+ host.remove();
139
+ });
140
+ }
141
+
142
+ for (const { icon, normalized, rule } of VIEW_STACK_ICON_NAME_VECTORS) {
143
+ // `null`/`undefined` have no attribute spelling — an absent icon-name
144
+ // is the same input and is covered by the page-descriptor rows above.
145
+ if (typeof icon !== 'string') continue;
146
+ await it(`icon-name="${icon}" → ${JSON.stringify(normalized)} — ${rule}`, () => {
147
+ const { stack, host } = mountStack([{ name: 'a', icon }]);
148
+ expect(stack.pages[0]!.icon).toBe(normalized);
149
+ host.remove();
150
+ });
151
+ }
152
+
153
+ await it('adopts the declared markup as the page BODY, dropping the wrapper tag', () => {
154
+ const { stack, host } = mountStack([{ name: 'learn' }, { name: 'code' }]);
155
+ expect(stack.querySelectorAll('adw-view-stack-page').length).toBe(0);
156
+ expect(stack.pages[0]!.content!.querySelector('.page-body')!.textContent).toBe('learn');
157
+ host.remove();
158
+ });
159
+ });
160
+
161
+ await describe('adw-view-stack imperative pages (Adw.ViewStack.add)', async () => {
162
+ await it('keeps pages added BEFORE the stack enters the document', () => {
163
+ // The regression: connectedCallback used to overwrite the page list
164
+ // with whatever <adw-view-stack-page> children it found — none, for
165
+ // imperative use — and then replaceChildren() the subtree away, so
166
+ // this exact order silently destroyed the caller's element.
167
+ // libadwaita has no parse-once phase (adw-view-stack.c:2160-2251).
168
+ const stack = document.createElement('adw-view-stack') as AdwViewStack;
169
+ const content = document.createElement('div');
170
+ content.textContent = 'learn body';
171
+ stack.add(content, 'learn', 'Learn');
172
+
173
+ document.body.appendChild(stack);
174
+
175
+ expect(stack.pages.length).toBe(1);
176
+ expect(stack.visibleChildName).toBe('learn');
177
+ expect(content.isConnected).toBe(true);
178
+ expect(content.hidden).toBe(false);
179
+ stack.remove();
180
+ });
181
+
182
+ await it('notifies for the auto-shown first page, so a bound switcher needs no refresh()', () => {
183
+ const stack = document.createElement('adw-view-stack') as AdwViewStack;
184
+ const changes: ViewStackVectorChange[] = [];
185
+ stack.addEventListener('notify::visible-child', (event) => {
186
+ changes.push((event as CustomEvent<ViewStackVectorChange>).detail);
187
+ });
188
+ document.body.appendChild(stack);
189
+
190
+ stack.add(document.createElement('div'), 'learn', 'Learn');
191
+ stack.add(document.createElement('div'), 'code', 'Code');
192
+
193
+ expect(changes).toStrictEqual([{ index: 0, name: 'learn', title: 'Learn', interactive: false }]);
194
+ stack.remove();
195
+ });
196
+
197
+ await it('detaches the content when a page is removed', () => {
198
+ const stack = document.createElement('adw-view-stack') as AdwViewStack;
199
+ document.body.appendChild(stack);
200
+ const first = document.createElement('div');
201
+ const second = document.createElement('div');
202
+ stack.add(first, 'learn');
203
+ stack.add(second, 'code');
204
+
205
+ expect(stack.removePage('learn')).toBe(true);
206
+ expect(first.isConnected).toBe(false);
207
+ expect(second.isConnected).toBe(true);
208
+ // The removed page WAS the visible one, so nothing is shown now.
209
+ expect(stack.visibleChildIndex).toBe(-1);
210
+ expect(second.hidden).toBe(true);
211
+ stack.remove();
212
+ });
213
+ });
214
+
215
+ await describe('adw-view-stack visible-child-name attribute', async () => {
216
+ await it('selects the declared page instead of the auto-pick', () => {
217
+ const { stack, host } = mountStack([{ name: 'learn' }, { name: 'code' }], 'code');
218
+ expect(stack.visibleChildName).toBe('code');
219
+ expect(shownPages(stack)).toStrictEqual([false, true]);
220
+ host.remove();
221
+ });
222
+
223
+ await it('treats an EMPTY declared name as a legal lookup, not as "no name"', () => {
224
+ // The declarative path used `initialName ? … : -1`, so an explicit
225
+ // empty name fell through to page 0 while the PROPERTY path matched
226
+ // the page named '' — one port, two answers for one input.
227
+ // g_strcmp0("", "") is 0 (adw-view-stack.c:901).
228
+ const { stack, host } = mountStack([{ name: 'a' }, { name: '' }], '');
229
+ expect(stack.visibleChildIndex).toBe(1);
230
+ expect(shownPages(stack)).toStrictEqual([false, true]);
231
+ host.remove();
232
+ });
233
+
234
+ await it('reflects the selection back, including an empty name', () => {
235
+ const { stack, host } = mountStack([{ name: 'a' }, { name: '' }]);
236
+ expect(stack.getAttribute('visible-child-name')).toBe('a');
237
+ stack.visibleChildIndex = 1;
238
+ // The old reflection was guarded by `if (current && …)`, so a stack of
239
+ // nameless pages never reflected its selection at all.
240
+ expect(stack.getAttribute('visible-child-name')).toBe('');
241
+ host.remove();
242
+ });
243
+
244
+ await it('drops the attribute when nothing is selected', () => {
245
+ const { stack, host } = mountStack([{ name: 'a' }]);
246
+ expect(stack.getAttribute('visible-child-name')).toBe('a');
247
+ stack.removePage('a');
248
+ expect(stack.hasAttribute('visible-child-name')).toBe(false);
249
+ host.remove();
250
+ });
251
+
252
+ await it('drives the selection when the attribute changes later', () => {
253
+ const { stack, host, changes } = mountStack([{ name: 'learn' }, { name: 'code' }]);
254
+ changes.length = 0;
255
+ stack.setAttribute('visible-child-name', 'code');
256
+ expect(stack.visibleChildIndex).toBe(1);
257
+ expect(changes).toStrictEqual([{ index: 1, name: 'code', title: 'code', interactive: true }]);
258
+ host.remove();
259
+ });
260
+
261
+ await it('does not bounce the selection back when a duplicate name is reflected', () => {
262
+ // Reflection writes the attribute, which re-enters
263
+ // attributeChangedCallback; with duplicate names a naive re-lookup
264
+ // would resolve to the FIRST match and undo the selection.
265
+ const { stack, host } = mountStack([{ name: 'dup' }, { name: 'dup' }]);
266
+ stack.visibleChildIndex = 1;
267
+ expect(stack.visibleChildIndex).toBe(1);
268
+ expect(stack.getAttribute('visible-child-name')).toBe('dup');
269
+ host.remove();
270
+ });
271
+ });
272
+ };