@gjsify/adwaita-web 0.64.1 → 0.64.2-next.254

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 (335) hide show
  1. package/README.md +22 -88
  2. package/dist/adwaita-web.css +91 -12
  3. package/dist/adwaita-web.css.map +1 -1
  4. package/lib/types/accessible.d.ts +8 -0
  5. package/lib/types/application.d.ts +1 -1
  6. package/lib/types/child-slot.spec.d.ts +1 -0
  7. package/lib/types/compute-expand.d.ts +5 -0
  8. package/lib/types/compute-expand.spec.d.ts +1 -0
  9. package/lib/types/container-fill.spec.d.ts +1 -0
  10. package/lib/types/elements/adw-about-dialog.d.ts +28 -1
  11. package/lib/types/elements/adw-action-row.d.ts +2 -1
  12. package/lib/types/elements/adw-alert-dialog.d.ts +80 -19
  13. package/lib/types/elements/adw-avatar.d.ts +2 -1
  14. package/lib/types/elements/adw-banner.d.ts +2 -1
  15. package/lib/types/elements/adw-bin.d.ts +2 -1
  16. package/lib/types/elements/adw-bottom-sheet.d.ts +5 -4
  17. package/lib/types/elements/adw-breakpoint-bin.d.ts +2 -1
  18. package/lib/types/elements/adw-button-content.d.ts +2 -1
  19. package/lib/types/elements/adw-button-row.d.ts +2 -1
  20. package/lib/types/elements/adw-card.d.ts +2 -1
  21. package/lib/types/elements/adw-carousel.d.ts +20 -3
  22. package/lib/types/elements/adw-clamp-scrollable.d.ts +2 -1
  23. package/lib/types/elements/adw-clamp.d.ts +2 -1
  24. package/lib/types/elements/adw-combo-row.d.ts +6 -1
  25. package/lib/types/elements/adw-data-grid.d.ts +2 -1
  26. package/lib/types/elements/adw-dialog.d.ts +2 -1
  27. package/lib/types/elements/adw-entry-row.d.ts +2 -1
  28. package/lib/types/elements/adw-expander-row.d.ts +2 -1
  29. package/lib/types/elements/adw-header-bar.d.ts +2 -1
  30. package/lib/types/elements/adw-inline-view-switcher.d.ts +3 -2
  31. package/lib/types/elements/adw-layout-slot.d.ts +2 -1
  32. package/lib/types/elements/adw-multi-layout-view.d.ts +2 -1
  33. package/lib/types/elements/adw-navigation-split-view.d.ts +2 -1
  34. package/lib/types/elements/adw-navigation-view.d.ts +3 -2
  35. package/lib/types/elements/adw-overlay-split-view.d.ts +2 -1
  36. package/lib/types/elements/adw-preferences-dialog.d.ts +5 -4
  37. package/lib/types/elements/adw-preferences-group.d.ts +2 -1
  38. package/lib/types/elements/adw-preferences-row.d.ts +2 -1
  39. package/lib/types/elements/adw-shortcut-label.d.ts +2 -1
  40. package/lib/types/elements/adw-shortcuts-item.d.ts +2 -1
  41. package/lib/types/elements/adw-shortcuts-section.d.ts +2 -1
  42. package/lib/types/elements/adw-sidebar.d.ts +4 -3
  43. package/lib/types/elements/adw-spin-row.d.ts +2 -1
  44. package/lib/types/elements/adw-spinner.d.ts +2 -1
  45. package/lib/types/elements/adw-split-button.d.ts +2 -1
  46. package/lib/types/elements/adw-status-page.d.ts +2 -1
  47. package/lib/types/elements/adw-switch-row.d.ts +4 -1
  48. package/lib/types/elements/adw-tab-bar.d.ts +2 -1
  49. package/lib/types/elements/adw-tab-button.d.ts +2 -1
  50. package/lib/types/elements/adw-tab-overview.d.ts +2 -1
  51. package/lib/types/elements/adw-tab-view.d.ts +3 -2
  52. package/lib/types/elements/adw-toast-overlay.d.ts +10 -30
  53. package/lib/types/elements/adw-toggle-group.d.ts +3 -2
  54. package/lib/types/elements/adw-toolbar-view.d.ts +2 -1
  55. package/lib/types/elements/adw-view-stack.d.ts +3 -1
  56. package/lib/types/elements/adw-view-switcher-bar.d.ts +2 -1
  57. package/lib/types/elements/adw-view-switcher-sidebar.d.ts +2 -1
  58. package/lib/types/elements/adw-view-switcher.d.ts +3 -2
  59. package/lib/types/elements/adw-window-features.d.ts +8 -0
  60. package/lib/types/elements/adw-window-title.d.ts +2 -1
  61. package/lib/types/elements/adw-wrap-box.d.ts +2 -1
  62. package/lib/types/elements/checks.d.ts +16 -1
  63. package/lib/types/elements/gtk-action-bar.d.ts +2 -1
  64. package/lib/types/elements/gtk-application-window.d.ts +1 -4
  65. package/lib/types/elements/gtk-aspect-frame.d.ts +2 -1
  66. package/lib/types/elements/gtk-box.d.ts +5 -1
  67. package/lib/types/elements/gtk-button.d.ts +5 -7
  68. package/lib/types/elements/gtk-calendar.d.ts +2 -1
  69. package/lib/types/elements/gtk-center-box.d.ts +2 -1
  70. package/lib/types/elements/gtk-color-dialog-button.d.ts +2 -1
  71. package/lib/types/elements/gtk-drag-icon.d.ts +2 -1
  72. package/lib/types/elements/gtk-drawing-area.d.ts +2 -1
  73. package/lib/types/elements/gtk-drop-down.d.ts +2 -1
  74. package/lib/types/elements/gtk-editable-label.d.ts +2 -1
  75. package/lib/types/elements/gtk-emoji-chooser.d.ts +2 -0
  76. package/lib/types/elements/gtk-entry.d.ts +2 -1
  77. package/lib/types/elements/gtk-expander.d.ts +2 -1
  78. package/lib/types/elements/gtk-fixed.d.ts +2 -1
  79. package/lib/types/elements/gtk-flow-box-child.d.ts +2 -1
  80. package/lib/types/elements/gtk-flow-box.d.ts +2 -1
  81. package/lib/types/elements/gtk-font-dialog-button.d.ts +2 -1
  82. package/lib/types/elements/gtk-frame.d.ts +2 -1
  83. package/lib/types/elements/gtk-gl-area.d.ts +2 -1
  84. package/lib/types/elements/gtk-graphics-offload.d.ts +2 -1
  85. package/lib/types/elements/gtk-grid.d.ts +2 -1
  86. package/lib/types/elements/gtk-header-bar.d.ts +2 -1
  87. package/lib/types/elements/gtk-image.d.ts +5 -6
  88. package/lib/types/elements/gtk-inscription.d.ts +2 -1
  89. package/lib/types/elements/gtk-label.d.ts +10 -3
  90. package/lib/types/elements/gtk-level-bar.d.ts +2 -1
  91. package/lib/types/elements/gtk-list-box-row.d.ts +2 -1
  92. package/lib/types/elements/gtk-list-box.d.ts +2 -1
  93. package/lib/types/elements/gtk-list-view.d.ts +2 -1
  94. package/lib/types/elements/gtk-media-controls.d.ts +2 -1
  95. package/lib/types/elements/gtk-menu-button.d.ts +2 -1
  96. package/lib/types/elements/gtk-notebook.d.ts +2 -1
  97. package/lib/types/elements/gtk-overlay.d.ts +2 -1
  98. package/lib/types/elements/gtk-paned.d.ts +2 -1
  99. package/lib/types/elements/gtk-picture.d.ts +2 -1
  100. package/lib/types/elements/gtk-popover-bin.d.ts +2 -1
  101. package/lib/types/elements/gtk-popover-menu-bar.d.ts +2 -1
  102. package/lib/types/elements/gtk-popover-menu.d.ts +2 -0
  103. package/lib/types/elements/gtk-popover.d.ts +7 -1
  104. package/lib/types/elements/gtk-progress-bar.d.ts +2 -1
  105. package/lib/types/elements/gtk-revealer.d.ts +2 -1
  106. package/lib/types/elements/gtk-scale-button.d.ts +2 -1
  107. package/lib/types/elements/gtk-scale.d.ts +2 -1
  108. package/lib/types/elements/gtk-scrollbar.d.ts +2 -1
  109. package/lib/types/elements/gtk-scrolled-window.d.ts +2 -1
  110. package/lib/types/elements/gtk-search-bar.d.ts +2 -1
  111. package/lib/types/elements/gtk-separator.d.ts +2 -1
  112. package/lib/types/elements/gtk-spin-button.d.ts +2 -1
  113. package/lib/types/elements/gtk-spinner.d.ts +2 -1
  114. package/lib/types/elements/gtk-stack-page.d.ts +29 -0
  115. package/lib/types/elements/gtk-stack-sidebar.d.ts +2 -1
  116. package/lib/types/elements/gtk-stack-switcher.d.ts +2 -1
  117. package/lib/types/elements/gtk-stack.d.ts +13 -1
  118. package/lib/types/elements/gtk-switch.d.ts +2 -1
  119. package/lib/types/elements/gtk-text-view.d.ts +2 -1
  120. package/lib/types/elements/gtk-text.d.ts +2 -1
  121. package/lib/types/elements/gtk-tree-expander.d.ts +2 -1
  122. package/lib/types/elements/gtk-video.d.ts +2 -1
  123. package/lib/types/elements/gtk-viewport.d.ts +2 -1
  124. package/lib/types/elements/gtk-window-controls.d.ts +2 -1
  125. package/lib/types/elements/gtk-window-handle.d.ts +2 -1
  126. package/lib/types/elements/gtk-window.d.ts +18 -2
  127. package/lib/types/gobject-elements.d.ts +15 -0
  128. package/lib/types/gtksource/gtk-source-view.d.ts +2 -1
  129. package/lib/types/{icon-registry.spec.d.ts → icon-masks.spec.d.ts} +1 -1
  130. package/lib/types/icon-theme.d.ts +15 -35
  131. package/lib/types/index.d.ts +3 -4
  132. package/lib/types/learn6502-window.spec.d.ts +1 -0
  133. package/lib/types/namespace/adw.d.ts +1 -1
  134. package/lib/types/namespace/gdk.d.ts +1 -0
  135. package/lib/types/namespace/gio.d.ts +1 -0
  136. package/lib/types/namespace/glib.d.ts +2 -1
  137. package/lib/types/namespace/gtk.d.ts +2 -0
  138. package/lib/types/shared-tree-builder.d.ts +31 -0
  139. package/lib/types/signals.d.ts +5 -1
  140. package/lib/types/source-view/adw-source-view.d.ts +2 -1
  141. package/lib/types/styles.generated.d.ts +1 -1
  142. package/lib/types/widget-element.d.ts +25 -0
  143. package/lib/types/widget-map.spec.d.ts +1 -0
  144. package/lib/types/widget-mapping.d.ts +7 -0
  145. package/lib/types/widget-visible.spec.d.ts +1 -0
  146. package/lib/types/window-maximize.spec.d.ts +1 -0
  147. package/package.json +13 -12
  148. package/scss/_about_dialog.scss +46 -1
  149. package/scss/_bin.scss +6 -3
  150. package/scss/_checks.scss +5 -2
  151. package/scss/_icon.scss +3 -4
  152. package/scss/_labels.scss +2 -2
  153. package/scss/_overlay.scss +11 -1
  154. package/scss/_overlay_split_view.scss +10 -0
  155. package/scss/_reset.scss +1 -1
  156. package/scss/_scrolled_window.scss +15 -0
  157. package/scss/_stack.scss +8 -2
  158. package/scss/_toast.scss +13 -3
  159. package/scss/_widget.scss +5 -1
  160. package/scss/_window.scss +16 -0
  161. package/src/accessible.ts +29 -0
  162. package/src/adw-about-dialog.spec.ts +81 -12
  163. package/src/adw-action-rows.spec.ts +2 -2
  164. package/src/adw-alert-dialog.spec.ts +71 -5
  165. package/src/adw-bin.spec.ts +4 -3
  166. package/src/adw-button-content.spec.ts +1 -1
  167. package/src/adw-carousel.spec.ts +49 -1
  168. package/src/adw-header-bar.spec.ts +18 -0
  169. package/src/adw-toast-overlay.spec.ts +68 -24
  170. package/src/application.spec.ts +44 -0
  171. package/src/application.ts +13 -18
  172. package/src/capabilities.mjs +2 -3
  173. package/src/checks.spec.ts +51 -1
  174. package/src/child-slot.spec.ts +71 -0
  175. package/src/compute-expand.spec.ts +186 -0
  176. package/src/compute-expand.ts +134 -0
  177. package/src/construct-vectors.spec.ts +97 -7
  178. package/src/container-fill.spec.ts +70 -0
  179. package/src/elements/adw-about-dialog.ts +152 -9
  180. package/src/elements/adw-action-row.ts +2 -1
  181. package/src/elements/adw-alert-dialog.ts +177 -46
  182. package/src/elements/adw-application-window.ts +0 -3
  183. package/src/elements/adw-avatar.ts +2 -1
  184. package/src/elements/adw-banner.ts +2 -1
  185. package/src/elements/adw-bin.ts +2 -1
  186. package/src/elements/adw-bottom-sheet.ts +5 -4
  187. package/src/elements/adw-breakpoint-bin.ts +5 -1
  188. package/src/elements/adw-button-content.ts +2 -1
  189. package/src/elements/adw-button-row.ts +2 -1
  190. package/src/elements/adw-card.ts +2 -1
  191. package/src/elements/adw-carousel.ts +62 -3
  192. package/src/elements/adw-clamp-scrollable.ts +2 -1
  193. package/src/elements/adw-clamp.ts +2 -1
  194. package/src/elements/adw-combo-row.ts +15 -1
  195. package/src/elements/adw-data-grid.ts +2 -1
  196. package/src/elements/adw-dialog.ts +5 -2
  197. package/src/elements/adw-entry-row.ts +2 -1
  198. package/src/elements/adw-expander-row.ts +2 -1
  199. package/src/elements/adw-header-bar.ts +6 -1
  200. package/src/elements/adw-inline-view-switcher.ts +3 -2
  201. package/src/elements/adw-layout-slot.ts +2 -1
  202. package/src/elements/adw-multi-layout-view.ts +2 -1
  203. package/src/elements/adw-navigation-split-view.ts +2 -1
  204. package/src/elements/adw-navigation-view.ts +3 -2
  205. package/src/elements/adw-overlay-split-view.ts +2 -1
  206. package/src/elements/adw-preferences-dialog.ts +6 -4
  207. package/src/elements/adw-preferences-group.ts +2 -1
  208. package/src/elements/adw-preferences-row.ts +2 -1
  209. package/src/elements/adw-shortcut-label.ts +2 -1
  210. package/src/elements/adw-shortcuts-item.ts +2 -1
  211. package/src/elements/adw-shortcuts-section.ts +2 -1
  212. package/src/elements/adw-sidebar.ts +4 -3
  213. package/src/elements/adw-spin-row.ts +2 -1
  214. package/src/elements/adw-spinner.ts +2 -1
  215. package/src/elements/adw-split-button.ts +2 -1
  216. package/src/elements/adw-status-page.ts +2 -1
  217. package/src/elements/adw-switch-row.ts +10 -1
  218. package/src/elements/adw-tab-bar.ts +2 -1
  219. package/src/elements/adw-tab-button.ts +2 -1
  220. package/src/elements/adw-tab-overview.ts +2 -1
  221. package/src/elements/adw-tab-view.ts +3 -2
  222. package/src/elements/adw-toast-overlay.ts +37 -70
  223. package/src/elements/adw-toggle-group.ts +3 -2
  224. package/src/elements/adw-toolbar-view.ts +2 -1
  225. package/src/elements/adw-view-stack.ts +39 -1
  226. package/src/elements/adw-view-switcher-bar.ts +2 -1
  227. package/src/elements/adw-view-switcher-sidebar.ts +2 -1
  228. package/src/elements/adw-view-switcher.ts +3 -2
  229. package/src/elements/adw-window-features.ts +14 -2
  230. package/src/elements/adw-window-title.ts +2 -1
  231. package/src/elements/adw-wrap-box.ts +2 -1
  232. package/src/elements/checks.ts +85 -6
  233. package/src/elements/gtk-action-bar.ts +2 -1
  234. package/src/elements/gtk-application-window.ts +3 -8
  235. package/src/elements/gtk-aspect-frame.ts +2 -1
  236. package/src/elements/gtk-box.ts +56 -1
  237. package/src/elements/gtk-button.ts +10 -22
  238. package/src/elements/gtk-calendar.ts +2 -1
  239. package/src/elements/gtk-center-box.ts +2 -1
  240. package/src/elements/gtk-color-dialog-button.ts +2 -1
  241. package/src/elements/gtk-drag-icon.ts +6 -1
  242. package/src/elements/gtk-drawing-area.ts +2 -1
  243. package/src/elements/gtk-drop-down.ts +2 -1
  244. package/src/elements/gtk-editable-label.ts +2 -1
  245. package/src/elements/gtk-emoji-chooser.ts +5 -0
  246. package/src/elements/gtk-entry.ts +2 -1
  247. package/src/elements/gtk-expander.ts +2 -1
  248. package/src/elements/gtk-fixed.ts +2 -1
  249. package/src/elements/gtk-flow-box-child.ts +5 -2
  250. package/src/elements/gtk-flow-box.ts +2 -1
  251. package/src/elements/gtk-font-dialog-button.ts +2 -1
  252. package/src/elements/gtk-frame.ts +2 -1
  253. package/src/elements/gtk-gl-area.ts +2 -1
  254. package/src/elements/gtk-graphics-offload.ts +5 -2
  255. package/src/elements/gtk-grid.ts +2 -1
  256. package/src/elements/gtk-header-bar.ts +2 -1
  257. package/src/elements/gtk-image.ts +14 -15
  258. package/src/elements/gtk-inscription.ts +2 -1
  259. package/src/elements/gtk-label.ts +12 -5
  260. package/src/elements/gtk-level-bar.ts +2 -1
  261. package/src/elements/gtk-list-box-row.ts +5 -2
  262. package/src/elements/gtk-list-box.ts +2 -1
  263. package/src/elements/gtk-list-view.ts +2 -1
  264. package/src/elements/gtk-media-controls.ts +2 -1
  265. package/src/elements/gtk-menu-button.ts +2 -1
  266. package/src/elements/gtk-notebook.ts +2 -1
  267. package/src/elements/gtk-overlay.ts +7 -5
  268. package/src/elements/gtk-paned.ts +9 -2
  269. package/src/elements/gtk-picture.ts +2 -1
  270. package/src/elements/gtk-popover-bin.ts +5 -1
  271. package/src/elements/gtk-popover-menu-bar.ts +2 -1
  272. package/src/elements/gtk-popover-menu.ts +5 -0
  273. package/src/elements/gtk-popover.ts +14 -1
  274. package/src/elements/gtk-progress-bar.ts +2 -1
  275. package/src/elements/gtk-revealer.ts +2 -1
  276. package/src/elements/gtk-scale-button.ts +2 -1
  277. package/src/elements/gtk-scale.ts +2 -1
  278. package/src/elements/gtk-scrollbar.ts +2 -1
  279. package/src/elements/gtk-scrolled-window.ts +8 -5
  280. package/src/elements/gtk-search-bar.ts +7 -6
  281. package/src/elements/gtk-separator.ts +2 -1
  282. package/src/elements/gtk-spin-button.ts +2 -1
  283. package/src/elements/gtk-spinner.ts +2 -1
  284. package/src/elements/gtk-stack-page.ts +112 -0
  285. package/src/elements/gtk-stack-sidebar.ts +2 -1
  286. package/src/elements/gtk-stack-switcher.ts +2 -1
  287. package/src/elements/gtk-stack.ts +52 -6
  288. package/src/elements/gtk-switch.ts +2 -1
  289. package/src/elements/gtk-text-view.ts +2 -1
  290. package/src/elements/gtk-text.ts +2 -1
  291. package/src/elements/gtk-tree-expander.ts +5 -1
  292. package/src/elements/gtk-video.ts +2 -1
  293. package/src/elements/gtk-viewport.ts +6 -1
  294. package/src/elements/gtk-window-controls.ts +2 -1
  295. package/src/elements/gtk-window-handle.ts +6 -1
  296. package/src/elements/gtk-window.ts +57 -7
  297. package/src/gio.spec.ts +13 -3
  298. package/src/glib.spec.ts +9 -0
  299. package/src/gobject-elements.ts +44 -2
  300. package/src/gtk-label.spec.ts +39 -0
  301. package/src/gtk-scrolled-window.spec.ts +6 -6
  302. package/src/gtk-stack.spec.ts +170 -0
  303. package/src/gtk-value-doors.spec.ts +19 -0
  304. package/src/gtk-window.spec.ts +2 -2
  305. package/src/gtksource/gtk-source-view.ts +2 -1
  306. package/src/{icon-registry.spec.ts → icon-masks.spec.ts} +4 -100
  307. package/src/icon-theme.spec.ts +69 -124
  308. package/src/icon-theme.ts +29 -119
  309. package/src/index.ts +5 -9
  310. package/src/keyboard-operable.spec.ts +3 -3
  311. package/src/learn6502-window.spec.ts +113 -0
  312. package/src/namespace/adw.ts +1 -1
  313. package/src/namespace/gdk.ts +1 -0
  314. package/src/namespace/gio.ts +8 -0
  315. package/src/namespace/glib.ts +4 -0
  316. package/src/namespace/gtk.ts +2 -0
  317. package/src/shared-tree-builder.ts +131 -13
  318. package/src/shared-trees.spec.ts +15 -1
  319. package/src/signals.ts +11 -2
  320. package/src/slotted-children.spec.ts +24 -0
  321. package/src/slotted-children.ts +18 -2
  322. package/src/source-view/adw-source-view.ts +2 -1
  323. package/src/split-button.spec.ts +1 -1
  324. package/src/style-manager.spec.ts +13 -0
  325. package/src/style-manager.ts +6 -0
  326. package/src/styles.generated.ts +1 -1
  327. package/src/test.browser.mts +16 -2
  328. package/src/value-lists.spec.ts +3 -3
  329. package/src/widget-element.ts +75 -0
  330. package/src/widget-map.spec.ts +116 -0
  331. package/src/widget-mapping.ts +100 -0
  332. package/src/widget-visible.spec.ts +148 -0
  333. package/src/window-maximize.spec.ts +93 -0
  334. package/lib/types/icon-registry.d.ts +0 -37
  335. package/src/icon-registry.ts +0 -163
