@gjsify/adwaita-web 0.53.0 → 0.55.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 (440) hide show
  1. package/dist/adwaita-web.css +8070 -3037
  2. package/dist/adwaita-web.css.map +1 -1
  3. package/lib/types/adw-application-window.spec.d.ts +1 -0
  4. package/lib/types/adw-bin.spec.d.ts +1 -0
  5. package/lib/types/adw-breakpoint-bin.spec.d.ts +1 -0
  6. package/lib/types/adw-clamp-scrollable.spec.d.ts +1 -0
  7. package/lib/types/adw-multi-layout-view.spec.d.ts +1 -0
  8. package/lib/types/adw-preferences-row.spec.d.ts +1 -0
  9. package/lib/types/adw-shortcuts.spec.d.ts +1 -0
  10. package/lib/types/adw-tab-bar.spec.d.ts +1 -0
  11. package/lib/types/adw-tab-button.spec.d.ts +1 -0
  12. package/lib/types/adw-tab-overview.spec.d.ts +1 -0
  13. package/lib/types/adw-view-switcher-sidebar.spec.d.ts +1 -0
  14. package/lib/types/adw-window.spec.d.ts +1 -0
  15. package/lib/types/breakpoints.d.ts +14 -3
  16. package/lib/types/construct-vectors.spec.d.ts +1 -0
  17. package/lib/types/elements/adw-application-window.d.ts +27 -0
  18. package/lib/types/elements/adw-bin.d.ts +17 -0
  19. package/lib/types/elements/adw-breakpoint-bin.d.ts +67 -0
  20. package/lib/types/elements/adw-clamp-scrollable.d.ts +38 -0
  21. package/lib/types/elements/adw-dialog.d.ts +10 -0
  22. package/lib/types/elements/adw-layout-slot.d.ts +19 -0
  23. package/lib/types/elements/adw-multi-layout-view.d.ts +101 -0
  24. package/lib/types/elements/adw-preferences-row.d.ts +31 -0
  25. package/lib/types/elements/adw-shortcuts-dialog.d.ts +13 -0
  26. package/lib/types/elements/adw-shortcuts-item.d.ts +36 -0
  27. package/lib/types/elements/adw-shortcuts-section.d.ts +14 -0
  28. package/lib/types/elements/adw-sidebar.d.ts +5 -0
  29. package/lib/types/elements/adw-switch-row.d.ts +3 -0
  30. package/lib/types/elements/adw-tab-bar.d.ts +108 -0
  31. package/lib/types/elements/adw-tab-button.d.ts +32 -0
  32. package/lib/types/elements/adw-tab-overview.d.ts +199 -0
  33. package/lib/types/elements/adw-tab-view.d.ts +19 -23
  34. package/lib/types/elements/adw-view-switcher-sidebar.d.ts +84 -0
  35. package/lib/types/elements/adw-window.d.ts +40 -0
  36. package/lib/types/elements/gtk-about-dialog.d.ts +146 -0
  37. package/lib/types/elements/gtk-application-window.d.ts +34 -0
  38. package/lib/types/elements/gtk-aspect-frame.d.ts +27 -0
  39. package/lib/types/elements/gtk-button.d.ts +9 -0
  40. package/lib/types/elements/gtk-calendar.d.ts +159 -0
  41. package/lib/types/elements/gtk-center-box.d.ts +39 -0
  42. package/lib/types/elements/gtk-color-dialog-button.d.ts +69 -0
  43. package/lib/types/elements/gtk-column-view.d.ts +50 -0
  44. package/lib/types/elements/gtk-drag-icon.d.ts +78 -0
  45. package/lib/types/elements/gtk-drawing-area.d.ts +95 -0
  46. package/lib/types/elements/gtk-editable-label.d.ts +33 -0
  47. package/lib/types/elements/gtk-emoji-chooser.d.ts +84 -0
  48. package/lib/types/elements/gtk-entry.d.ts +20 -1
  49. package/lib/types/elements/gtk-expander.d.ts +49 -0
  50. package/lib/types/elements/gtk-fixed.d.ts +13 -0
  51. package/lib/types/elements/gtk-flow-box-child.d.ts +14 -0
  52. package/lib/types/elements/gtk-flow-box.d.ts +87 -0
  53. package/lib/types/elements/gtk-font-dialog-button.d.ts +83 -0
  54. package/lib/types/elements/gtk-frame.d.ts +24 -0
  55. package/lib/types/elements/gtk-gl-area.d.ts +136 -0
  56. package/lib/types/elements/gtk-graphics-offload.d.ts +24 -0
  57. package/lib/types/elements/gtk-grid-view.d.ts +39 -0
  58. package/lib/types/elements/gtk-grid.d.ts +27 -0
  59. package/lib/types/elements/gtk-header-bar.d.ts +60 -0
  60. package/lib/types/elements/gtk-inscription.d.ts +64 -0
  61. package/lib/types/elements/gtk-label.d.ts +3 -0
  62. package/lib/types/elements/gtk-level-bar.d.ts +59 -0
  63. package/lib/types/elements/gtk-link-button.d.ts +29 -0
  64. package/lib/types/elements/gtk-list-box-row.d.ts +54 -0
  65. package/lib/types/elements/gtk-list-box.d.ts +100 -0
  66. package/lib/types/elements/gtk-list-view.d.ts +73 -0
  67. package/lib/types/elements/gtk-media-controls.d.ts +142 -0
  68. package/lib/types/elements/gtk-notebook.d.ts +148 -0
  69. package/lib/types/elements/gtk-overlay.d.ts +22 -0
  70. package/lib/types/elements/gtk-page-setup-unix-dialog.d.ts +87 -0
  71. package/lib/types/elements/gtk-paned.d.ts +121 -0
  72. package/lib/types/elements/gtk-password-entry.d.ts +27 -0
  73. package/lib/types/elements/gtk-picture.d.ts +66 -0
  74. package/lib/types/elements/gtk-popover-bin.d.ts +82 -0
  75. package/lib/types/elements/gtk-popover-menu-bar.d.ts +66 -0
  76. package/lib/types/elements/gtk-popover-menu.d.ts +83 -0
  77. package/lib/types/elements/gtk-popover.d.ts +28 -3
  78. package/lib/types/elements/gtk-print-unix-dialog.d.ts +167 -0
  79. package/lib/types/elements/gtk-revealer.d.ts +55 -0
  80. package/lib/types/elements/gtk-scale-button.d.ts +59 -0
  81. package/lib/types/elements/gtk-scale.d.ts +98 -0
  82. package/lib/types/elements/gtk-scrollbar.d.ts +124 -0
  83. package/lib/types/elements/gtk-scrolled-window.d.ts +121 -0
  84. package/lib/types/elements/gtk-search-bar.d.ts +39 -0
  85. package/lib/types/elements/gtk-search-entry.d.ts +27 -0
  86. package/lib/types/elements/gtk-separator.d.ts +10 -0
  87. package/lib/types/elements/gtk-spin-button.d.ts +94 -0
  88. package/lib/types/elements/gtk-spinner.d.ts +14 -0
  89. package/lib/types/elements/gtk-stack-sidebar.d.ts +43 -0
  90. package/lib/types/elements/gtk-stack-switcher.d.ts +58 -0
  91. package/lib/types/elements/gtk-stack.d.ts +154 -0
  92. package/lib/types/elements/gtk-switch.d.ts +4 -1
  93. package/lib/types/elements/gtk-text-view.d.ts +62 -0
  94. package/lib/types/elements/gtk-text.d.ts +45 -0
  95. package/lib/types/elements/gtk-toggle-button.d.ts +45 -0
  96. package/lib/types/elements/gtk-tree-expander.d.ts +38 -0
  97. package/lib/types/elements/gtk-video.d.ts +97 -0
  98. package/lib/types/elements/gtk-viewport.d.ts +84 -0
  99. package/lib/types/elements/gtk-window-controls.d.ts +80 -0
  100. package/lib/types/elements/gtk-window-handle.d.ts +39 -0
  101. package/lib/types/elements/gtk-window.d.ts +97 -0
  102. package/lib/types/elements/justification.d.ts +2 -0
  103. package/lib/types/elements/popover-menu.d.ts +18 -0
  104. package/lib/types/elements/roving-focus.d.ts +29 -1
  105. package/lib/types/emoji-data.d.ts +38 -0
  106. package/lib/types/font-description.d.ts +96 -0
  107. package/lib/types/gtk-about-dialog.spec.d.ts +1 -0
  108. package/lib/types/gtk-action-bar.spec.d.ts +1 -0
  109. package/lib/types/gtk-application-window.spec.d.ts +1 -0
  110. package/lib/types/gtk-aspect-frame.spec.d.ts +1 -0
  111. package/lib/types/gtk-calendar.spec.d.ts +1 -0
  112. package/lib/types/gtk-center-box.spec.d.ts +1 -0
  113. package/lib/types/gtk-color-dialog-button.spec.d.ts +1 -0
  114. package/lib/types/gtk-column-view.spec.d.ts +1 -0
  115. package/lib/types/gtk-drag-icon.spec.d.ts +1 -0
  116. package/lib/types/gtk-drawing-area.spec.d.ts +1 -0
  117. package/lib/types/gtk-editable-label.spec.d.ts +1 -0
  118. package/lib/types/gtk-emoji-chooser.spec.d.ts +1 -0
  119. package/lib/types/gtk-expander.spec.d.ts +1 -0
  120. package/lib/types/gtk-fixed.spec.d.ts +1 -0
  121. package/lib/types/gtk-flow-box.spec.d.ts +1 -0
  122. package/lib/types/gtk-font-dialog-button.spec.d.ts +1 -0
  123. package/lib/types/gtk-frame.spec.d.ts +1 -0
  124. package/lib/types/gtk-gl-area.spec.d.ts +1 -0
  125. package/lib/types/gtk-graphics-offload.spec.d.ts +1 -0
  126. package/lib/types/gtk-grid-view.spec.d.ts +1 -0
  127. package/lib/types/gtk-grid.spec.d.ts +1 -0
  128. package/lib/types/gtk-header-bar.spec.d.ts +1 -0
  129. package/lib/types/gtk-inscription.spec.d.ts +1 -0
  130. package/lib/types/gtk-level-bar.spec.d.ts +1 -0
  131. package/lib/types/gtk-link-button.spec.d.ts +1 -0
  132. package/lib/types/gtk-list-box.spec.d.ts +1 -0
  133. package/lib/types/gtk-list-view.spec.d.ts +1 -0
  134. package/lib/types/gtk-media-controls.spec.d.ts +1 -0
  135. package/lib/types/gtk-notebook.spec.d.ts +1 -0
  136. package/lib/types/gtk-overlay.spec.d.ts +1 -0
  137. package/lib/types/gtk-paned.spec.d.ts +1 -0
  138. package/lib/types/gtk-password-entry.spec.d.ts +1 -0
  139. package/lib/types/gtk-picture.spec.d.ts +1 -0
  140. package/lib/types/gtk-popover-bin.spec.d.ts +1 -0
  141. package/lib/types/gtk-popover-menu-bar.spec.d.ts +1 -0
  142. package/lib/types/gtk-popover-menu.spec.d.ts +1 -0
  143. package/lib/types/gtk-popover-role.spec.d.ts +1 -0
  144. package/lib/types/gtk-popover.spec.d.ts +1 -0
  145. package/lib/types/gtk-print-dialogs.spec.d.ts +1 -0
  146. package/lib/types/gtk-revealer.spec.d.ts +1 -0
  147. package/lib/types/gtk-scale-button.spec.d.ts +1 -0
  148. package/lib/types/gtk-scale.spec.d.ts +1 -0
  149. package/lib/types/gtk-scrollbar.spec.d.ts +1 -0
  150. package/lib/types/gtk-scrolled-window.spec.d.ts +1 -0
  151. package/lib/types/gtk-search-bar.spec.d.ts +1 -0
  152. package/lib/types/gtk-search-entry.spec.d.ts +1 -0
  153. package/lib/types/gtk-separator.spec.d.ts +1 -0
  154. package/lib/types/gtk-spin-button.spec.d.ts +1 -0
  155. package/lib/types/gtk-spinner.spec.d.ts +1 -0
  156. package/lib/types/gtk-stack-sidebar.spec.d.ts +1 -0
  157. package/lib/types/gtk-stack-switcher.spec.d.ts +1 -0
  158. package/lib/types/gtk-stack.spec.d.ts +1 -0
  159. package/lib/types/gtk-text-view.spec.d.ts +1 -0
  160. package/lib/types/gtk-text.spec.d.ts +1 -0
  161. package/lib/types/gtk-toggle-button.spec.d.ts +1 -0
  162. package/lib/types/gtk-tree-expander.spec.d.ts +1 -0
  163. package/lib/types/gtk-video.spec.d.ts +1 -0
  164. package/lib/types/gtk-viewport.spec.d.ts +1 -0
  165. package/lib/types/gtk-window-controls.spec.d.ts +1 -0
  166. package/lib/types/gtk-window-handle.spec.d.ts +1 -0
  167. package/lib/types/gtk-window.spec.d.ts +1 -0
  168. package/lib/types/index.d.ts +4 -1
  169. package/lib/types/list-view-dom.d.ts +44 -0
  170. package/lib/types/namespace/adw.d.ts +12 -0
  171. package/lib/types/namespace/gtk.d.ts +61 -0
  172. package/lib/types/page-setup.d.ts +100 -0
  173. package/lib/types/scroll-shading.d.ts +15 -1
  174. package/lib/types/shared-tree-builder.d.ts +30 -2
  175. package/lib/types/signals.d.ts +4 -0
  176. package/lib/types/styles.generated.d.ts +1 -1
  177. package/lib/types/tab-chip.d.ts +73 -0
  178. package/lib/types/template-classes.d.ts +11 -0
  179. package/package.json +12 -8
  180. package/scss/_action_row.scss +2 -2
  181. package/scss/_application_window.scss +26 -0
  182. package/scss/_aspect_frame.scss +25 -0
  183. package/scss/_bin.scss +26 -0
  184. package/scss/_button.scss +45 -0
  185. package/scss/_calendar.scss +176 -0
  186. package/scss/_center_box.scss +25 -0
  187. package/scss/_checks.scss +6 -1
  188. package/scss/_clamp_scrollable.scss +28 -0
  189. package/scss/_color_button.scss +67 -0
  190. package/scss/_column_view.scss +126 -0
  191. package/scss/_drag_icon.scss +55 -0
  192. package/scss/_drawing.scss +85 -0
  193. package/scss/_emoji_chooser.scss +213 -0
  194. package/scss/_entry.scss +13 -5
  195. package/scss/_entry_row.scss +1 -1
  196. package/scss/_expander.scss +113 -0
  197. package/scss/_fixed.scss +29 -0
  198. package/scss/_flow_box.scss +94 -0
  199. package/scss/_font_button.scss +63 -0
  200. package/scss/_frame.scss +41 -0
  201. package/scss/_grid.scss +19 -0
  202. package/scss/_grid_view.scss +46 -0
  203. package/scss/_gtk_about_dialog.scss +186 -0
  204. package/scss/_headerbar.scss +132 -0
  205. package/scss/_icons.generated.scss +186 -0
  206. package/scss/_inscription.scss +125 -0
  207. package/scss/_level_bar.scss +190 -0
  208. package/scss/_link.scss +82 -0
  209. package/scss/_list_box.scss +88 -0
  210. package/scss/_list_view.scss +96 -0
  211. package/scss/_media_controls.scss +139 -0
  212. package/scss/_overlay.scss +68 -0
  213. package/scss/_paned.scss +130 -0
  214. package/scss/_password_entry.scss +83 -0
  215. package/scss/_picture.scss +80 -0
  216. package/scss/_popover_bin.scss +53 -0
  217. package/scss/_popover_menu_bar.scss +104 -0
  218. package/scss/_preferences_row.scss +51 -0
  219. package/scss/_print_dialogs.scss +328 -0
  220. package/scss/_reset.scss +21 -19
  221. package/scss/_revealer.scss +37 -0
  222. package/scss/_row.scss +20 -5
  223. package/scss/_scale.scss +298 -0
  224. package/scss/_scale_button.scss +167 -0
  225. package/scss/_scrollbar.scss +230 -0
  226. package/scss/_scrolled_window.scss +131 -0
  227. package/scss/_search_bar.scss +118 -0
  228. package/scss/_search_entry.scss +86 -0
  229. package/scss/_separator.scss +44 -0
  230. package/scss/_shortcuts_dialog.scss +100 -0
  231. package/scss/_spin_button.scss +172 -0
  232. package/scss/_spin_row.scss +56 -8
  233. package/scss/_spinner.scss +85 -4
  234. package/scss/_stack.scss +729 -0
  235. package/scss/_switch.scss +13 -5
  236. package/scss/_switch_row.scss +10 -0
  237. package/scss/_tab_bar.scss +317 -0
  238. package/scss/_tab_button.scss +98 -0
  239. package/scss/_tab_overview.scss +483 -0
  240. package/scss/_text.scss +182 -0
  241. package/scss/_theme.scss +1 -1
  242. package/scss/_tree_expander.scss +62 -0
  243. package/scss/_variables.scss +43 -21
  244. package/scss/_video.scss +146 -0
  245. package/scss/_view_switcher_sidebar.scss +112 -0
  246. package/scss/_viewport.scss +50 -0
  247. package/scss/_window.scss +118 -1
  248. package/scss/_window_handle.scss +20 -0
  249. package/scss/adwaita-skin.scss +109 -0
  250. package/src/adw-action-rows.spec.ts +76 -0
  251. package/src/adw-application-window.spec.ts +118 -0
  252. package/src/adw-bin.spec.ts +75 -0
  253. package/src/adw-breakpoint-bin.spec.ts +324 -0
  254. package/src/adw-clamp-scrollable.spec.ts +189 -0
  255. package/src/adw-multi-layout-view.spec.ts +243 -0
  256. package/src/adw-preferences-row.spec.ts +164 -0
  257. package/src/adw-row-state.spec.ts +142 -0
  258. package/src/adw-shortcuts.spec.ts +155 -0
  259. package/src/adw-tab-bar.spec.ts +287 -0
  260. package/src/adw-tab-button.spec.ts +101 -0
  261. package/src/adw-tab-overview.spec.ts +443 -0
  262. package/src/adw-view-switcher-sidebar.spec.ts +281 -0
  263. package/src/adw-window.spec.ts +129 -0
  264. package/src/breakpoints.ts +25 -8
  265. package/src/capabilities.d.mts +3 -0
  266. package/src/capabilities.mjs +18 -0
  267. package/src/connect-lifecycle.spec.ts +12 -0
  268. package/src/construct-vectors.spec.ts +175 -0
  269. package/src/elements/adw-application-window.ts +154 -0
  270. package/src/elements/adw-bin.ts +101 -0
  271. package/src/elements/adw-breakpoint-bin.ts +287 -0
  272. package/src/elements/adw-clamp-scrollable.ts +207 -0
  273. package/src/elements/adw-dialog.ts +19 -0
  274. package/src/elements/adw-layout-slot.ts +95 -0
  275. package/src/elements/adw-multi-layout-view.ts +326 -0
  276. package/src/elements/adw-preferences-row.ts +150 -0
  277. package/src/elements/adw-shortcuts-dialog.ts +52 -0
  278. package/src/elements/adw-shortcuts-item.ts +171 -0
  279. package/src/elements/adw-shortcuts-section.ts +86 -0
  280. package/src/elements/adw-sidebar.ts +42 -3
  281. package/src/elements/adw-switch-row.ts +28 -1
  282. package/src/elements/adw-tab-bar.ts +529 -0
  283. package/src/elements/adw-tab-button.ts +194 -0
  284. package/src/elements/adw-tab-overview.ts +1011 -0
  285. package/src/elements/adw-tab-view.ts +66 -145
  286. package/src/elements/adw-toggle-group.ts +20 -5
  287. package/src/elements/adw-view-stack.ts +18 -0
  288. package/src/elements/adw-view-switcher-sidebar.ts +332 -0
  289. package/src/elements/adw-window.ts +185 -11
  290. package/src/elements/checks.ts +35 -8
  291. package/src/elements/gtk-about-dialog.ts +811 -0
  292. package/src/elements/gtk-application-window.ts +108 -0
  293. package/src/elements/gtk-aspect-frame.ts +160 -0
  294. package/src/elements/gtk-button.ts +12 -0
  295. package/src/elements/gtk-calendar.ts +757 -0
  296. package/src/elements/gtk-center-box.ts +184 -0
  297. package/src/elements/gtk-color-dialog-button.ts +278 -0
  298. package/src/elements/gtk-column-view.ts +320 -0
  299. package/src/elements/gtk-drag-icon.ts +257 -0
  300. package/src/elements/gtk-drawing-area.ts +303 -0
  301. package/src/elements/gtk-editable-label.ts +204 -0
  302. package/src/elements/gtk-emoji-chooser.ts +542 -0
  303. package/src/elements/gtk-entry.ts +39 -5
  304. package/src/elements/gtk-expander.ts +301 -0
  305. package/src/elements/gtk-fixed.ts +99 -0
  306. package/src/elements/gtk-flow-box-child.ts +68 -0
  307. package/src/elements/gtk-flow-box.ts +480 -0
  308. package/src/elements/gtk-font-dialog-button.ts +432 -0
  309. package/src/elements/gtk-frame.ts +174 -0
  310. package/src/elements/gtk-gl-area.ts +476 -0
  311. package/src/elements/gtk-graphics-offload.ts +130 -0
  312. package/src/elements/gtk-grid-view.ts +168 -0
  313. package/src/elements/gtk-grid.ts +127 -0
  314. package/src/elements/gtk-header-bar.ts +273 -0
  315. package/src/elements/gtk-inscription.ts +387 -0
  316. package/src/elements/gtk-label.ts +19 -11
  317. package/src/elements/gtk-level-bar.ts +323 -0
  318. package/src/elements/gtk-link-button.ts +138 -0
  319. package/src/elements/gtk-list-box-row.ts +195 -0
  320. package/src/elements/gtk-list-box.ts +413 -0
  321. package/src/elements/gtk-list-view.ts +370 -0
  322. package/src/elements/gtk-media-controls.ts +509 -0
  323. package/src/elements/gtk-notebook.ts +664 -0
  324. package/src/elements/gtk-overlay.ts +149 -0
  325. package/src/elements/gtk-page-setup-unix-dialog.ts +420 -0
  326. package/src/elements/gtk-paned.ts +597 -0
  327. package/src/elements/gtk-password-entry.ts +170 -0
  328. package/src/elements/gtk-picture.ts +254 -0
  329. package/src/elements/gtk-popover-bin.ts +332 -0
  330. package/src/elements/gtk-popover-menu-bar.ts +355 -0
  331. package/src/elements/gtk-popover-menu.ts +339 -0
  332. package/src/elements/gtk-popover.ts +92 -10
  333. package/src/elements/gtk-print-unix-dialog.ts +989 -0
  334. package/src/elements/gtk-progress-bar.ts +24 -11
  335. package/src/elements/gtk-revealer.ts +406 -0
  336. package/src/elements/gtk-scale-button.ts +358 -0
  337. package/src/elements/gtk-scale.ts +492 -0
  338. package/src/elements/gtk-scrollbar.ts +528 -0
  339. package/src/elements/gtk-scrolled-window.ts +642 -0
  340. package/src/elements/gtk-search-bar.ts +200 -0
  341. package/src/elements/gtk-search-entry.ts +172 -0
  342. package/src/elements/gtk-separator.ts +52 -0
  343. package/src/elements/gtk-spin-button.ts +475 -0
  344. package/src/elements/gtk-spinner.ts +126 -0
  345. package/src/elements/gtk-stack-sidebar.ts +212 -0
  346. package/src/elements/gtk-stack-switcher.ts +279 -0
  347. package/src/elements/gtk-stack.ts +748 -0
  348. package/src/elements/gtk-switch.ts +52 -19
  349. package/src/elements/gtk-text-view.ts +290 -0
  350. package/src/elements/gtk-text.ts +219 -0
  351. package/src/elements/gtk-toggle-button.ts +239 -0
  352. package/src/elements/gtk-tree-expander.ts +243 -0
  353. package/src/elements/gtk-video.ts +423 -0
  354. package/src/elements/gtk-viewport.ts +277 -0
  355. package/src/elements/gtk-window-controls.ts +341 -0
  356. package/src/elements/gtk-window-handle.ts +221 -0
  357. package/src/elements/gtk-window.ts +366 -0
  358. package/src/elements/justification.ts +24 -0
  359. package/src/elements/popover-menu.ts +24 -0
  360. package/src/elements/roving-focus.ts +53 -21
  361. package/src/emoji-data.ts +577 -0
  362. package/src/entry-rows.spec.ts +31 -0
  363. package/src/font-description.ts +238 -0
  364. package/src/gtk-about-dialog.spec.ts +250 -0
  365. package/src/gtk-action-bar.spec.ts +89 -0
  366. package/src/gtk-application-window.spec.ts +107 -0
  367. package/src/gtk-aspect-frame.spec.ts +114 -0
  368. package/src/gtk-calendar.spec.ts +589 -0
  369. package/src/gtk-center-box.spec.ts +125 -0
  370. package/src/gtk-color-dialog-button.spec.ts +141 -0
  371. package/src/gtk-column-view.spec.ts +154 -0
  372. package/src/gtk-drag-icon.spec.ts +190 -0
  373. package/src/gtk-drawing-area.spec.ts +211 -0
  374. package/src/gtk-editable-label.spec.ts +190 -0
  375. package/src/gtk-emoji-chooser.spec.ts +173 -0
  376. package/src/gtk-entry.spec.ts +23 -0
  377. package/src/gtk-expander.spec.ts +170 -0
  378. package/src/gtk-fixed.spec.ts +97 -0
  379. package/src/gtk-flow-box.spec.ts +289 -0
  380. package/src/gtk-font-dialog-button.spec.ts +166 -0
  381. package/src/gtk-frame.spec.ts +119 -0
  382. package/src/gtk-gl-area.spec.ts +361 -0
  383. package/src/gtk-graphics-offload.spec.ts +119 -0
  384. package/src/gtk-grid-view.spec.ts +106 -0
  385. package/src/gtk-grid.spec.ts +122 -0
  386. package/src/gtk-header-bar.spec.ts +202 -0
  387. package/src/gtk-inscription.spec.ts +235 -0
  388. package/src/gtk-level-bar.spec.ts +199 -0
  389. package/src/gtk-link-button.spec.ts +117 -0
  390. package/src/gtk-list-box.spec.ts +422 -0
  391. package/src/gtk-list-view.spec.ts +203 -0
  392. package/src/gtk-media-controls.spec.ts +295 -0
  393. package/src/gtk-notebook.spec.ts +546 -0
  394. package/src/gtk-overlay.spec.ts +141 -0
  395. package/src/gtk-paned.spec.ts +267 -0
  396. package/src/gtk-password-entry.spec.ts +150 -0
  397. package/src/gtk-picture.spec.ts +172 -0
  398. package/src/gtk-popover-bin.spec.ts +229 -0
  399. package/src/gtk-popover-menu-bar.spec.ts +296 -0
  400. package/src/gtk-popover-menu.spec.ts +261 -0
  401. package/src/gtk-popover-role.spec.ts +138 -0
  402. package/src/gtk-popover.spec.ts +264 -0
  403. package/src/gtk-print-dialogs.spec.ts +251 -0
  404. package/src/gtk-revealer.spec.ts +164 -0
  405. package/src/gtk-scale-button.spec.ts +218 -0
  406. package/src/gtk-scale.spec.ts +213 -0
  407. package/src/gtk-scrollbar.spec.ts +244 -0
  408. package/src/gtk-scrolled-window.spec.ts +262 -0
  409. package/src/gtk-search-bar.spec.ts +179 -0
  410. package/src/gtk-search-entry.spec.ts +142 -0
  411. package/src/gtk-separator.spec.ts +74 -0
  412. package/src/gtk-spin-button.spec.ts +186 -0
  413. package/src/gtk-spinner.spec.ts +130 -0
  414. package/src/gtk-stack-sidebar.spec.ts +217 -0
  415. package/src/gtk-stack-switcher.spec.ts +249 -0
  416. package/src/gtk-stack.spec.ts +432 -0
  417. package/src/gtk-text-view.spec.ts +189 -0
  418. package/src/gtk-text.spec.ts +163 -0
  419. package/src/gtk-toggle-button.spec.ts +186 -0
  420. package/src/gtk-tree-expander.spec.ts +201 -0
  421. package/src/gtk-video.spec.ts +240 -0
  422. package/src/gtk-viewport.spec.ts +140 -0
  423. package/src/gtk-window-controls.spec.ts +232 -0
  424. package/src/gtk-window-handle.spec.ts +158 -0
  425. package/src/gtk-window.spec.ts +190 -0
  426. package/src/index.ts +9 -1
  427. package/src/keyboard-operable.spec.ts +33 -1
  428. package/src/list-view-dom.ts +121 -0
  429. package/src/namespace/adw.ts +12 -0
  430. package/src/namespace/gtk.ts +61 -0
  431. package/src/page-setup.ts +222 -0
  432. package/src/scroll-shading.ts +18 -1
  433. package/src/shared-tree-builder.ts +263 -21
  434. package/src/signals.ts +15 -0
  435. package/src/source-view/theme.ts +11 -5
  436. package/src/style-classes.spec.ts +9 -8
  437. package/src/styles.generated.ts +1 -1
  438. package/src/tab-chip.ts +200 -0
  439. package/src/template-classes.ts +45 -0
  440. package/src/test.browser.mts +152 -0
