@gjsify/adwaita-web 0.30.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,301 @@
1
+ // DOM-level conformance tests for the chrome elements — <adw-clamp>,
2
+ // <adw-spinner> and <adw-toolbar-view> — driven by the SAME vectors the
3
+ // NativeScript renderer asserts against (`@gjsify/adwaita-core/conformance`).
4
+ //
5
+ // Each of the three used to answer a question libadwaita had already answered,
6
+ // and answered it differently from the NativeScript port: the clamp was a plain
7
+ // `max-width` with no tightening and a `maximum-size="0"` that collapsed the
8
+ // child to nothing, the spinner defaulted to 24px and stroked a 2px ring where
9
+ // Adwaita strokes 3px, and the toolbar view carried none of the four style
10
+ // classes at all. Nothing failed, because nothing compared them.
11
+ //
12
+ // Sizes are driven by really resizing a host element and awaiting a real
13
+ // ResizeObserver delivery, the same way `breakpoints.spec.ts` does — never by
14
+ // faking one.
15
+ import { describe, expect, it } from '@gjsify/unit';
16
+
17
+ import { ADW_SPINNER_MIN_SIZE, spinnerGeometry } from '@gjsify/adwaita-core';
18
+ import {
19
+ CLAMP_ALLOCATE_VECTORS,
20
+ CLAMP_PROPERTY_VECTORS,
21
+ SPINNER_GEOMETRY_VECTORS,
22
+ SPINNER_SIZE_VECTORS,
23
+ TOOLBAR_VIEW_CLASS_VECTORS,
24
+ } from '@gjsify/adwaita-core/conformance';
25
+
26
+ /** Wait for a ResizeObserver delivery (it runs after layout, before paint). */
27
+ function settle(): Promise<void> {
28
+ return new Promise((resolve) => {
29
+ requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
30
+ });
31
+ }
32
+
33
+ /** A fixed-width host in the document, plus a way to resize it. */
34
+ function mountSized(width: number): { host: HTMLElement; resize: (w: number) => Promise<void> } {
35
+ const host = document.createElement('div');
36
+ host.style.width = `${width}px`;
37
+ document.body.appendChild(host);
38
+ return {
39
+ host,
40
+ resize: async (w: number) => {
41
+ host.style.width = `${w}px`;
42
+ await settle();
43
+ },
44
+ };
45
+ }
46
+
47
+ /** A clamp with one block child, mounted in a host of `available` px. */
48
+ function mountClamp(available: number): {
49
+ host: HTMLElement;
50
+ clamp: HTMLElement;
51
+ child: HTMLElement;
52
+ resize: (w: number) => Promise<void>;
53
+ } {
54
+ const { host, resize } = mountSized(available);
55
+ const clamp = document.createElement('adw-clamp');
56
+ const child = document.createElement('div');
57
+ // A bare block child fills the clamp and is capped by the max-width the
58
+ // element writes — the same shape every story uses.
59
+ child.textContent = 'clamped';
60
+ clamp.appendChild(child);
61
+ host.appendChild(clamp);
62
+ return { host, clamp, child, resize };
63
+ }
64
+
65
+ /** The child width a clamp settles on, rounded to the pixel it renders at. */
66
+ function clampedWidth(child: HTMLElement): number {
67
+ return Math.round(child.getBoundingClientRect().width);
68
+ }
69
+
70
+ /** The three classes `adw_clamp_layout_allocate` stamps on the child. */
71
+ function sizeClassOf(child: HTMLElement): string | null {
72
+ for (const cls of ['small', 'medium', 'large']) {
73
+ if (child.classList.contains(cls)) return cls;
74
+ }
75
+ return null;
76
+ }
77
+
78
+ /** A toolbar view with bars of the given pixel heights (0 = no bar at all). */
79
+ function mountToolbarView(topBarHeight: number, bottomBarHeight: number): { host: HTMLElement; view: HTMLElement } {
80
+ const { host } = mountSized(400);
81
+ host.style.height = '300px';
82
+ const view = document.createElement('adw-toolbar-view');
83
+ for (const [slot, height] of [
84
+ ['top', topBarHeight],
85
+ ['bottom', bottomBarHeight],
86
+ ] as const) {
87
+ if (height <= 0) continue;
88
+ const bar = document.createElement('div');
89
+ bar.setAttribute('slot', slot);
90
+ bar.style.height = `${height}px`;
91
+ view.appendChild(bar);
92
+ }
93
+ host.appendChild(view);
94
+ return { host, view };
95
+ }
96
+
97
+ export const AdwChromeTest = async () => {
98
+ await describe('adw-clamp allocation (AdwClampLayout conformance vectors)', async () => {
99
+ // The browser resolves the child's own minimum through the normal
100
+ // `min-width` cascade, so only the rows that pass a zero child minimum
101
+ // describe what this element can be held to.
102
+ const vectors = CLAMP_ALLOCATE_VECTORS.filter((v) => v.params.childMin === 0);
103
+
104
+ for (const { availableSize, params, childSize, sizeClass, offset, rule } of vectors) {
105
+ await it(`${availableSize}px available → a ${childSize}px ${sizeClass} child — ${rule}`, async () => {
106
+ const { host, clamp, child } = mountClamp(availableSize);
107
+ clamp.setAttribute('maximum-size', String(params.maximumSize));
108
+ clamp.setAttribute('tightening-threshold', String(params.tighteningThreshold));
109
+ await settle();
110
+
111
+ expect(clampedWidth(child)).toBe(childSize);
112
+ expect(sizeClassOf(child)).toBe(sizeClass);
113
+ // `margin-inline: auto` splits the remainder evenly; libadwaita
114
+ // truncates, so the two can differ by half a pixel.
115
+ const leading = child.getBoundingClientRect().left - clamp.getBoundingClientRect().left;
116
+ expect(Math.abs(leading - offset) <= 1).toBe(true);
117
+
118
+ host.remove();
119
+ });
120
+ }
121
+
122
+ await it('follows the viewport instead of pinning the child to the maximum', async () => {
123
+ // The NativeScript twin's bug in browser form: a narrow clamp must
124
+ // hand the child ALL of the available width, not the cap.
125
+ const { host, clamp, child, resize } = mountClamp(1000);
126
+ await settle();
127
+ expect(clampedWidth(child)).toBe(600);
128
+ await resize(360);
129
+ expect(clampedWidth(child)).toBe(360);
130
+ expect(Math.round(clamp.getBoundingClientRect().width)).toBe(360);
131
+ host.remove();
132
+ });
133
+
134
+ await it('honours tightening-threshold, which the element used to ignore', async () => {
135
+ // The storybook has exposed this control all along and only the GTK
136
+ // pane responded to it.
137
+ const { host, clamp, child } = mountClamp(700);
138
+ clamp.setAttribute('maximum-size', '600');
139
+ clamp.setAttribute('tightening-threshold', '400');
140
+ await settle();
141
+ expect(clampedWidth(child)).toBe(575);
142
+
143
+ // Raising the threshold above the maximum removes the easing.
144
+ clamp.setAttribute('tightening-threshold', '800');
145
+ await settle();
146
+ expect(clampedWidth(child)).toBe(600);
147
+ host.remove();
148
+ });
149
+
150
+ await it('clamps children appended after it was connected', async () => {
151
+ // The element only saw its children once, at connect time — the same
152
+ // "evaluated once" shape the toolbar view's empty-bar handling has.
153
+ const { host } = mountSized(1000);
154
+ const clamp = document.createElement('adw-clamp');
155
+ host.appendChild(clamp);
156
+ await settle();
157
+
158
+ const late = document.createElement('div');
159
+ late.textContent = 'appended later';
160
+ clamp.appendChild(late);
161
+ await settle();
162
+
163
+ expect(clampedWidth(late)).toBe(600);
164
+ host.remove();
165
+ });
166
+ });
167
+
168
+ await describe('adw-clamp maximum-size parsing (g_param_spec_int range)', async () => {
169
+ for (const { value, fallback, size, rule } of CLAMP_PROPERTY_VECTORS) {
170
+ // Only the rows whose fallback is the real property default describe
171
+ // what an attribute on THIS element resolves to.
172
+ if (fallback !== 600) continue;
173
+
174
+ await it(`maximum-size ${JSON.stringify(value ?? null)} caps at ${size}px — ${rule}`, async () => {
175
+ const { host, clamp, child } = mountClamp(1000);
176
+ // `null`/`undefined` are the ABSENT attribute, so nothing is set.
177
+ if (value !== null && value !== undefined) clamp.setAttribute('maximum-size', String(value));
178
+ await settle();
179
+
180
+ // 1000px is past `upper` for every cap in the table, so the child
181
+ // sits exactly at the resolved property and it is readable off
182
+ // the rendered width.
183
+ expect(clampedWidth(child)).toBe(size);
184
+ host.remove();
185
+ });
186
+ }
187
+
188
+ await it('recovers the default after an unparsable value, instead of freezing on the old cap', async () => {
189
+ // `parseFloat('abc')` produced `max-width: NaNpx`, which the CSSOM
190
+ // rejects — so the element silently kept whatever cap it had.
191
+ const { host, clamp, child } = mountClamp(1000);
192
+ clamp.setAttribute('maximum-size', '300');
193
+ await settle();
194
+ expect(clampedWidth(child)).toBe(300);
195
+
196
+ clamp.setAttribute('maximum-size', 'abc');
197
+ await settle();
198
+ expect(clampedWidth(child)).toBe(600);
199
+ host.remove();
200
+ });
201
+ });
202
+
203
+ await describe('adw-spinner geometry (AdwSpinnerPaintable conformance vectors)', async () => {
204
+ for (const { width, height, diameter, lineWidth, rule } of SPINNER_GEOMETRY_VECTORS) {
205
+ // The element is the ring itself, so only the square, allocatable
206
+ // boxes describe what a `size` attribute can produce.
207
+ if (width !== height || width < ADW_SPINNER_MIN_SIZE) continue;
208
+
209
+ await it(`size ${width} draws a ${diameter}px ring stroked ${lineWidth}px — ${rule}`, () => {
210
+ const { host } = mountSized(200);
211
+ const spinner = document.createElement('adw-spinner');
212
+ spinner.setAttribute('size', String(width));
213
+ host.appendChild(spinner);
214
+
215
+ expect(spinner.offsetWidth).toBe(diameter);
216
+ expect(spinner.offsetHeight).toBe(diameter);
217
+ expect(Number.parseFloat(spinner.style.borderWidth)).toBe(lineWidth);
218
+ host.remove();
219
+ });
220
+ }
221
+
222
+ await it('strokes 3px at 24 and 6px at 48, where it used to stroke 2 and 4', () => {
223
+ const { host } = mountSized(200);
224
+ const spinner = document.createElement('adw-spinner');
225
+ host.appendChild(spinner);
226
+ const strokeAt = (size: number) => {
227
+ spinner.setAttribute('size', String(size));
228
+ return Number.parseFloat(spinner.style.borderWidth);
229
+ };
230
+ expect(strokeAt(24)).toBe(3);
231
+ expect(strokeAt(48)).toBe(6);
232
+ expect(strokeAt(64)).toBe(8);
233
+ host.remove();
234
+ });
235
+ });
236
+
237
+ await describe('adw-spinner sizing (adw_spinner_measure minimum = natural)', async () => {
238
+ for (const { value, size, rule } of SPINNER_SIZE_VECTORS) {
239
+ await it(`size ${JSON.stringify(value ?? null)} → a ${spinnerGeometry(size, size).diameter}px ring — ${rule}`, () => {
240
+ const { host } = mountSized(400);
241
+ const spinner = document.createElement('adw-spinner');
242
+ if (value !== null && value !== undefined) spinner.setAttribute('size', String(value));
243
+ host.appendChild(spinner);
244
+
245
+ expect(spinner.offsetWidth).toBe(spinnerGeometry(size, size).diameter);
246
+ host.remove();
247
+ });
248
+ }
249
+
250
+ await it('defaults to the measured 16, not the invented 24', () => {
251
+ const { host } = mountSized(400);
252
+ const spinner = document.createElement('adw-spinner');
253
+ host.appendChild(spinner);
254
+ expect(spinner.offsetWidth).toBe(ADW_SPINNER_MIN_SIZE);
255
+ host.remove();
256
+ });
257
+ });
258
+
259
+ await describe('adw-toolbar-view style classes (update_undershoots + the style setters)', async () => {
260
+ for (const { input, view, topBar, bottomBar, rule } of TOOLBAR_VIEW_CLASS_VECTORS) {
261
+ await it(`${input.topBarStyle}/${input.bottomBarStyle} → [${view.join(' ')}] — ${rule}`, () => {
262
+ const { host, view: element } = mountToolbarView(input.topBarHeight, input.bottomBarHeight);
263
+ element.setAttribute('top-bar-style', input.topBarStyle);
264
+ element.setAttribute('bottom-bar-style', input.bottomBarStyle);
265
+ if (input.extendContentToTopEdge) element.setAttribute('extend-content-to-top-edge', '');
266
+ if (input.extendContentToBottomEdge) element.setAttribute('extend-content-to-bottom-edge', '');
267
+
268
+ const topEl = element.querySelector('.adw-toolbar-view-top') as HTMLElement;
269
+ const bottomEl = element.querySelector('.adw-toolbar-view-bottom') as HTMLElement;
270
+
271
+ for (const cls of ['undershoot-top', 'undershoot-bottom']) {
272
+ expect(element.classList.contains(cls)).toBe(view.includes(cls));
273
+ }
274
+ for (const cls of ['raised', 'border']) {
275
+ expect(topEl.classList.contains(cls)).toBe(topBar.includes(cls));
276
+ expect(bottomEl.classList.contains(cls)).toBe(bottomBar.includes(cls));
277
+ }
278
+ host.remove();
279
+ });
280
+ }
281
+
282
+ await it('drops the undershoot again when the style stops being flat', () => {
283
+ const { host, view } = mountToolbarView(46, 0);
284
+ expect(view.classList.contains('undershoot-top')).toBe(true);
285
+ view.setAttribute('top-bar-style', 'raised');
286
+ expect(view.classList.contains('undershoot-top')).toBe(false);
287
+ view.setAttribute('top-bar-style', 'flat');
288
+ expect(view.classList.contains('undershoot-top')).toBe(true);
289
+ host.remove();
290
+ });
291
+
292
+ await it('treats an unknown style as flat, the way the enum guard does', () => {
293
+ const { host, view } = mountToolbarView(46, 0);
294
+ view.setAttribute('top-bar-style', 'shadowed');
295
+ const topEl = view.querySelector('.adw-toolbar-view-top') as HTMLElement;
296
+ expect(topEl.classList.contains('raised')).toBe(false);
297
+ expect(view.classList.contains('undershoot-top')).toBe(true);
298
+ host.remove();
299
+ });
300
+ });
301
+ };