@@ -0,0 +1,186 @@
1
+ // `gtk_widget_compute_expand` in a box (gtkwidget.c:8387-8423, gtkboxlayout.c:793): a widget
2
+ // that authored no `hexpand` grows when a descendant does, an explicit value stops the walk, and
3
+ // the answer follows the tree as it changes. Plus the two size facts the same app layout broke
4
+ // on: a scrolled window requests 0 on a scrolling axis (gtkscrolledwindow.c:1821-1960), and a
5
+ // page leaving a view stack takes none of the stack's marks with it (adw-view-stack.c:1184-1205).
6
+ import { describe, expect, it } from '@gjsify/unit';
7
+
8
+ import type { AdwViewStack } from './elements/adw-view-stack.js';
9
+
10
+ function mount(html: string, width = 600, height = 600): { host: HTMLElement; box: (sel: string) => DOMRect } {
11
+ const host = document.createElement('div');
12
+ host.style.cssText = `width:${width}px;height:${height}px;display:flex;flex-direction:column`;
13
+ host.innerHTML = html;
14
+ document.body.appendChild(host);
15
+ const box = (sel: string) => (host.querySelector(sel) as HTMLElement).getBoundingClientRect();
16
+ return { host, box };
17
+ }
18
+
19
+ /** The box writes its computed values from a MutationObserver: one microtask, then a frame. */
20
+ const settle = () => new Promise((resolve) => requestAnimationFrame(resolve));
21
+
22
+ export const ComputeExpandTest = async () => {
23
+ await describe('computed expand in a <gtk-box>', async () => {
24
+ await it('a bin with an hexpand descendant fills a horizontal box', async () => {
25
+ const { host, box } = mount(
26
+ `<gtk-box id="row" style="width:600px">
27
+ <div id="fixed" style="width:100px"></div>
28
+ <adw-bin id="bin"><gtk-box><div id="leaf" hexpand></div></gtk-box></adw-bin>
29
+ </gtk-box>`,
30
+ );
31
+ await settle();
32
+ expect(host.querySelector('#bin')!.hasAttribute('data-computed-hexpand')).toBe(true);
33
+ expect(box('#bin').width).toBe(500);
34
+ host.remove();
35
+ });
36
+
37
+ await it('an explicit hexpand=false on an intermediate stops propagation', async () => {
38
+ const { host, box } = mount(
39
+ `<gtk-box style="width:600px">
40
+ <adw-bin id="bin"><gtk-box hexpand="false"><div style="width:10px" hexpand></div></gtk-box></adw-bin>
41
+ </gtk-box>`,
42
+ );
43
+ await settle();
44
+ expect(host.querySelector('#bin')!.hasAttribute('data-computed-hexpand')).toBe(false);
45
+ expect(box('#bin').width).toBe(10);
46
+ host.remove();
47
+ });
48
+
49
+ await it('a leaf widget does not propagate, and a hidden one never expands', async () => {
50
+ const { host } = mount(
51
+ `<gtk-box>
52
+ <gtk-label id="label"><div hexpand></div></gtk-label>
53
+ <adw-bin id="bin"><gtk-box hidden><div hexpand></div></gtk-box></adw-bin>
54
+ </gtk-box>`,
55
+ );
56
+ await settle();
57
+ expect(host.querySelector('#label')!.hasAttribute('data-computed-hexpand')).toBe(false);
58
+ expect(host.querySelector('#bin')!.hasAttribute('data-computed-hexpand')).toBe(false);
59
+ host.remove();
60
+ });
61
+
62
+ await it('propagation updates after a runtime reparent', async () => {
63
+ const { host, box } = mount(
64
+ `<gtk-box style="width:600px">
65
+ <div style="width:100px"></div>
66
+ <adw-bin id="bin"><div style="width:20px"></div></adw-bin>
67
+ </gtk-box>
68
+ <div id="parking"><div id="editor" hexpand></div></div>`,
69
+ );
70
+ await settle();
71
+ expect(box('#bin').width).toBe(20);
72
+ const bin = host.querySelector('#bin')!;
73
+ bin.replaceChildren(host.querySelector('#editor')!);
74
+ await settle();
75
+ expect(bin.hasAttribute('data-computed-hexpand')).toBe(true);
76
+ expect(box('#bin').width).toBe(500);
77
+ host.querySelector('#parking')!.append(host.querySelector('#editor')!);
78
+ await settle();
79
+ expect(bin.hasAttribute('data-computed-hexpand')).toBe(false);
80
+ host.remove();
81
+ });
82
+
83
+ await it('rewriting a label text does not recompute, adding a widget does', async () => {
84
+ const { host } = mount(
85
+ `<gtk-box>
86
+ <adw-bin id="bin"><gtk-box id="inner"><gtk-label id="label">0000</gtk-label><div hexpand></div></gtk-box></adw-bin>
87
+ </gtk-box>`,
88
+ );
89
+ await settle();
90
+ const bin = host.querySelector('#bin')!;
91
+ expect(bin.hasAttribute('data-computed-hexpand')).toBe(true);
92
+ // Drop the mark by hand: only a recompute would write it back.
93
+ bin.removeAttribute('data-computed-hexpand');
94
+ host.querySelector('#label')!.textContent = 'FFFF';
95
+ await settle();
96
+ expect(bin.hasAttribute('data-computed-hexpand')).toBe(false);
97
+ host.querySelector('#inner')!.append(document.createElement('gtk-label'));
98
+ await settle();
99
+ expect(bin.hasAttribute('data-computed-hexpand')).toBe(true);
100
+ host.remove();
101
+ });
102
+
103
+ await it('a registered subclass of a bin propagates as its base does', async () => {
104
+ const Bin = customElements.get('adw-bin')!;
105
+ const tag = 'compute-expand-test-bin';
106
+ if (!customElements.get(tag)) customElements.define(tag, class extends Bin {});
107
+ const { host, box } = mount(
108
+ `<gtk-box style="width:600px">
109
+ <div style="width:100px"></div>
110
+ <${tag} id="sub"><div hexpand></div></${tag}>
111
+ </gtk-box>`,
112
+ );
113
+ await settle();
114
+ expect(box('#sub').width).toBe(500);
115
+ host.remove();
116
+ });
117
+ });
118
+
119
+ await describe('<gtk-scrolled-window> requests no content size on a scrolling axis', async () => {
120
+ await it('in a horizontal box inside a 600px-high box it stays 600 and scrolls', async () => {
121
+ const { host, box } = mount(
122
+ `<gtk-box orientation="vertical" style="height:600px">
123
+ <gtk-box id="row" vexpand>
124
+ <gtk-scrolled-window id="sw" hexpand>
125
+ <div style="height:2000px;width:50px"></div>
126
+ </gtk-scrolled-window>
127
+ </gtk-box>
128
+ </gtk-box>`,
129
+ );
130
+ await settle();
131
+ expect(box('#row').height).toBe(600);
132
+ expect(box('#sw').height).toBe(600);
133
+ const viewport = host.querySelector('#sw gtk-viewport') as HTMLElement;
134
+ expect(viewport.getBoundingClientRect().height).toBe(600);
135
+ expect(viewport.scrollHeight > 600).toBe(true);
136
+ host.remove();
137
+ });
138
+
139
+ await it('sizes the measured chain: split-view content fills, the editor bin gets width', async () => {
140
+ const { host, box } = mount(
141
+ `<adw-overlay-split-view id="osv" style="height:600px">
142
+ <div slot="sidebar" style="width:100px"></div>
143
+ <gtk-box id="centerRight">
144
+ <adw-bin id="editor"><div hexpand style="height:10px"></div></adw-bin>
145
+ <gtk-scrolled-window id="right" hscrollbar-policy="never" style="flex:0 1 auto">
146
+ <div style="width:150px;height:3000px"></div>
147
+ </gtk-scrolled-window>
148
+ </gtk-box>
149
+ </adw-overlay-split-view>`,
150
+ 800,
151
+ 600,
152
+ );
153
+ await settle();
154
+ expect(box('#centerRight').height).toBe(box('#osv').height);
155
+ expect(box('#editor').width > 0).toBe(true);
156
+ expect(box('#right').height).toBe(box('#centerRight').height);
157
+ host.remove();
158
+ });
159
+ });
160
+
161
+ await describe('<adw-view-stack> page removal', async () => {
162
+ await it('a child removed from a view stack loses the stack classes', async () => {
163
+ const { host } = mount('<adw-view-stack id="s"></adw-view-stack><div id="elsewhere"></div>');
164
+ const stack = host.querySelector('#s') as AdwViewStack;
165
+ const a = document.createElement('div');
166
+ const b = document.createElement('div');
167
+ stack.add(a, 'a');
168
+ stack.add(b, 'b');
169
+ expect(a.classList.contains('active-view')).toBe(true);
170
+ expect(b.hidden).toBe(true);
171
+
172
+ stack.removePage('a');
173
+ expect(a.classList.contains('adw-view-stack-child')).toBe(false);
174
+ expect(a.classList.contains('active-view')).toBe(false);
175
+ expect(a.dataset.name).toBe(undefined);
176
+
177
+ // A plain DOM reparent is `gtk_widget_unparent` reaching the stack too.
178
+ host.querySelector('#elsewhere')!.append(b);
179
+ await settle();
180
+ expect(b.classList.contains('adw-view-stack-child')).toBe(false);
181
+ expect(b.hidden).toBe(false);
182
+ expect(stack.pages.length).toBe(0);
183
+ host.remove();
184
+ });
185
+ });
186
+ };
@@ -0,0 +1,134 @@
1
+ // compute-expand.ts — `gtk_widget_compute_expand`: does a widget tree rooted here want spare space?
2
+ //
3
+ // GTK does not only read a widget's OWN `hexpand`: a widget that set none expands when one of
4
+ // its children does (gtkwidget.c:8387-8423, `gtk_widget_update_computed_expand`). An AdwBin
5
+ // whose child is an hexpand editor therefore grows in a horizontal box, where the `[hexpand]`
6
+ // rules in `_widget.scss` gave it nothing and the editor came out 0px wide.
7
+ //
8
+ // THREE RULES, all in the C:
9
+ //
10
+ // · a widget that is not visible never expands (gtkwidget.c:8508-8509);
11
+ // · an EXPLICIT value wins and stops the walk — `hexpand_set` is checked before the vfunc
12
+ // runs, so an intermediate `hexpand: false` hides every expanding descendant;
13
+ // · otherwise only a class with a `compute_expand` vfunc looks at its children at all. The
14
+ // base GtkWidget has none, so a leaf (a label, a button's icon) never propagates.
15
+ //
16
+ // WHY JS AND NOT `:has()`: a selector cannot say "and no widget between here and there set
17
+ // hexpand" at arbitrary depth, it cannot tell a container with the vfunc from a leaf without
18
+ // one, and a registered subclass (`GObject.registerClass` over AdwBin) is a NEW tag that only
19
+ // the prototype chain links back to its base. So the value is computed here, and the box that
20
+ // needs it writes it as `data-computed-hexpand` / `data-computed-vexpand` on its children
21
+ // (gtk-box.ts) — internal state, never an authored attribute.
22
+ //
23
+ // The DOM has wrapper elements GTK has no widget for (`.adw-overlay-child`, a view stack's
24
+ // page `div`): those are transparent, the walk goes through them to the widgets inside.
25
+ //
26
+ // Reference: refs/gtk/gtk/gtkwidget.c (gtk_widget_compute_expand, update_computed_expand)
27
+ // Reference: refs/gtk/gtk/gtkbox.c:181-194 (every child, OR-ed)
28
+ // Reference: refs/gtk/gtk/gtkstack.c:2342-2373 (every page, visible or not)
29
+ // Reference: refs/gtk/gtk/gtkoverlay.c:292-301 (the main child only)
30
+ // Reference: refs/gtk/gtk/gtkscrolledwindow.c:544-554, gtkviewport.c:211-220 (the child only)
31
+ // Reference: refs/libadwaita/src/adw-widget-utils.c:501-536 (adw_widget_compute_expand)
32
+ // Copyright (c) The GTK Team. LGPLv2.1+. Copyright (c) The libadwaita authors. LGPLv2.1+.
33
+
34
+ export type ExpandOrientation = 'horizontal' | 'vertical';
35
+
36
+ /**
37
+ * Per tag that has a `compute_expand` vfunc: the subtrees of its own that are NOT the children
38
+ * the vfunc reads, and whether it answers on one axis only. A tag missing here behaves as the
39
+ * base GtkWidget — it does not propagate.
40
+ */
41
+ interface ExpandVfunc {
42
+ readonly skip?: string;
43
+ readonly horizontalOnly?: boolean;
44
+ }
45
+
46
+ /** `adw_widget_compute_expand` (adw-widget-utils.c:501), by the class_init that installs it. */
47
+ const ADW_WIDGET: ExpandVfunc = {};
48
+
49
+ const VFUNCS: ReadonlyMap<string, ExpandVfunc> = new Map<string, ExpandVfunc>([
50
+ ['gtk-box', {}],
51
+ ['gtk-stack', {}],
52
+ // The overlay children are its children too, but `gtk_overlay_compute_expand` reads only `child`.
53
+ ['gtk-overlay', { skip: '.adw-overlay-layer' }],
54
+ // The scrollbars are children the vfunc skips; they are leaves, so skipping them is exact.
55
+ ['gtk-scrolled-window', { skip: 'gtk-scrollbar' }],
56
+ ['gtk-viewport', {}],
57
+ ['adw-bin', ADW_WIDGET], // adw-bin.c:108
58
+ ['adw-bottom-sheet', ADW_WIDGET], // adw-bottom-sheet.c:795
59
+ ['adw-breakpoint-bin', ADW_WIDGET], // adw-breakpoint-bin.c:623
60
+ ['adw-clamp', ADW_WIDGET], // adw-clamp.c:174
61
+ ['adw-clamp-scrollable', ADW_WIDGET], // adw-clamp-scrollable.c:244
62
+ ['adw-layout-slot', ADW_WIDGET], // adw-layout-slot.c:128
63
+ ['adw-multi-layout-view', ADW_WIDGET], // adw-multi-layout-view.c:354
64
+ ['adw-navigation-view', ADW_WIDGET], // adw-navigation-view.c:1900
65
+ ['adw-overlay-split-view', ADW_WIDGET], // adw-overlay-split-view.c:909
66
+ ['adw-status-page', ADW_WIDGET], // adw-status-page.c:213
67
+ ['adw-toast-overlay', ADW_WIDGET], // adw-toast-overlay.c:543
68
+ ['adw-toolbar-view', ADW_WIDGET], // adw-toolbar-view.c:525
69
+ ['adw-view-stack', ADW_WIDGET], // adw-view-stack.c:1520
70
+ ['adw-header-bar', { horizontalOnly: true }], // adw-header-bar.c:853
71
+ ]);
72
+
73
+ const ATTRIBUTE: Record<ExpandOrientation, string> = { horizontal: 'hexpand', vertical: 'vexpand' };
74
+
75
+ /** The base tag whose vfunc `el` inherits — its own tag, or the one a registered subclass extends. */
76
+ function vfuncOf(el: Element): ExpandVfunc | undefined {
77
+ const own = VFUNCS.get(el.localName);
78
+ if (own !== undefined) return own;
79
+ if (typeof customElements.getName !== 'function') return undefined;
80
+ for (let proto = Object.getPrototypeOf(el); proto && proto !== HTMLElement.prototype;) {
81
+ const tag = customElements.getName(proto.constructor as CustomElementConstructor);
82
+ const found = tag === null ? undefined : VFUNCS.get(tag);
83
+ if (found !== undefined) return found;
84
+ proto = Object.getPrototypeOf(proto);
85
+ }
86
+ return undefined;
87
+ }
88
+
89
+ /**
90
+ * A GTK widget, as opposed to a wrapper this renderer added: every element is a custom element,
91
+ * and a plain element an author gave an expand value stands in for one.
92
+ */
93
+ function isWidget(el: Element): boolean {
94
+ return el.localName.includes('-') || el.hasAttribute('hexpand') || el.hasAttribute('vexpand');
95
+ }
96
+
97
+ /**
98
+ * A view stack writes `hidden` on every page but the selected one. That is
99
+ * `gtk_widget_set_child_visible` (adw-view-stack.c:1016-1023), not `GtkWidget:visible`, and
100
+ * `gtk_widget_compute_expand` reads only the latter — so a background page still counts.
101
+ */
102
+ function isStackPage(el: Element): boolean {
103
+ return el.classList.contains('adw-view-stack-child') && el.parentElement?.localName === 'adw-view-stack';
104
+ }
105
+
106
+ /** The value of an authored `hexpand` / `vexpand`: `""` is true, `"false"` false, absent unset. */
107
+ export function explicitExpand(el: Element, orientation: ExpandOrientation): boolean | undefined {
108
+ const raw = el.getAttribute(ATTRIBUTE[orientation]);
109
+ return raw === null ? undefined : raw !== 'false';
110
+ }
111
+
112
+ /** `gtk_widget_compute_expand (el, orientation)`. */
113
+ export function computeExpand(el: Element, orientation: ExpandOrientation): boolean {
114
+ if (el.hasAttribute('hidden') && !isStackPage(el)) return false;
115
+ const explicit = explicitExpand(el, orientation);
116
+ if (explicit !== undefined) return explicit;
117
+ const vfunc = vfuncOf(el);
118
+ if (vfunc === undefined) return false;
119
+ if (vfunc.horizontalOnly && orientation === 'vertical') return false;
120
+ return anyChildExpands(el, orientation, vfunc.skip);
121
+ }
122
+
123
+ /** The OR over the widget children of `parent`, reached through any wrapper that is not one. */
124
+ function anyChildExpands(parent: Element, orientation: ExpandOrientation, skip: string | undefined): boolean {
125
+ for (const child of parent.children) {
126
+ if (skip !== undefined && child.matches(skip)) continue;
127
+ if (isWidget(child)) {
128
+ if (computeExpand(child, orientation)) return true;
129
+ } else if (anyChildExpands(child, orientation, undefined)) {
130
+ return true;
131
+ }
132
+ }
133
+ return false;
134
+ }
@@ -13,7 +13,7 @@ import { describe, expect, it } from '@gjsify/unit';
13
13
 