@@ -0,0 +1,130 @@
1
+ // DOM-level tests for <gtk-spinner>. GtkSpinner has exactly ONE property, so what
2
+ // matters is the four facts around it: `spinning` is the state and `:checked` (spelled
3
+ // `.checked`) is how the stylesheet sees it, `start()`/`stop()` carry the busy state as
4
+ // well as the property, and the drawn picture is the icon GTK loads — a faint ring with
5
+ // one quarter arc on it, which is a STATIC shape.
6
+ import { describe, expect, it } from '@gjsify/unit';
7
+
8
+ import type { GtkSpinner } from './elements/gtk-spinner.js';
9
+
10
+ function mount(attrs: Record<string, string> = {}): { el: GtkSpinner; host: HTMLElement } {
11
+ const host = document.createElement('div');
12
+ document.body.appendChild(host);
13
+ const el = document.createElement('gtk-spinner') as GtkSpinner;
14
+ for (const [name, value] of Object.entries(attrs)) el.setAttribute(name, value);
15
+ host.appendChild(el);
16
+ return { el, host };
17
+ }
18
+
19
+ export const GtkSpinnerTest = async () => {
20
+ await describe('<gtk-spinner> state', async () => {
21
+ await it('starts stopped, with no busy state announced', () => {
22
+ // gtkspinner.c:338-340 — the property's default is FALSE, and nothing has
23
+ // called start(), so there is no aria-busy either.
24
+ const { el, host } = mount();
25
+ expect(el.spinning).toBe(false);
26
+ expect(el.classList.contains('checked')).toBe(false);
27
+ expect(el.hasAttribute('aria-busy')).toBe(false);
28
+ host.remove();
29
+ });
30
+
31
+ await it('adopts a declarative spinning attribute without emitting', () => {
32
+ const host = document.createElement('div');
33
+ document.body.appendChild(host);
34
+ const el = document.createElement('gtk-spinner') as GtkSpinner;
35
+ const events: unknown[] = [];
36
+ el.addEventListener('notify::spinning', (e) => events.push((e as CustomEvent).detail));
37
+ el.setAttribute('spinning', '');
38
+ host.appendChild(el);
39
+ expect(el.spinning).toBe(true);
40
+ expect(el.classList.contains('checked')).toBe(true);
41
+ expect(events.length).toBe(0);
42
+ host.remove();
43
+ });
44
+
45
+ await it('a programmatic set notifies once per real change', () => {
46
+ const { el, host } = mount();
47
+ const events: unknown[] = [];
48
+ el.addEventListener('notify::spinning', (e) => events.push((e as CustomEvent).detail));
49
+ el.spinning = true;
50
+ el.spinning = true;
51
+ el.spinning = false;
52
+ expect(events).toStrictEqual([{ spinning: true }, { spinning: false }]);
53
+ host.remove();
54
+ });
55
+
56
+ await it('the attribute and the property are the same state', () => {
57
+ const { el, host } = mount();
58
+ el.setAttribute('spinning', '');
59
+ expect(el.spinning).toBe(true);
60
+ expect(el.classList.contains('checked')).toBe(true);
61
+ el.spinning = false;
62
+ expect(el.hasAttribute('spinning')).toBe(false);
63
+ expect(el.classList.contains('checked')).toBe(false);
64
+ host.remove();
65
+ });
66
+ });
67
+
68
+ await describe('<gtk-spinner> start / stop', async () => {
69
+ await it('start sets the property AND the busy state; stop clears both', () => {
70
+ // gtkspinner.c:373-383 and :391-400 — `gtk_accessible_update_state (…,
71
+ // GTK_ACCESSIBLE_STATE_BUSY, …)` is in both methods, not in the setter.
72
+ const { el, host } = mount();
73
+ el.start();
74
+ expect(el.spinning).toBe(true);
75
+ expect(el.getAttribute('aria-busy')).toBe('true');
76
+ el.stop();
77
+ expect(el.spinning).toBe(false);
78
+ expect(el.getAttribute('aria-busy')).toBe('false');
79
+ host.remove();
80
+ });
81
+
82
+ await it('setting the property alone does not announce a busy state', () => {
83
+ const { el, host } = mount();
84
+ el.spinning = true;
85
+ expect(el.hasAttribute('aria-busy')).toBe(false);
86
+ host.remove();
87
+ });
88
+ });
89
+
90
+ await describe('<gtk-spinner> picture', async () => {
91
+ await it('is a faint ring plus ONE quarter arc, the icon GTK loads', () => {
92
+ // refs/gtk/gtk/icons/process-working-symbolic.svg — a full circle at
93
+ // stroke-opacity 0.2 and the arc from (1,8) to (8,1) on a radius-7 circle.
94
+ const { el, host } = mount();
95
+ expect(el.querySelectorAll('.adw-spinner-track').length).toBe(1);
96
+ const arcs = el.querySelectorAll('.adw-spinner-arc');
97
+ expect(arcs.length).toBe(1);
98
+ expect((arcs[0] as SVGPathElement).getAttribute('d')).toBe('M 1 8 A 7 7 0 0 1 8 1');
99
+ const track = el.querySelector('.adw-spinner-track') as SVGCircleElement;
100
+ expect(getComputedStyle(track).strokeOpacity).toBe('0.2');
101
+ host.remove();
102
+ });
103
+
104
+ await it('carries the progressbar role and no value range', () => {
105
+ // gtkspinner.c:345 — GTK_ACCESSIBLE_ROLE_PROGRESS_BAR. There is no fraction
106
+ // to report, so none of aria-valuemin/max/now is written.
107
+ const { el, host } = mount();
108
+ expect(el.getAttribute('role')).toBe('progressbar');
109
+ expect(el.hasAttribute('aria-valuenow')).toBe(false);
110
+ host.remove();
111
+ });
112
+
113
+ await it('is 16px until a rule says otherwise, which is its icon size', () => {
114
+ // gtkspinner.c:110-123 — measured from `icon-size`, reported as minimum AND
115
+ // natural, so the box is square at the icon's intrinsic size.
116
+ const { el, host } = mount();
117
+ const box = getComputedStyle(el);
118
+ expect(box.width).toBe('16px');
119
+ expect(box.height).toBe('16px');
120
+ host.remove();
121
+ });
122
+
123
+ await it('dims when disabled, the way libadwaita dims a spinner node', () => {
124
+ // refs/libadwaita/src/stylesheet/widgets/_spinner.scss:1-3.
125
+ const { el, host } = mount({ disabled: '' });
126
+ expect(Number(getComputedStyle(el).opacity)).toBeLessThan(1);
127
+ host.remove();
128
+ });
129
+ });
130
+ };
@@ -0,0 +1,217 @@
1
+ // DOM-level tests for <gtk-stack-sidebar>. The widget is three separable things: the
2
+ // ROW-VISIBILITY rule (which is NOT the switcher's — no icon fallback here), the
3
+ // two-way binding with the stack, and the list's own node contract.
4
+ import { describe, expect, it } from '@gjsify/unit';
5
+
6
+ import type { GtkStack } from './elements/gtk-stack.js';
7
+ import type { GtkStackSidebar } from './elements/gtk-stack-sidebar.js';
8
+
9
+ interface Page {
10
+ name: string;
11
+ title?: string;
12
+ icon?: string;
13
+ hidden?: boolean;
14
+ attention?: boolean;
15
+ }
16
+
17
+ /** A stack with its pages, a sidebar bound to it by id, both in the document. */
18
+ function mount(
19
+ pages: Page[],
20
+ attrs: Record<string, string> = {},
21
+ ): { stack: GtkStack; sidebar: GtkStackSidebar; host: HTMLElement; stackId: string } {
22
+ const host = document.createElement('div');
23
+ document.body.appendChild(host);
24
+ // A UNIQUE id per mount: the switcher binds by id, so two stacks left in the document
25
+ // under one id make `getElementById` answer with whichever came first. A counter in
26
+ // this module is not enough, because the suite's cases are free to interleave.
27
+ const stackId = `stack-under-test-${crypto.randomUUID()}`;
28
+ const stack = document.createElement('gtk-stack') as GtkStack;
29
+ stack.id = stackId;
30
+ for (const page of pages) {
31
+ const child = document.createElement('div');
32
+ child.setAttribute('name', page.name);
33
+ if (page.title !== undefined) child.setAttribute('title', page.title);
34
+ if (page.icon !== undefined) child.setAttribute('icon-name', page.icon);
35
+ if (page.hidden) child.setAttribute('hidden', '');
36
+ if (page.attention) child.setAttribute('needs-attention', '');
37
+ stack.appendChild(child);
38
+ }
39
+ const sidebar = document.createElement('gtk-stack-sidebar') as GtkStackSidebar;
40
+ for (const [name, value] of Object.entries(attrs)) sidebar.setAttribute(name, value);
41
+ sidebar.setAttribute('stack', stackId);
42
+ host.append(stack, sidebar);
43
+ return { stack, sidebar, host, stackId };
44
+ }
45
+
46
+ /** The rows, with hidden ones marked, as the visibility rule leaves them. */
47
+ function rows(el: GtkStackSidebar): string[] {
48
+ return [...el.querySelectorAll<HTMLButtonElement>('.adw-stack-sidebar-row')].map((row) => {
49
+ const parts = [row.querySelector('.adw-stack-sidebar-title')?.textContent ?? ''];
50
+ if (row.hidden) parts.push('hidden');
51
+ if (row.classList.contains('selected')) parts.push('selected');
52
+ if (row.classList.contains('needs-attention')) parts.push('attention');
53
+ return parts.filter(Boolean).join(' ');
54
+ });
55
+ }
56
+
57
+ /** A turn of the event loop — a page's change reaches the sidebar through the stack's observer. */
58
+ function settle(): Promise<void> {
59
+ return new Promise((resolve) => setTimeout(resolve, 0));
60
+ }
61
+
62
+ const THREE = [
63
+ { name: 'a', title: 'Alpha' },
64
+ { name: 'b', title: 'Beta' },
65
+ { name: 'c', title: 'Gamma' },
66
+ ];
67
+
68
+ export const GtkStackSidebarTest = async () => {
69
+ await describe('<gtk-stack-sidebar> defaults', async () => {
70
+ await it('builds one row per page, the first one selected', () => {
71
+ const { sidebar, host } = mount(THREE);
72
+ expect(rows(sidebar)).toStrictEqual(['Alpha selected', 'Beta', 'Gamma']);
73
+ // C's own classes: `.sidebar` on the widget, `.navigation-sidebar` on the list
74
+ // (gtkstacksidebar.c:155, :163).
75
+ expect(sidebar.classList.contains('sidebar')).toBe(true);
76
+ const list = sidebar.querySelector('.adw-stack-sidebar-list');
77
+ expect(list?.classList.contains('navigation-sidebar')).toBe(true);
78
+ expect(list?.getAttribute('role')).toBe('listbox');
79
+ // The translated "Sidebar" of gtkstacksidebar.c:157-159.
80
+ expect(list?.getAttribute('aria-label')).toBe('Sidebar');
81
+ host.remove();
82
+ });
83
+
84
+ await it('a row is a link, selected state and all', () => {
85
+ const { sidebar, host } = mount(THREE);
86
+ const [first, second] = sidebar.querySelectorAll<HTMLButtonElement>('.adw-stack-sidebar-row');
87
+ expect(first.getAttribute('role')).toBe('link');
88
+ expect(first.getAttribute('aria-selected')).toBe('true');
89
+ expect(second.getAttribute('aria-selected')).toBe('false');
90
+ host.remove();
91
+ });
92
+ });
93
+
94
+ await describe('<gtk-stack-sidebar> row visibility', async () => {
95
+ await it('a page with a title shows a row', () => {
96
+ const { sidebar, host } = mount([{ name: 'a', title: 'Alpha' }]);
97
+ expect(rows(sidebar)).toStrictEqual(['Alpha selected']);
98
+ host.remove();
99
+ });
100
+
101
+ await it('a page with an icon and NO title gets no row text and is hidden', () => {
102
+ // gtkstacksidebar.c:186 — `visible && title != NULL`. `update_row` never
103
+ // reads icon-name, so there is NO icon fallback here, which is the one rule
104
+ // where this sidebar and the switcher disagree.
105
+ const { sidebar, host } = mount([
106
+ { name: 'a', icon: 'go-next-symbolic' },
107
+ { name: 'b', title: 'Beta' },
108
+ ]);
109
+ // `a` is the first page, so it is the visible one — and its row has no text.
110
+ expect(rows(sidebar)).toStrictEqual(['hidden selected', 'Beta']);
111
+ host.remove();
112
+ });
113
+
114
+ await it('a hidden page is hidden whatever it carries', () => {
115
+ const { sidebar, host } = mount([
116
+ { name: 'a', title: 'Alpha' },
117
+ { name: 'b', title: 'Beta', hidden: true },
118
+ ]);
119
+ expect(rows(sidebar)).toStrictEqual(['Alpha selected', 'Beta hidden']);
120
+ host.remove();
121
+ });
122
+
123
+ await it('needs-attention puts the class on the ROW', () => {
124
+ // gtkstacksidebar.c:188-191 — the class goes on the row, not the label.
125
+ const { sidebar, host } = mount([
126
+ { name: 'a', title: 'Alpha', attention: true },
127
+ { name: 'b', title: 'Beta' },
128
+ ]);
129
+ expect(rows(sidebar)).toStrictEqual(['Alpha selected attention', 'Beta']);
130
+ host.remove();
131
+ });
132
+ });
133
+
134
+ await describe('<gtk-stack-sidebar> the two-way binding', async () => {
135
+ await it('clicking a row makes its page visible', () => {
136
+ const { stack, sidebar, host } = mount(THREE);
137
+ (sidebar.querySelectorAll('.adw-stack-sidebar-row')[2] as HTMLButtonElement).click();
138
+ expect(stack.visibleChildName).toBe('c');
139
+ expect(rows(sidebar)).toStrictEqual(['Alpha', 'Beta', 'Gamma selected']);
140
+ host.remove();
141
+ });
142
+
143
+ await it('a page change re-marks the rows without rebuilding them', () => {
144
+ // gtkstacksidebar.c:317-338 — selection_changed_cb never repopulates.
145
+ const { stack, sidebar, host } = mount(THREE);
146
+ const before = sidebar.querySelectorAll('.adw-stack-sidebar-row')[1];
147
+ stack.setVisibleChildName('c');
148
+ const after = sidebar.querySelectorAll('.adw-stack-sidebar-row')[1];
149
+ expect(after).toBe(before);
150
+ expect(rows(sidebar)).toStrictEqual(['Alpha', 'Beta', 'Gamma selected']);
151
+ host.remove();
152
+ });
153
+
154
+ await it('clicking the CURRENT row leaves it selected', () => {
155
+ // gtkstacksidebar.c:130-143 — only a SELECTION drives the model, so a click on
156
+ // the current row is the same no-op the switcher's is.
157
+ const { stack, sidebar, host } = mount(THREE);
158
+ (sidebar.querySelectorAll('.adw-stack-sidebar-row')[0] as HTMLButtonElement).click();
159
+ expect(stack.visibleChildName).toBe('a');
160
+ expect(rows(sidebar)).toStrictEqual(['Alpha selected', 'Beta', 'Gamma']);
161
+ host.remove();
162
+ });
163
+
164
+ await it('a page added later brings its own row', async () => {
165
+ // items_changed_cb rebuilds the whole list (gtkstacksidebar.c:307-311).
166
+ const { stack, sidebar, host } = mount(THREE);
167
+ const added = document.createElement('div');
168
+ added.setAttribute('name', 'd');
169
+ added.setAttribute('title', 'Delta');
170
+ stack.appendChild(added);
171
+ await settle();
172
+ expect(rows(sidebar)).toStrictEqual(['Alpha selected', 'Beta', 'Gamma', 'Delta']);
173
+ host.remove();
174
+ });
175
+
176
+ await it('a page title change reaches the row', async () => {
177
+ const { sidebar, host, stackId } = mount(THREE);
178
+ const page = document.querySelector(`#${stackId} > div:nth-child(2)`);
179
+ page?.setAttribute('title', 'Renamed');
180
+ await settle();
181
+ expect(rows(sidebar)).toStrictEqual(['Alpha selected', 'Renamed', 'Gamma']);
182
+ host.remove();
183
+ });
184
+ });
185
+
186
+ await describe('<gtk-stack-sidebar> binding and properties', async () => {
187
+ await it('resolves the stack by id, and an unknown id leaves it unbound', () => {
188
+ const { stack, sidebar, host } = mount(THREE);
189
+ expect(sidebar.stack).toBe(stack);
190
+ sidebar.setAttribute('stack', 'no-such-stack-anywhere');
191
+ expect(sidebar.stack).toBe(null);
192
+ expect(sidebar.querySelectorAll('.adw-stack-sidebar-row').length).toBe(0);
193
+ host.remove();
194
+ });
195
+
196
+ await it('setStack notifies, and re-binding the SAME stack does not', () => {
197
+ // gtkstacksidebar.c:422-433 — the early return before the notify.
198
+ const { stack, sidebar, host } = mount(THREE);
199
+ const events: unknown[] = [];
200
+ sidebar.addEventListener('notify::stack', (e) => events.push((e as CustomEvent).detail));
201
+ sidebar.setStack(stack);
202
+ sidebar.setStack(null);
203
+ expect(events).toStrictEqual([{ stack: null }]);
204
+ host.remove();
205
+ });
206
+
207
+ await it('an unbound sidebar is empty, not broken', () => {
208
+ const host = document.createElement('div');
209
+ document.body.appendChild(host);
210
+ const sidebar = document.createElement('gtk-stack-sidebar') as GtkStackSidebar;
211
+ host.appendChild(sidebar);
212
+ expect(sidebar.stack).toBe(null);
213
+ expect(sidebar.querySelectorAll('.adw-stack-sidebar-row').length).toBe(0);
214
+ host.remove();
215
+ });
216
+ });
217
+ };
@@ -0,0 +1,249 @@
1
+ // DOM-level tests for <gtk-stack-switcher>. The widget is three separable things:
2
+ // the BUTTON-VISIBILITY rule (which decides how many buttons exist at all), the
3
+ // TWO-WAY binding with the stack (including C's refusal to let a page be deselected),
4
+ // and the page-update path.
5
+ import { describe, expect, it } from '@gjsify/unit';
6
+
7
+ import type { GtkStack } from './elements/gtk-stack.js';
8
+ import type { GtkStackSwitcher } from './elements/gtk-stack-switcher.js';
9
+
10
+ interface Page {
11
+ name: string;
12
+ title?: string;
13
+ icon?: string;
14
+ hidden?: boolean;
15
+ attention?: boolean;
16
+ }
17
+
18
+ /** A stack with its pages, a switcher bound to it by id, both in the document. */
19
+ function mount(
20
+ pages: Page[],
21
+ attrs: Record<string, string> = {},
22
+ ): { stack: GtkStack; switcher: GtkStackSwitcher; host: HTMLElement; stackId: string } {
23
+ const host = document.createElement('div');
24
+ document.body.appendChild(host);
25
+ // A UNIQUE id per mount: the switcher binds by id, so two stacks left in the document
26
+ // under one id make `getElementById` answer with whichever came first. A counter in
27
+ // this module is not enough, because the suite's cases are free to interleave.
28
+ const stackId = `stack-under-test-${crypto.randomUUID()}`;
29
+ const stack = document.createElement('gtk-stack') as GtkStack;
30
+ stack.id = stackId;
31
+ for (const page of pages) {
32
+ const child = document.createElement('div');
33
+ child.setAttribute('name', page.name);
34
+ if (page.title !== undefined) child.setAttribute('title', page.title);
35
+ if (page.icon !== undefined) child.setAttribute('icon-name', page.icon);
36
+ if (page.hidden) child.setAttribute('hidden', '');
37
+ if (page.attention) child.setAttribute('needs-attention', '');
38
+ stack.appendChild(child);
39
+ }
40
+ const switcher = document.createElement('gtk-stack-switcher') as GtkStackSwitcher;
41
+ for (const [name, value] of Object.entries(attrs)) switcher.setAttribute(name, value);
42
+ switcher.setAttribute('stack', stackId);
43
+ host.append(stack, switcher);
44
+ return { stack, switcher, host, stackId };
45
+ }
46
+
47
+ /** The buttons, with hidden ones marked, as the visibility rule leaves them. */
48
+ function buttons(el: GtkStackSwitcher): string[] {
49
+ return [...el.querySelectorAll<HTMLButtonElement>('.adw-stack-switcher-button')].map((button) => {
50
+ const title = button.querySelector('.adw-stack-switcher-title')?.textContent ?? '';
51
+ const icon = button.querySelector('.adw-stack-switcher-icon')?.getAttribute('icon-name');
52
+ const parts = [title];
53
+ if (icon) parts.push(icon);
54
+ if (button.hidden) parts.push('hidden');
55
+ if (button.classList.contains('active')) parts.push('active');
56
+ if (button.classList.contains('needs-attention')) parts.push('attention');
57
+ return parts.filter(Boolean).join(' ');
58
+ });
59
+ }
60
+
61
+ /** A turn of the event loop — a page's change reaches the switcher through the stack's observer. */
62
+ function settle(): Promise<void> {
63
+ return new Promise((resolve) => setTimeout(resolve, 0));
64
+ }
65
+
66
+ const THREE = [
67
+ { name: 'a', title: 'Alpha' },
68
+ { name: 'b', title: 'Beta' },
69
+ { name: 'c', title: 'Gamma' },
70
+ ];
71
+
72
+ export const GtkStackSwitcherTest = async () => {
73
+ await describe('<gtk-stack-switcher> defaults', async () => {
74
+ await it('builds one button per page, the first one active', () => {
75
+ const { switcher, host } = mount(THREE);
76
+ expect(buttons(switcher)).toStrictEqual(['Alpha active', 'Beta', 'Gamma']);
77
+ expect(switcher.getAttribute('role')).toBe('tablist');
78
+ // `.linked` is what C puts on the switcher itself (gtkstackswitcher.c:49-57).
79
+ expect(switcher.classList.contains('linked')).toBe(true);
80
+ expect(switcher.orientation).toBe('horizontal');
81
+ host.remove();
82
+ });
83
+
84
+ await it('a button is a tab, selected state and all', () => {
85
+ const { switcher, host } = mount(THREE);
86
+ const [first, second] = switcher.querySelectorAll<HTMLButtonElement>('.adw-stack-switcher-button');
87
+ expect(first.getAttribute('role')).toBe('tab');
88
+ expect(first.getAttribute('aria-selected')).toBe('true');
89
+ expect(second.getAttribute('aria-selected')).toBe('false');
90
+ host.remove();
91
+ });
92
+ });
93
+
94
+ await describe('<gtk-stack-switcher> button visibility', async () => {
95
+ await it('a page with a title shows a label button', () => {
96
+ const { switcher, host } = mount([{ name: 'a', title: 'Alpha' }]);
97
+ const label = switcher.querySelector<HTMLElement>('.adw-stack-switcher-title');
98
+ const icon = switcher.querySelector<HTMLElement>('.adw-stack-switcher-icon');
99
+ expect(label?.textContent).toBe('Alpha');
100
+ expect(label?.hidden).toBe(false);
101
+ expect(icon?.hidden).toBe(true);
102
+ expect((switcher.querySelector('.adw-stack-switcher-button') as HTMLElement).dataset.buttonMode).toBe(
103
+ 'text',
104
+ );
105
+ host.remove();
106
+ });
107
+
108
+ await it('an icon AND a title show both, and the tooltip lands on the switcher', () => {
109
+ // gtkstackswitcher.c:145-155 — the tooltip is set on `self`, not on the button.
110
+ const { switcher, host } = mount([{ name: 'a', title: 'Alpha', icon: 'go-next-symbolic' }]);
111
+ const icon = switcher.querySelector<HTMLElement>('.adw-stack-switcher-icon');
112
+ expect(icon?.hidden).toBe(false);
113
+ expect(icon?.getAttribute('icon-name')).toBe('go-next-symbolic');
114
+ expect(switcher.title).toBe('Alpha');
115
+ expect((switcher.querySelector('.adw-stack-switcher-button') as HTMLElement).dataset.buttonMode).toBe(
116
+ 'both',
117
+ );
118
+ host.remove();
119
+ });
120
+
121
+ await it('an icon WITHOUT a title takes the switcher tooltip and clears the label', () => {
122
+ // gtkstackswitcher.c:146-147 sets the tooltip; :158-160 leaves no label child.
123
+ const { switcher, host } = mount([{ name: 'a', icon: 'go-next-symbolic' }]);
124
+ expect(switcher.querySelector<HTMLElement>('.adw-stack-switcher-title')?.hidden).toBe(true);
125
+ expect(switcher.title).toBe('');
126
+ host.remove();
127
+ });
128
+
129
+ await it('a page with NEITHER title nor icon is hidden', () => {
130
+ // gtkstackswitcher.c:200 — `visible && (title != NULL || icon_name != NULL)`.
131
+ const { switcher, host } = mount([{ name: 'a' }, { name: 'b', title: 'Beta' }]);
132
+ // `a` is the FIRST page, so it is the visible one — and a visible page with
133
+ // nothing to draw is still a hidden button, selected.
134
+ expect(buttons(switcher)).toStrictEqual(['hidden active', 'Beta']);
135
+ host.remove();
136
+ });
137
+
138
+ await it('a hidden page is hidden, whatever it carries', () => {
139
+ const { switcher, host } = mount([
140
+ { name: 'a', title: 'Alpha' },
141
+ { name: 'b', title: 'Beta', hidden: true },
142
+ ]);
143
+ expect(buttons(switcher)).toStrictEqual(['Alpha active', 'Beta hidden']);
144
+ host.remove();
145
+ });
146
+
147
+ await it('a title-only button CLEARS a tooltip an icon-only page set', () => {
148
+ // gtkstackswitcher.c:168 — `gtk_widget_set_tooltip_text (self, NULL)`.
149
+ const { switcher, host } = mount([
150
+ { name: 'a', icon: 'go-next-symbolic' },
151
+ { name: 'b', title: 'Beta' },
152
+ ]);
153
+ // The LAST page wins, and it has a title, so the tooltip is cleared.
154
+ expect(switcher.title).toBe('');
155
+ host.remove();
156
+ });
157
+ });
158
+
159
+ await describe('<gtk-stack-switcher> the two-way binding', async () => {
160
+ await it('clicking a button makes its page visible', () => {
161
+ const { stack, switcher, host } = mount(THREE);
162
+ (switcher.querySelectorAll('.adw-stack-switcher-button')[2] as HTMLButtonElement).click();
163
+ expect(stack.visibleChildName).toBe('c');
164
+ expect(buttons(switcher)).toStrictEqual(['Alpha', 'Beta', 'Gamma active']);
165
+ host.remove();
166
+ });
167
+
168
+ await it('a page change re-marks the buttons without rebuilding them', () => {
169
+ // gtkstackswitcher.c:359-381 — only the buttons in the changed range move.
170
+ const { stack, switcher, host } = mount(THREE);
171
+ const before = switcher.querySelectorAll('.adw-stack-switcher-button')[1];
172
+ stack.setVisibleChildName('c');
173
+ const after = switcher.querySelectorAll('.adw-stack-switcher-button')[1];
174
+ expect(after).toBe(before);
175
+ expect(buttons(switcher)).toStrictEqual(['Alpha', 'Beta', 'Gamma active']);
176
+ host.remove();
177
+ });
178
+
179
+ await it('clicking the CURRENT page leaves it selected', () => {
180
+ // gtkstackswitcher.c:126-131 — a deactivation is taken back from the model.
181
+ const { stack, switcher, host } = mount(THREE);
182
+ (switcher.querySelectorAll('.adw-stack-switcher-button')[0] as HTMLButtonElement).click();
183
+ expect(stack.visibleChildName).toBe('a');
184
+ expect(buttons(switcher)).toStrictEqual(['Alpha active', 'Beta', 'Gamma']);
185
+ host.remove();
186
+ });
187
+
188
+ await it('needs-attention puts the class on the button', () => {
189
+ const { switcher, host } = mount([
190
+ { name: 'a', title: 'Alpha', attention: true },
191
+ { name: 'b', title: 'Beta' },
192
+ ]);
193
+ expect(buttons(switcher)).toStrictEqual(['Alpha active attention', 'Beta']);
194
+ host.remove();
195
+ });
196
+
197
+ await it('a page added later brings its own button', async () => {
198
+ // `items_changed_cb` rebuilds the whole row (gtkstackswitcher.c:347-355).
199
+ const { stack, switcher, host } = mount(THREE);
200
+ const added = document.createElement('div');
201
+ added.setAttribute('name', 'd');
202
+ added.setAttribute('title', 'Delta');
203
+ stack.appendChild(added);
204
+ await settle();
205
+ expect(buttons(switcher)).toStrictEqual(['Alpha active', 'Beta', 'Gamma', 'Delta']);
206
+ host.remove();
207
+ });
208
+ });
209
+
210
+ await describe('<gtk-stack-switcher> binding and properties', async () => {
211
+ await it('resolves the stack by id, and an unknown id leaves it unbound', () => {
212
+ const { stack, switcher, host } = mount(THREE);
213
+ expect(switcher.stack).toBe(stack);
214
+ switcher.setAttribute('stack', 'no-such-stack-anywhere');
215
+ expect(switcher.stack).toBe(null);
216
+ expect(switcher.querySelectorAll('.adw-stack-switcher-button').length).toBe(0);
217
+ host.remove();
218
+ });
219
+
220
+ await it('setStack notifies, and re-binding the SAME stack does not', () => {
221
+ // gtkstackswitcher.c:430-432 — the early return before the notify.
222
+ const { stack, switcher, host } = mount(THREE);
223
+ const events: unknown[] = [];
224
+ switcher.addEventListener('notify::stack', (e) => events.push((e as CustomEvent).detail));
225
+ switcher.setStack(stack);
226
+ switcher.setStack(null);
227
+ expect(events).toStrictEqual([{ stack: null }]);
228
+ host.remove();
229
+ });
230
+
231
+ await it('orientation is a property and the switcher keeps its horizontal default', () => {
232
+ const { switcher, host } = mount(THREE, { orientation: 'vertical' });
233
+ expect(switcher.orientation).toBe('vertical');
234
+ switcher.orientation = 'horizontal';
235
+ expect(switcher.orientation).toBe('horizontal');
236
+ host.remove();
237
+ });
238
+
239
+ await it('an unbound switcher is empty, not broken', () => {
240
+ const host = document.createElement('div');
241
+ document.body.appendChild(host);
242
+ const switcher = document.createElement('gtk-stack-switcher') as GtkStackSwitcher;
243
+ host.appendChild(switcher);
244
+ expect(switcher.stack).toBe(null);
245
+ expect(switcher.querySelectorAll('.adw-stack-switcher-button').length).toBe(0);
246
+ host.remove();
247
+ });
248
+ });
249
+ };