14
14
  import type { AdwAlertDialog } from './elements/adw-alert-dialog.js';
15
15
  import { capabilities } from './capabilities.mjs';
16
- import { buildSharedTree, mountSharedTree } from './shared-tree-builder.js';
16
+ import { buildSharedTree, buildWithSiblings, mountSharedTree } from './shared-tree-builder.js';
17
17
  import { registerTemplateClass } from './template-classes.js';
18
18
 
19
19
  const vector = (kind: string) => CONSTRUCT_VECTORS.find((each) => each.kind === kind)!.tree;
@@ -24,8 +24,21 @@ registerTemplateClass(EXTERN_VECTOR_CLASS, 'corpus-extern');
24
24
 
25
25
  function observe(vector: ConstructVector): unknown {
26
26
  if (vector.kind === 'sibling-object') {
27
- buildSharedTree(vector.tree);
28
- return undefined;
27
+ let feed: ((size: { width: number; height: number }) => void) | undefined;
28
+ const { root, siblings } = buildWithSiblings(vector.tree, {
29
+ observeSize: (_element, onSize) => {
30
+ feed = onSize;
31
+ return () => {};
32
+ },
33
+ });
34
+ const beside = siblings.get('beside')!;
35
+ return {
36
+ insideRoot: root.contains(beside) || beside.parentElement !== null,
37
+ label: BREAKPOINT_VECTOR_SIZES.map((size) => {
38
+ feed!(size);
39
+ return beside.getAttribute('label');
40
+ }),
41
+ };
29
42
  }
30
43
  if (vector.kind === 'layout') {
31
44
  // CSS Grid lines count from 1, GTK cells from 0: `2 / span 2` is column 1, columnSpan 2.
@@ -93,15 +106,22 @@ function observe(vector: ConstructVector): unknown {
93
106
  }
94
107
  const { root, unmount } = mountSharedTree(vector.tree);
95
108
  try {
109
+ if (vector.kind === 'accessibility') {
110
+ // Read off the inner `<button>`, the one that carries the role and is announced.
111
+ const control = (root.querySelector('#described') as unknown as { button: HTMLButtonElement }).button;
112
+ return [
113
+ { label: control.getAttribute('aria-label'), description: control.getAttribute('aria-description') },
114
+ ];
115
+ }
96
116
  if (vector.kind === 'strings') {
97
117
  return [...root.querySelectorAll('option, .adw-drop-down-item-label')].map((node) => node.textContent);
98
118
  }
99
119
  const dialog = root as unknown as AdwAlertDialog;
100
120
  return (vector.tree.extensions?.responses ?? []).map(({ id }) => ({
101
121
  id,
102
- label: dialog.getResponseLabel(id),
103
- appearance: dialog.getResponseAppearance(id),
104
- enabled: dialog.getResponseEnabled(id),
122
+ label: dialog.get_response_label(id),
123
+ appearance: dialog.get_response_appearance(id),
124
+ enabled: dialog.get_response_enabled(id),
105
125
  }));
106
126
  } finally {
107
127
  unmount();
@@ -139,7 +159,9 @@ export const AdwConstructVectorsTest = async () => {
139
159
  children: [{ tag: 'GtkButton', signals: [signal] }],
140
160
  });
141
161
  const scope = { onX: () => {} };
142
- expect(() => mountSharedTree(on({ name: 'map', handler: 'onX' }), { scope })).toThrow("no signal 'map'");
162
+ expect(() => mountSharedTree(on({ name: 'toggled', handler: 'onX' }), { scope })).toThrow(
163
+ "no signal 'toggled'",
164
+ );
143
165
  expect(() => mountSharedTree(on({ name: 'clicked', handler: 'onX', flags: ['after'] }), { scope })).toThrow(
144
166
  'plain handlers only',
145
167
  );
@@ -331,6 +353,74 @@ export const AdwConstructVectorsTest = async () => {
331
353
  expect(() => mountSharedTree(withBreakpoint('template'))).toThrow("id 'template'");
332
354
  expect(() => mountSharedTree(withBreakpoint('caption', 'wide please'))).toThrow('breakpoint condition');
333
355
  });
356
+ await it('hands a sibling back by id, a dialog included, and builds none of them into the root', () => {
357
+ const tree: SharedTreeNode = {
358
+ tag: 'GtkBox',
359
+ children: [{ tag: 'GtkLabel', id: 'inside' }],
360
+ siblings: [
361
+ { tag: 'AdwAlertDialog', id: 'confirm', props: { heading: 'Save?' } },
362
+ { tag: EXTERN_VECTOR_CLASS, extern: true, id: 'screen' },
363
+ ],
364
+ };
365
+ const { root, siblings, objects } = buildWithSiblings(tree);
366
+ expect([...siblings.keys()].join()).toBe('confirm,screen');
367
+ expect(siblings.get('screen') instanceof CorpusExtern).toBe(true);
368
+ expect(siblings.get('confirm')!.localName).toBe('adw-alert-dialog');
369
+ expect(siblings.get('confirm')!.getAttribute('heading')).toBe('Save?');
370
+ expect(root.querySelector('#screen')).toBe(null);
371
+ expect([...objects.keys()].sort().join()).toBe('confirm,inside,screen');
372
+ });
373
+ await it('refuses siblings through every one-root door, a sibling without an id, and a duplicate id', () => {
374
+ const withSiblings = (siblings: SharedTreeNode[]): SharedTreeNode => ({
375
+ tag: 'GtkBox',
376
+ children: [{ tag: 'GtkLabel', id: 'taken' }],
377
+ siblings,
378
+ });
379
+ const labelled = withSiblings([{ tag: 'GtkLabel', id: 'beside' }]);
380
+ expect(() => buildSharedTree(labelled)).toThrow('buildWithSiblings');
381
+ expect(() => mountSharedTree(labelled)).toThrow('buildWithSiblings');
382
+ expect(() => buildWithSiblings(withSiblings([{ tag: 'GtkLabel' }]))).toThrow('has no id');
383
+ expect(() => buildWithSiblings(withSiblings([{ tag: 'GtkLabel', id: 'taken' }]))).toThrow("id 'taken'");
384
+ });
385
+ await it('refuses a breakpoint setter that names a sibling the tree does not carry', () => {
386
+ expect(() => buildWithSiblings({ ...vector('sibling-object'), siblings: [] })).toThrow("id 'beside'");
387
+ });
388
+ await it('names a check button on its input, and lets an authored name beat the icon fallback', () => {
389
+ const { root, unmount } = mountSharedTree({
390
+ tag: 'GtkBox',
391
+ children: [
392
+ { tag: 'GtkCheckButton', id: 'check', accessibility: { label: { value: 'Dark' } } },
393
+ {
394
+ tag: 'GtkButton',
395
+ id: 'icon',
396
+ props: { 'icon-name': 'edit-copy-symbolic', 'tooltip-text': 'Copy' },
397
+ accessibility: { label: { value: 'Copy the link' } },
398
+ },
399
+ ],
400
+ });
401
+ try {
402
+ const check = root.querySelector('#check')!;
403
+ const input = check.querySelector('.adw-check-input')!;
404
+ expect(input.getAttribute('aria-label')).toBe('Dark');
405
+ expect(input.hasAttribute('aria-description')).toBe(false);
406
+ check.removeAttribute('aria-label');
407
+ expect(input.hasAttribute('aria-label')).toBe(false);
408
+ const icon = root.querySelector('#icon') as unknown as HTMLElement & { button: HTMLButtonElement };
409
+ expect(icon.button.getAttribute('aria-label')).toBe('Copy the link');
410
+ icon.removeAttribute('aria-label');
411
+ expect(icon.button.getAttribute('aria-label')).toBe('Copy');
412
+ } finally {
413
+ unmount();
414
+ }
415
+ });
416
+ await it('refuses an accessibility name it has no door for, naming it', () => {
417
+ const tree: SharedTreeNode = {
418
+ tag: 'GtkBox',
419
+ children: [{ tag: 'GtkCheckButton', accessibility: { checked: { value: true } } }],
420
+ };
421
+ expect(() => buildSharedTree(tree)).toThrow("'checked'");
422
+ expect(() => mountSharedTree(tree)).toThrow('cannot be rendered by adwaita-web');
423
+ });
334
424
  await it('does not read a registered name for a node that is not extern', () => {
335
425
  const el = buildSharedTree({ tag: 'GtkBox', children: [{ tag: 'GtkLabel' }] });
336
426
  expect(el.children[0]!.localName).toBe('gtk-label');
@@ -0,0 +1,70 @@
1
+ // Single-child containers hand their child the WHOLE allocation, on both axes: GtkOverlay's main
2
+ // child (gtkoverlay.c size_allocate), AdwToastOverlay's child, GtkStack's visible page
3
+ // (gtkstack.c:2778-2831) and AdwBin's child (gtkbinlayout.c:81). A block box gives a child only
4
+ // its content height, and a percentage resolves against an indefinite one — these specs pin the
5
+ // chain a real window builds: overlay > toast-overlay > stack > bin > view-stack.
6
+ import { describe, expect, it } from '@gjsify/unit';
7
+
8
+ function mount(html: string, width = 400, height = 300): { host: HTMLElement; box: (sel: string) => DOMRect } {
9
+ const host = document.createElement('div');
10
+ host.style.cssText = `width:${width}px;height:${height}px;display:flex;flex-direction:column`;
11
+ host.innerHTML = html;
12
+ document.body.appendChild(host);
13
+ const box = (sel: string) => (host.querySelector(sel) as HTMLElement).getBoundingClientRect();
14
+ return { host, box };
15
+ }
16
+
17
+ export const ContainerFillTest = async () => {
18
+ await describe('single-child containers fill', async () => {
19
+ await it('<gtk-overlay> gives its main child the whole sized overlay', () => {
20
+ const { host, box } = mount('<gtk-overlay style="flex:1"><div id="main"></div></gtk-overlay>');
21
+ expect(box('#main').width).toBe(400);
22
+ expect(box('#main').height).toBe(300);
23
+ host.remove();
24
+ });
25
+
26
+ await it('<gtk-overlay> still measures as its main child when unsized', () => {
27
+ const { host, box } = mount('<gtk-overlay id="o"><div style="height:70px"></div></gtk-overlay>');
28
+ expect(box('#o').height).toBe(70);
29
+ host.remove();
30
+ });
31
+
32
+ await it('<adw-toast-overlay> gives its child the whole overlay', () => {
33
+ const { host, box } = mount('<adw-toast-overlay style="flex:1"><div id="c"></div></adw-toast-overlay>');
34
+ expect(box('#c').width).toBe(400);
35
+ expect(box('#c').height).toBe(300);
36
+ host.remove();
37
+ });
38
+
39
+ await it('a default <gtk-stack> page fills both axes, even with a zero-intrinsic child', () => {
40
+ const { host, box } = mount(
41
+ '<gtk-stack style="flex:1"><div id="p" name="a"><div id="z"></div></div></gtk-stack>',
42
+ );
43
+ expect(box('#p').width).toBe(400);
44
+ expect(box('#p').height).toBe(300);
45
+ host.remove();
46
+ });
47
+
48
+ await it('hhomogeneous="false" sizes a page to max-content', () => {
49
+ const { host, box } = mount(
50
+ '<gtk-stack hhomogeneous="false" style="flex:1"><div id="p" name="a" style="width:max-content"><span>hi</span></div></gtk-stack>',
51
+ );
52
+ expect(box('#p').width < 400).toBe(true);
53
+ host.remove();
54
+ });
55
+
56
+ await it('overlay > toast-overlay > stack > bin > view-stack fills the box', async () => {
57
+ const { host, box } = mount(
58
+ `<gtk-overlay style="flex:1"><adw-toast-overlay><gtk-stack>
59
+ <adw-bin id="b" name="a"><adw-view-stack id="v" style="flex:1"></adw-view-stack></adw-bin>
60
+ </gtk-stack></adw-toast-overlay></gtk-overlay>`,
61
+ );
62
+ await new Promise((resolve) => requestAnimationFrame(resolve));
63
+ for (const sel of ['adw-toast-overlay', 'gtk-stack', '#b', '#v']) {
64
+ expect(box(sel).width).toBe(400);
65
+ expect(box(sel).height).toBe(300);
66
+ }
67
+ host.remove();
68
+ });
69
+ });
70
+ };