@gjsify/adwaita-web 0.54.0 → 0.56.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 (479) hide show
  1. package/README.md +8 -0
  2. package/dist/adwaita-web.css +8146 -3011
  3. package/dist/adwaita-web.css.map +1 -1
  4. package/lib/types/adw-application-window.spec.d.ts +1 -0
  5. package/lib/types/adw-bin.spec.d.ts +1 -0
  6. package/lib/types/adw-breakpoint-bin.spec.d.ts +1 -0
  7. package/lib/types/adw-clamp-scrollable.spec.d.ts +1 -0
  8. package/lib/types/adw-multi-layout-view.spec.d.ts +1 -0
  9. package/lib/types/adw-preferences-row.spec.d.ts +1 -0
  10. package/lib/types/adw-shortcuts.spec.d.ts +1 -0
  11. package/lib/types/adw-status-page.spec.d.ts +1 -0
  12. package/lib/types/adw-tab-bar.spec.d.ts +1 -0
  13. package/lib/types/adw-tab-button.spec.d.ts +1 -0
  14. package/lib/types/adw-tab-overview.spec.d.ts +1 -0
  15. package/lib/types/adw-view-switcher-sidebar.spec.d.ts +1 -0
  16. package/lib/types/adw-window.spec.d.ts +1 -0
  17. package/lib/types/attributes.d.ts +19 -0
  18. package/lib/types/breakpoints.d.ts +14 -3
  19. package/lib/types/construct-vectors.spec.d.ts +1 -0
  20. package/lib/types/elements/adw-action-row.d.ts +4 -0
  21. package/lib/types/elements/adw-application-window.d.ts +27 -0
  22. package/lib/types/elements/adw-bin.d.ts +17 -0
  23. package/lib/types/elements/adw-breakpoint-bin.d.ts +67 -0
  24. package/lib/types/elements/adw-clamp-scrollable.d.ts +38 -0
  25. package/lib/types/elements/adw-combo-row.d.ts +4 -0
  26. package/lib/types/elements/adw-dialog.d.ts +10 -0
  27. package/lib/types/elements/adw-entry-row.d.ts +6 -0
  28. package/lib/types/elements/adw-expander-row.d.ts +2 -0
  29. package/lib/types/elements/adw-header-bar.d.ts +6 -0
  30. package/lib/types/elements/adw-layout-slot.d.ts +19 -0
  31. package/lib/types/elements/adw-multi-layout-view.d.ts +101 -0
  32. package/lib/types/elements/adw-overlay-split-view.d.ts +13 -0
  33. package/lib/types/elements/adw-preferences-row.d.ts +31 -0
  34. package/lib/types/elements/adw-shortcuts-dialog.d.ts +13 -0
  35. package/lib/types/elements/adw-shortcuts-item.d.ts +36 -0
  36. package/lib/types/elements/adw-shortcuts-section.d.ts +14 -0
  37. package/lib/types/elements/adw-sidebar.d.ts +8 -2
  38. package/lib/types/elements/adw-spin-row.d.ts +6 -0
  39. package/lib/types/elements/adw-switch-row.d.ts +7 -0
  40. package/lib/types/elements/adw-tab-bar.d.ts +108 -0
  41. package/lib/types/elements/adw-tab-button.d.ts +32 -0
  42. package/lib/types/elements/adw-tab-overview.d.ts +199 -0
  43. package/lib/types/elements/adw-tab-view.d.ts +19 -23
  44. package/lib/types/elements/adw-toggle-group.d.ts +17 -0
  45. package/lib/types/elements/adw-view-switcher-sidebar.d.ts +84 -0
  46. package/lib/types/elements/adw-window.d.ts +40 -0
  47. package/lib/types/elements/gtk-about-dialog.d.ts +146 -0
  48. package/lib/types/elements/gtk-application-window.d.ts +34 -0
  49. package/lib/types/elements/gtk-aspect-frame.d.ts +27 -0
  50. package/lib/types/elements/gtk-button.d.ts +9 -0
  51. package/lib/types/elements/gtk-calendar.d.ts +159 -0
  52. package/lib/types/elements/gtk-center-box.d.ts +39 -0
  53. package/lib/types/elements/gtk-color-dialog-button.d.ts +69 -0
  54. package/lib/types/elements/gtk-column-view.d.ts +50 -0
  55. package/lib/types/elements/gtk-drag-icon.d.ts +78 -0
  56. package/lib/types/elements/gtk-drawing-area.d.ts +95 -0
  57. package/lib/types/elements/gtk-editable-label.d.ts +33 -0
  58. package/lib/types/elements/gtk-emoji-chooser.d.ts +84 -0
  59. package/lib/types/elements/gtk-entry.d.ts +20 -1
  60. package/lib/types/elements/gtk-expander.d.ts +49 -0
  61. package/lib/types/elements/gtk-fixed.d.ts +13 -0
  62. package/lib/types/elements/gtk-flow-box-child.d.ts +14 -0
  63. package/lib/types/elements/gtk-flow-box.d.ts +87 -0
  64. package/lib/types/elements/gtk-font-dialog-button.d.ts +83 -0
  65. package/lib/types/elements/gtk-frame.d.ts +24 -0
  66. package/lib/types/elements/gtk-gl-area.d.ts +136 -0
  67. package/lib/types/elements/gtk-graphics-offload.d.ts +24 -0
  68. package/lib/types/elements/gtk-grid-view.d.ts +39 -0
  69. package/lib/types/elements/gtk-grid.d.ts +27 -0
  70. package/lib/types/elements/gtk-header-bar.d.ts +60 -0
  71. package/lib/types/elements/gtk-inscription.d.ts +64 -0
  72. package/lib/types/elements/gtk-label.d.ts +3 -0
  73. package/lib/types/elements/gtk-level-bar.d.ts +59 -0
  74. package/lib/types/elements/gtk-link-button.d.ts +29 -0
  75. package/lib/types/elements/gtk-list-box-row.d.ts +54 -0
  76. package/lib/types/elements/gtk-list-box.d.ts +100 -0
  77. package/lib/types/elements/gtk-list-view.d.ts +73 -0
  78. package/lib/types/elements/gtk-media-controls.d.ts +142 -0
  79. package/lib/types/elements/gtk-notebook.d.ts +148 -0
  80. package/lib/types/elements/gtk-overlay.d.ts +22 -0
  81. package/lib/types/elements/gtk-page-setup-unix-dialog.d.ts +87 -0
  82. package/lib/types/elements/gtk-paned.d.ts +121 -0
  83. package/lib/types/elements/gtk-password-entry.d.ts +27 -0
  84. package/lib/types/elements/gtk-picture.d.ts +66 -0
  85. package/lib/types/elements/gtk-popover-bin.d.ts +82 -0
  86. package/lib/types/elements/gtk-popover-menu-bar.d.ts +66 -0
  87. package/lib/types/elements/gtk-popover-menu.d.ts +83 -0
  88. package/lib/types/elements/gtk-popover.d.ts +28 -3
  89. package/lib/types/elements/gtk-print-unix-dialog.d.ts +167 -0
  90. package/lib/types/elements/gtk-revealer.d.ts +55 -0
  91. package/lib/types/elements/gtk-scale-button.d.ts +59 -0
  92. package/lib/types/elements/gtk-scale.d.ts +98 -0
  93. package/lib/types/elements/gtk-scrollbar.d.ts +124 -0
  94. package/lib/types/elements/gtk-scrolled-window.d.ts +129 -0
  95. package/lib/types/elements/gtk-search-bar.d.ts +39 -0
  96. package/lib/types/elements/gtk-search-entry.d.ts +27 -0
  97. package/lib/types/elements/gtk-separator.d.ts +10 -0
  98. package/lib/types/elements/gtk-spin-button.d.ts +94 -0
  99. package/lib/types/elements/gtk-spinner.d.ts +14 -0
  100. package/lib/types/elements/gtk-stack-sidebar.d.ts +43 -0
  101. package/lib/types/elements/gtk-stack-switcher.d.ts +58 -0
  102. package/lib/types/elements/gtk-stack.d.ts +154 -0
  103. package/lib/types/elements/gtk-switch.d.ts +4 -1
  104. package/lib/types/elements/gtk-text-view.d.ts +62 -0
  105. package/lib/types/elements/gtk-text.d.ts +45 -0
  106. package/lib/types/elements/gtk-toggle-button.d.ts +45 -0
  107. package/lib/types/elements/gtk-tree-expander.d.ts +38 -0
  108. package/lib/types/elements/gtk-video.d.ts +97 -0
  109. package/lib/types/elements/gtk-viewport.d.ts +84 -0
  110. package/lib/types/elements/gtk-window-controls.d.ts +80 -0
  111. package/lib/types/elements/gtk-window-handle.d.ts +39 -0
  112. package/lib/types/elements/gtk-window.d.ts +103 -0
  113. package/lib/types/elements/justification.d.ts +2 -0
  114. package/lib/types/elements/popover-menu.d.ts +18 -0
  115. package/lib/types/elements/roving-focus.d.ts +29 -1
  116. package/lib/types/emoji-data.d.ts +38 -0
  117. package/lib/types/font-description.d.ts +96 -0
  118. package/lib/types/gtk-about-dialog.spec.d.ts +1 -0
  119. package/lib/types/gtk-action-bar.spec.d.ts +1 -0
  120. package/lib/types/gtk-application-window.spec.d.ts +1 -0
  121. package/lib/types/gtk-aspect-frame.spec.d.ts +1 -0
  122. package/lib/types/gtk-calendar.spec.d.ts +1 -0
  123. package/lib/types/gtk-center-box.spec.d.ts +1 -0
  124. package/lib/types/gtk-color-dialog-button.spec.d.ts +1 -0
  125. package/lib/types/gtk-column-view.spec.d.ts +1 -0
  126. package/lib/types/gtk-drag-icon.spec.d.ts +1 -0
  127. package/lib/types/gtk-drawing-area.spec.d.ts +1 -0
  128. package/lib/types/gtk-editable-label.spec.d.ts +1 -0
  129. package/lib/types/gtk-emoji-chooser.spec.d.ts +1 -0
  130. package/lib/types/gtk-expander.spec.d.ts +1 -0
  131. package/lib/types/gtk-fixed.spec.d.ts +1 -0
  132. package/lib/types/gtk-flow-box.spec.d.ts +1 -0
  133. package/lib/types/gtk-font-dialog-button.spec.d.ts +1 -0
  134. package/lib/types/gtk-frame.spec.d.ts +1 -0
  135. package/lib/types/gtk-gl-area.spec.d.ts +1 -0
  136. package/lib/types/gtk-graphics-offload.spec.d.ts +1 -0
  137. package/lib/types/gtk-grid-view.spec.d.ts +1 -0
  138. package/lib/types/gtk-grid.spec.d.ts +1 -0
  139. package/lib/types/gtk-header-bar.spec.d.ts +1 -0
  140. package/lib/types/gtk-inscription.spec.d.ts +1 -0
  141. package/lib/types/gtk-level-bar.spec.d.ts +1 -0
  142. package/lib/types/gtk-link-button.spec.d.ts +1 -0
  143. package/lib/types/gtk-list-box.spec.d.ts +1 -0
  144. package/lib/types/gtk-list-view.spec.d.ts +1 -0
  145. package/lib/types/gtk-media-controls.spec.d.ts +1 -0
  146. package/lib/types/gtk-notebook.spec.d.ts +1 -0
  147. package/lib/types/gtk-overlay.spec.d.ts +1 -0
  148. package/lib/types/gtk-paned.spec.d.ts +1 -0
  149. package/lib/types/gtk-password-entry.spec.d.ts +1 -0
  150. package/lib/types/gtk-picture.spec.d.ts +1 -0
  151. package/lib/types/gtk-popover-bin.spec.d.ts +1 -0
  152. package/lib/types/gtk-popover-menu-bar.spec.d.ts +1 -0
  153. package/lib/types/gtk-popover-menu.spec.d.ts +1 -0
  154. package/lib/types/gtk-popover-role.spec.d.ts +1 -0
  155. package/lib/types/gtk-popover.spec.d.ts +1 -0
  156. package/lib/types/gtk-print-dialogs.spec.d.ts +1 -0
  157. package/lib/types/gtk-revealer.spec.d.ts +1 -0
  158. package/lib/types/gtk-scale-button.spec.d.ts +1 -0
  159. package/lib/types/gtk-scale.spec.d.ts +1 -0
  160. package/lib/types/gtk-scrollbar.spec.d.ts +1 -0
  161. package/lib/types/gtk-scrolled-window.spec.d.ts +1 -0
  162. package/lib/types/gtk-search-bar.spec.d.ts +1 -0
  163. package/lib/types/gtk-search-entry.spec.d.ts +1 -0
  164. package/lib/types/gtk-separator.spec.d.ts +1 -0
  165. package/lib/types/gtk-spin-button.spec.d.ts +1 -0
  166. package/lib/types/gtk-spinner.spec.d.ts +1 -0
  167. package/lib/types/gtk-stack-sidebar.spec.d.ts +1 -0
  168. package/lib/types/gtk-stack-switcher.spec.d.ts +1 -0
  169. package/lib/types/gtk-stack.spec.d.ts +1 -0
  170. package/lib/types/gtk-text-view.spec.d.ts +1 -0
  171. package/lib/types/gtk-text.spec.d.ts +1 -0
  172. package/lib/types/gtk-toggle-button.spec.d.ts +1 -0
  173. package/lib/types/gtk-tree-expander.spec.d.ts +1 -0
  174. package/lib/types/gtk-video.spec.d.ts +1 -0
  175. package/lib/types/gtk-viewport.spec.d.ts +1 -0
  176. package/lib/types/gtk-window-controls.spec.d.ts +1 -0
  177. package/lib/types/gtk-window-handle.spec.d.ts +1 -0
  178. package/lib/types/gtk-window.spec.d.ts +1 -0
  179. package/lib/types/index.d.ts +4 -1
  180. package/lib/types/list-view-dom.d.ts +44 -0
  181. package/lib/types/namespace/adw.d.ts +12 -0
  182. package/lib/types/namespace/gtk.d.ts +61 -0
  183. package/lib/types/page-setup.d.ts +100 -0
  184. package/lib/types/row-line-clamp.d.ts +20 -0
  185. package/lib/types/row-tooltip.d.ts +8 -0
  186. package/lib/types/row-tooltip.spec.d.ts +1 -0
  187. package/lib/types/scroll-shading.d.ts +15 -1
  188. package/lib/types/shared-tree-builder.d.ts +75 -17
  189. package/lib/types/shared-tree-mount.spec.d.ts +1 -0
  190. package/lib/types/signals.d.ts +4 -0
  191. package/lib/types/styles.generated.d.ts +1 -1
  192. package/lib/types/tab-chip.d.ts +73 -0
  193. package/lib/types/template-classes.d.ts +11 -0
  194. package/lib/types/widget-size.spec.d.ts +1 -0
  195. package/package.json +12 -8
  196. package/scss/_action_row.scss +41 -17
  197. package/scss/_application_window.scss +26 -0
  198. package/scss/_aspect_frame.scss +25 -0
  199. package/scss/_bin.scss +26 -0
  200. package/scss/_button.scss +45 -0
  201. package/scss/_button_row.scss +5 -0
  202. package/scss/_calendar.scss +176 -0
  203. package/scss/_center_box.scss +25 -0
  204. package/scss/_checks.scss +6 -1
  205. package/scss/_clamp_scrollable.scss +28 -0
  206. package/scss/_color_button.scss +67 -0
  207. package/scss/_column_view.scss +126 -0
  208. package/scss/_drag_icon.scss +55 -0
  209. package/scss/_drawing.scss +85 -0
  210. package/scss/_emoji_chooser.scss +213 -0
  211. package/scss/_entry.scss +13 -5
  212. package/scss/_entry_row.scss +1 -1
  213. package/scss/_expander.scss +113 -0
  214. package/scss/_expander_row.scss +36 -3
  215. package/scss/_fixed.scss +29 -0
  216. package/scss/_flow_box.scss +94 -0
  217. package/scss/_font_button.scss +63 -0
  218. package/scss/_frame.scss +41 -0
  219. package/scss/_grid.scss +19 -0
  220. package/scss/_grid_view.scss +46 -0
  221. package/scss/_gtk_about_dialog.scss +186 -0
  222. package/scss/_headerbar.scss +134 -1
  223. package/scss/_icons.generated.scss +186 -0
  224. package/scss/_inscription.scss +125 -0
  225. package/scss/_level_bar.scss +190 -0
  226. package/scss/_link.scss +82 -0
  227. package/scss/_list_box.scss +88 -0
  228. package/scss/_list_view.scss +96 -0
  229. package/scss/_media_controls.scss +139 -0
  230. package/scss/_overlay.scss +68 -0
  231. package/scss/_paned.scss +130 -0
  232. package/scss/_password_entry.scss +83 -0
  233. package/scss/_picture.scss +80 -0
  234. package/scss/_popover_bin.scss +53 -0
  235. package/scss/_popover_menu_bar.scss +104 -0
  236. package/scss/_preferences_row.scss +60 -0
  237. package/scss/_print_dialogs.scss +328 -0
  238. package/scss/_reset.scss +21 -19
  239. package/scss/_revealer.scss +37 -0
  240. package/scss/_row.scss +36 -5
  241. package/scss/_scale.scss +298 -0
  242. package/scss/_scale_button.scss +167 -0
  243. package/scss/_scrollbar.scss +230 -0
  244. package/scss/_scrolled_window.scss +131 -0
  245. package/scss/_search_bar.scss +118 -0
  246. package/scss/_search_entry.scss +86 -0
  247. package/scss/_separator.scss +44 -0
  248. package/scss/_shortcuts_dialog.scss +100 -0
  249. package/scss/_spin_button.scss +172 -0
  250. package/scss/_spin_row.scss +56 -8
  251. package/scss/_spinner.scss +85 -4
  252. package/scss/_stack.scss +729 -0
  253. package/scss/_status_page.scss +1 -1
  254. package/scss/_switch.scss +13 -5
  255. package/scss/_switch_row.scss +10 -0
  256. package/scss/_tab_bar.scss +317 -0
  257. package/scss/_tab_button.scss +98 -0
  258. package/scss/_tab_overview.scss +483 -0
  259. package/scss/_text.scss +182 -0
  260. package/scss/_theme.scss +1 -1
  261. package/scss/_toggle_group.scss +28 -0
  262. package/scss/_tree_expander.scss +62 -0
  263. package/scss/_variables.scss +1 -1
  264. package/scss/_video.scss +146 -0
  265. package/scss/_view_switcher_sidebar.scss +112 -0
  266. package/scss/_viewport.scss +50 -0
  267. package/scss/_widget.scss +14 -3
  268. package/scss/_window.scss +131 -2
  269. package/scss/_window_handle.scss +20 -0
  270. package/scss/adwaita-skin.scss +109 -0
  271. package/src/adw-action-rows.spec.ts +331 -0
  272. package/src/adw-application-window.spec.ts +118 -0
  273. package/src/adw-bin.spec.ts +75 -0
  274. package/src/adw-breakpoint-bin.spec.ts +324 -0
  275. package/src/adw-clamp-scrollable.spec.ts +189 -0
  276. package/src/adw-header-bar.spec.ts +18 -0
  277. package/src/adw-multi-layout-view.spec.ts +243 -0
  278. package/src/adw-preferences-row.spec.ts +164 -0
  279. package/src/adw-row-state.spec.ts +142 -0
  280. package/src/adw-shortcuts.spec.ts +155 -0
  281. package/src/adw-status-page.spec.ts +124 -0
  282. package/src/adw-tab-bar.spec.ts +287 -0
  283. package/src/adw-tab-button.spec.ts +101 -0
  284. package/src/adw-tab-overview.spec.ts +443 -0
  285. package/src/adw-view-switcher-sidebar.spec.ts +281 -0
  286. package/src/adw-window.spec.ts +129 -0
  287. package/src/attributes.ts +52 -0
  288. package/src/breakpoints.ts +25 -8
  289. package/src/capabilities.d.mts +3 -0
  290. package/src/capabilities.mjs +21 -0
  291. package/src/connect-lifecycle.spec.ts +12 -0
  292. package/src/construct-vectors.spec.ts +175 -0
  293. package/src/elements/adw-action-row.ts +31 -2
  294. package/src/elements/adw-alert-dialog.ts +4 -1
  295. package/src/elements/adw-application-window.ts +154 -0
  296. package/src/elements/adw-bin.ts +101 -0
  297. package/src/elements/adw-breakpoint-bin.ts +287 -0
  298. package/src/elements/adw-button-row.ts +7 -1
  299. package/src/elements/adw-clamp-scrollable.ts +207 -0
  300. package/src/elements/adw-combo-row.ts +26 -1
  301. package/src/elements/adw-dialog.ts +19 -0
  302. package/src/elements/adw-entry-row.ts +22 -12
  303. package/src/elements/adw-expander-row.ts +34 -1
  304. package/src/elements/adw-header-bar.ts +18 -4
  305. package/src/elements/adw-layout-slot.ts +95 -0
  306. package/src/elements/adw-multi-layout-view.ts +326 -0
  307. package/src/elements/adw-overlay-split-view.ts +29 -6
  308. package/src/elements/adw-preferences-row.ts +153 -0
  309. package/src/elements/adw-shortcuts-dialog.ts +52 -0
  310. package/src/elements/adw-shortcuts-item.ts +171 -0
  311. package/src/elements/adw-shortcuts-section.ts +86 -0
  312. package/src/elements/adw-sidebar.ts +74 -7
  313. package/src/elements/adw-spin-row.ts +37 -2
  314. package/src/elements/adw-status-page.ts +10 -2
  315. package/src/elements/adw-switch-row.ts +53 -2
  316. package/src/elements/adw-tab-bar.ts +529 -0
  317. package/src/elements/adw-tab-button.ts +194 -0
  318. package/src/elements/adw-tab-overview.ts +1011 -0
  319. package/src/elements/adw-tab-view.ts +66 -145
  320. package/src/elements/adw-toggle-group.ts +138 -14
  321. package/src/elements/adw-view-stack.ts +18 -0
  322. package/src/elements/adw-view-switcher-sidebar.ts +332 -0
  323. package/src/elements/adw-window.ts +190 -11
  324. package/src/elements/checks.ts +35 -8
  325. package/src/elements/gtk-about-dialog.ts +811 -0
  326. package/src/elements/gtk-application-window.ts +108 -0
  327. package/src/elements/gtk-aspect-frame.ts +160 -0
  328. package/src/elements/gtk-button.ts +12 -0
  329. package/src/elements/gtk-calendar.ts +757 -0
  330. package/src/elements/gtk-center-box.ts +184 -0
  331. package/src/elements/gtk-color-dialog-button.ts +278 -0
  332. package/src/elements/gtk-column-view.ts +320 -0
  333. package/src/elements/gtk-drag-icon.ts +257 -0
  334. package/src/elements/gtk-drawing-area.ts +303 -0
  335. package/src/elements/gtk-editable-label.ts +204 -0
  336. package/src/elements/gtk-emoji-chooser.ts +542 -0
  337. package/src/elements/gtk-entry.ts +39 -5
  338. package/src/elements/gtk-expander.ts +301 -0
  339. package/src/elements/gtk-fixed.ts +99 -0
  340. package/src/elements/gtk-flow-box-child.ts +68 -0
  341. package/src/elements/gtk-flow-box.ts +480 -0
  342. package/src/elements/gtk-font-dialog-button.ts +432 -0
  343. package/src/elements/gtk-frame.ts +174 -0
  344. package/src/elements/gtk-gl-area.ts +476 -0
  345. package/src/elements/gtk-graphics-offload.ts +130 -0
  346. package/src/elements/gtk-grid-view.ts +168 -0
  347. package/src/elements/gtk-grid.ts +127 -0
  348. package/src/elements/gtk-header-bar.ts +273 -0
  349. package/src/elements/gtk-inscription.ts +387 -0
  350. package/src/elements/gtk-label.ts +19 -11
  351. package/src/elements/gtk-level-bar.ts +323 -0
  352. package/src/elements/gtk-link-button.ts +138 -0
  353. package/src/elements/gtk-list-box-row.ts +195 -0
  354. package/src/elements/gtk-list-box.ts +413 -0
  355. package/src/elements/gtk-list-view.ts +370 -0
  356. package/src/elements/gtk-media-controls.ts +509 -0
  357. package/src/elements/gtk-notebook.ts +664 -0
  358. package/src/elements/gtk-overlay.ts +149 -0
  359. package/src/elements/gtk-page-setup-unix-dialog.ts +420 -0
  360. package/src/elements/gtk-paned.ts +597 -0
  361. package/src/elements/gtk-password-entry.ts +170 -0
  362. package/src/elements/gtk-picture.ts +254 -0
  363. package/src/elements/gtk-popover-bin.ts +332 -0
  364. package/src/elements/gtk-popover-menu-bar.ts +355 -0
  365. package/src/elements/gtk-popover-menu.ts +339 -0
  366. package/src/elements/gtk-popover.ts +92 -10
  367. package/src/elements/gtk-print-unix-dialog.ts +989 -0
  368. package/src/elements/gtk-progress-bar.ts +24 -11
  369. package/src/elements/gtk-revealer.ts +406 -0
  370. package/src/elements/gtk-scale-button.ts +358 -0
  371. package/src/elements/gtk-scale.ts +492 -0
  372. package/src/elements/gtk-scrollbar.ts +528 -0
  373. package/src/elements/gtk-scrolled-window.ts +653 -0
  374. package/src/elements/gtk-search-bar.ts +200 -0
  375. package/src/elements/gtk-search-entry.ts +172 -0
  376. package/src/elements/gtk-separator.ts +52 -0
  377. package/src/elements/gtk-spin-button.ts +475 -0
  378. package/src/elements/gtk-spinner.ts +126 -0
  379. package/src/elements/gtk-stack-sidebar.ts +212 -0
  380. package/src/elements/gtk-stack-switcher.ts +279 -0
  381. package/src/elements/gtk-stack.ts +748 -0
  382. package/src/elements/gtk-switch.ts +52 -19
  383. package/src/elements/gtk-text-view.ts +290 -0
  384. package/src/elements/gtk-text.ts +219 -0
  385. package/src/elements/gtk-toggle-button.ts +239 -0
  386. package/src/elements/gtk-tree-expander.ts +243 -0
  387. package/src/elements/gtk-video.ts +423 -0
  388. package/src/elements/gtk-viewport.ts +277 -0
  389. package/src/elements/gtk-window-controls.ts +341 -0
  390. package/src/elements/gtk-window-handle.ts +221 -0
  391. package/src/elements/gtk-window.ts +372 -0
  392. package/src/elements/justification.ts +24 -0
  393. package/src/elements/popover-menu.ts +24 -0
  394. package/src/elements/roving-focus.ts +53 -21
  395. package/src/emoji-data.ts +577 -0
  396. package/src/entry-rows.spec.ts +31 -0
  397. package/src/font-description.ts +238 -0
  398. package/src/gtk-about-dialog.spec.ts +250 -0
  399. package/src/gtk-action-bar.spec.ts +89 -0
  400. package/src/gtk-application-window.spec.ts +107 -0
  401. package/src/gtk-aspect-frame.spec.ts +114 -0
  402. package/src/gtk-calendar.spec.ts +589 -0
  403. package/src/gtk-center-box.spec.ts +125 -0
  404. package/src/gtk-color-dialog-button.spec.ts +141 -0
  405. package/src/gtk-column-view.spec.ts +154 -0
  406. package/src/gtk-drag-icon.spec.ts +190 -0
  407. package/src/gtk-drawing-area.spec.ts +211 -0
  408. package/src/gtk-editable-label.spec.ts +190 -0
  409. package/src/gtk-emoji-chooser.spec.ts +173 -0
  410. package/src/gtk-entry.spec.ts +23 -0
  411. package/src/gtk-expander.spec.ts +170 -0
  412. package/src/gtk-fixed.spec.ts +97 -0
  413. package/src/gtk-flow-box.spec.ts +289 -0
  414. package/src/gtk-font-dialog-button.spec.ts +166 -0
  415. package/src/gtk-frame.spec.ts +119 -0
  416. package/src/gtk-gl-area.spec.ts +361 -0
  417. package/src/gtk-graphics-offload.spec.ts +119 -0
  418. package/src/gtk-grid-view.spec.ts +106 -0
  419. package/src/gtk-grid.spec.ts +122 -0
  420. package/src/gtk-header-bar.spec.ts +202 -0
  421. package/src/gtk-inscription.spec.ts +235 -0
  422. package/src/gtk-level-bar.spec.ts +199 -0
  423. package/src/gtk-link-button.spec.ts +117 -0
  424. package/src/gtk-list-box.spec.ts +422 -0
  425. package/src/gtk-list-view.spec.ts +203 -0
  426. package/src/gtk-media-controls.spec.ts +295 -0
  427. package/src/gtk-notebook.spec.ts +546 -0
  428. package/src/gtk-overlay.spec.ts +141 -0
  429. package/src/gtk-paned.spec.ts +267 -0
  430. package/src/gtk-password-entry.spec.ts +150 -0
  431. package/src/gtk-picture.spec.ts +172 -0
  432. package/src/gtk-popover-bin.spec.ts +229 -0
  433. package/src/gtk-popover-menu-bar.spec.ts +296 -0
  434. package/src/gtk-popover-menu.spec.ts +261 -0
  435. package/src/gtk-popover-role.spec.ts +138 -0
  436. package/src/gtk-popover.spec.ts +264 -0
  437. package/src/gtk-print-dialogs.spec.ts +251 -0
  438. package/src/gtk-revealer.spec.ts +164 -0
  439. package/src/gtk-scale-button.spec.ts +218 -0
  440. package/src/gtk-scale.spec.ts +213 -0
  441. package/src/gtk-scrollbar.spec.ts +244 -0
  442. package/src/gtk-scrolled-window.spec.ts +262 -0
  443. package/src/gtk-search-bar.spec.ts +179 -0
  444. package/src/gtk-search-entry.spec.ts +142 -0
  445. package/src/gtk-separator.spec.ts +74 -0
  446. package/src/gtk-spin-button.spec.ts +186 -0
  447. package/src/gtk-spinner.spec.ts +130 -0
  448. package/src/gtk-stack-sidebar.spec.ts +217 -0
  449. package/src/gtk-stack-switcher.spec.ts +249 -0
  450. package/src/gtk-stack.spec.ts +432 -0
  451. package/src/gtk-text-view.spec.ts +189 -0
  452. package/src/gtk-text.spec.ts +163 -0
  453. package/src/gtk-toggle-button.spec.ts +186 -0
  454. package/src/gtk-tree-expander.spec.ts +201 -0
  455. package/src/gtk-video.spec.ts +240 -0
  456. package/src/gtk-viewport.spec.ts +140 -0
  457. package/src/gtk-window-controls.spec.ts +232 -0
  458. package/src/gtk-window-handle.spec.ts +158 -0
  459. package/src/gtk-window.spec.ts +202 -0
  460. package/src/index.ts +9 -1
  461. package/src/keyboard-operable.spec.ts +77 -9
  462. package/src/list-view-dom.ts +121 -0
  463. package/src/namespace/adw.ts +12 -0
  464. package/src/namespace/gtk.ts +61 -0
  465. package/src/page-setup.ts +222 -0
  466. package/src/row-line-clamp.ts +74 -0
  467. package/src/row-tooltip.spec.ts +313 -0
  468. package/src/row-tooltip.ts +52 -0
  469. package/src/scroll-shading.ts +18 -1
  470. package/src/shared-tree-builder.ts +336 -41
  471. package/src/shared-tree-mount.spec.ts +130 -0
  472. package/src/shared-trees.spec.ts +15 -0
  473. package/src/signals.ts +15 -0
  474. package/src/style-classes.spec.ts +9 -8
  475. package/src/styles.generated.ts +1 -1
  476. package/src/tab-chip.ts +200 -0
  477. package/src/template-classes.ts +45 -0
  478. package/src/test.browser.mts +160 -0
  479. package/src/widget-size.spec.ts +117 -0
@@ -0,0 +1,155 @@
1
+ // DOM-level behaviour tests for the shortcuts trio — <adw-shortcuts-dialog> and the
2
+ // two markup-only objects it holds. Runs in a real browser via the @gjsify/adwaita-web
3
+ // browser test axis (tests/browser Playwright harness).
4
+ //
5
+ // The dialog half is <adw-dialog>'s own spec; what is here is the content model and the
6
+ // rules the C states for an item (adw-shortcuts-item.c:19-28).
7
+ import { describe, expect, it } from '@gjsify/unit';
8
+
9
+ import type { AdwShortcutsDialog } from './elements/adw-shortcuts-dialog.js';
10
+ import type { AdwShortcutsSection } from './elements/adw-shortcuts-section.js';
11
+
12
+ type Item = HTMLElement & { hasAccelerator: boolean; subtitle: string; accelerator: string };
13
+
14
+ function item(title: string, accelerator = '', subtitle = ''): Item {
15
+ const el = document.createElement('adw-shortcuts-item') as Item;
16
+ el.setAttribute('title', title);
17
+ if (accelerator) el.setAttribute('accelerator', accelerator);
18
+ if (subtitle) el.setAttribute('subtitle', subtitle);
19
+ return el;
20
+ }
21
+
22
+ function section(title: string | null, ...items: Item[]): AdwShortcutsSection {
23
+ const el = document.createElement('adw-shortcuts-section') as AdwShortcutsSection;
24
+ if (title !== null) el.setAttribute('title', title);
25
+ el.append(...items);
26
+ return el;
27
+ }
28
+
29
+ function dialog(...sections: AdwShortcutsSection[]): AdwShortcutsDialog {
30
+ const el = document.createElement('adw-shortcuts-dialog') as AdwShortcutsDialog;
31
+ el.setAttribute('title', 'Keyboard Shortcuts');
32
+ el.append(...sections);
33
+ document.body.appendChild(el);
34
+ return el;
35
+ }
36
+
37
+ export const AdwShortcutsTest = async () => {
38
+ await describe('adw-shortcuts-dialog is an adw-dialog', async () => {
39
+ await it('carries the dialog chrome and its title', async () => {
40
+ const dlg = dialog(section('General'));
41
+ // The flat header with the close button is the base's, and the class is
42
+ // `dialog.shortcuts` upstream — which is why the element adds it as a class
43
+ // of its own rather than a wrapper element.
44
+ expect(dlg.classList.contains('adw-dialog')).toBe(true);
45
+ expect(dlg.classList.contains('adw-shortcuts-dialog')).toBe(true);
46
+ expect(dlg.querySelector('.adw-dialog-header')).not.toBeNull();
47
+ expect(dlg.querySelector('.adw-dialog-title')?.textContent).toBe('Keyboard Shortcuts');
48
+ dlg.remove();
49
+ });
50
+
51
+ await it('presents and closes through the inherited dialog API', async () => {
52
+ const dlg = dialog(section('General'));
53
+ expect(dlg.open).toBe(false);
54
+ dlg.present();
55
+ expect(dlg.open).toBe(true);
56
+ dlg.close();
57
+ expect(dlg.open).toBe(false);
58
+ dlg.remove();
59
+ });
60
+
61
+ await it('holds its sections inside the dialog content area', async () => {
62
+ const dlg = dialog(section('General'), section('Navigation'));
63
+ expect(dlg.sections.length).toBe(2);
64
+ // The sections sit in the base's own content area — no wrapper of this
65
+ // class's own — so the dialog's padding and scrolling wrap them the way
66
+ // they wrap any other child, and a section added later lands there too.
67
+ expect(dlg.sections.every((el) => el.parentElement === dlg.contentArea)).toBe(true);
68
+ dlg.remove();
69
+ });
70
+
71
+ await it('routes a section appended after connect into the content area', async () => {
72
+ const dlg = dialog(section('General'));
73
+ const late = section('Navigation');
74
+ dlg.appendChild(late);
75
+ // The routing is a MutationObserver (`bindSlottedChildren`), so the move is
76
+ // a microtask away rather than synchronous with the append.
77
+ await Promise.resolve();
78
+ expect(dlg.sections.length).toBe(2);
79
+ expect(late.parentElement).toBe(dlg.contentArea);
80
+ dlg.remove();
81
+ });
82
+ });
83
+
84
+ await describe('adw-shortcuts-section', async () => {
85
+ await it('draws a heading only when it has a title', async () => {
86
+ // The class documents an untitled section as the way to subdivide a section
87
+ // into groups, and libadwaita draws no heading for one.
88
+ const titled = section('General', item('Open Menu', 'F10'));
89
+ const untitled = section(null, item('Move Tab Left', '<Shift><Ctrl>Page_Up'));
90
+ document.body.append(titled, untitled);
91
+
92
+ const titledHeading = titled.querySelector('.adw-shortcuts-section-title') as HTMLElement;
93
+ const untitledHeading = untitled.querySelector('.adw-shortcuts-section-title') as HTMLElement;
94
+ expect(titledHeading.hidden).toBe(false);
95
+ expect(untitledHeading.hidden).toBe(true);
96
+ titled.remove();
97
+ untitled.remove();
98
+ });
99
+
100
+ await it('exposes its items in document order', async () => {
101
+ const sec = section('General', item('Open Menu', 'F10'), item('Quit', '<Control>Q'));
102
+ document.body.appendChild(sec);
103
+ expect(sec.items.map((el) => el.getAttribute('title'))).toStrictEqual(['Open Menu', 'Quit']);
104
+ sec.remove();
105
+ });
106
+ });
107
+
108
+ await describe('adw-shortcuts-item', async () => {
109
+ await it('draws the accelerator as keycaps', async () => {
110
+ const el = item('Open Menu', 'F10');
111
+ document.body.appendChild(el);
112
+ const label = el.querySelector('adw-shortcut-label');
113
+ expect(label?.getAttribute('accelerator')).toBe('F10');
114
+ expect(el.hasAccelerator).toBe(true);
115
+ el.remove();
116
+ });
117
+
118
+ await it('draws no keycaps for an item that stands for an action alone', async () => {
119
+ // An accelerator for an action comes from Gtk.Application, which a document
120
+ // has no way to ask, so the row is allowed to carry the action alone.
121
+ const el = item('Preferences');
122
+ el.setAttribute('action-name', 'app.preferences');
123
+ document.body.appendChild(el);
124
+ expect(el.hasAccelerator).toBe(false);
125
+ expect((el.querySelector('adw-shortcut-label') as HTMLElement | null)?.hidden).toBe(true);
126
+ el.remove();
127
+ });
128
+
129
+ await it('draws the subtitle only when there is one', async () => {
130
+ const withSub = item('Move Tab Left', '<Shift><Ctrl>Page_Up', 'Move the current tab');
131
+ const without = item('Quit', '<Control>Q');
132
+ document.body.append(withSub, without);
133
+ const sub = withSub.querySelector('.adw-shortcuts-item-subtitle') as HTMLElement;
134
+ expect(sub.hidden).toBe(false);
135
+ expect(sub.textContent).toBe('Move the current tab');
136
+ expect((without.querySelector('.adw-shortcuts-item-subtitle') as HTMLElement).hidden).toBe(true);
137
+ withSub.remove();
138
+ without.remove();
139
+ });
140
+
141
+ await it('hides a row that belongs to the other text direction', async () => {
142
+ // adw-shortcuts-item.c:25-28 — one dialog carries both an LTR and an RTL
143
+ // accelerator for the same action, and each row shows in its own direction.
144
+ const rtl = item('Move Tab Left', '<Shift><Ctrl>Page_Up');
145
+ rtl.setAttribute('direction', 'rtl');
146
+ const both = item('Quit', '<Control>Q');
147
+ document.body.append(rtl, both);
148
+ // The harness document is LTR, so the RTL row is the one that hides.
149
+ expect(rtl.hidden).toBe(true);
150
+ expect(both.hidden).toBe(false);
151
+ rtl.remove();
152
+ both.remove();
153
+ });
154
+ });
155
+ };
@@ -0,0 +1,124 @@
1
+ // <adw-status-page> compact — the documented smaller status page for a narrow surface.
2
+ //
3
+ // libadwaita ships ONE style class on this widget, `.compact`
4
+ // (style-classes.md "Compact Status Page"), and `_misc.scss` gives it four differences from the
5
+ // full page: margin 24px 12px not 36px 12px, icon 96px not 128px, 12px not 24px under the icon,
6
+ // `.title-2` not `.title-1`. The child keeps libadwaita's 24px spacing, of which this renderer's
7
+ // flex `gap` supplies 12 — hence `margin-top: 12px`.
8
+ //
9
+ // Each is asserted as a COMPUTED size on the node the rule selects, not as the class alone: the
10
+ // class would pass against the pre-#1826 element whenever an author wrote `class="compact"` by
11
+ // hand, so a class-only suite would have left the variant unmeasured.
12
+ import { describe, expect, it } from '@gjsify/unit';
13
+
14
+ import type { AdwStatusPage } from './elements/adw-status-page.js';
15
+
16
+ /** Mount a status page wide enough for the full variant to differ from the compact one. */
17
+ function mount(
18
+ attributes: Record<string, string> = {},
19
+ width = 400,
20
+ ): {
21
+ el: AdwStatusPage;
22
+ host: HTMLElement;
23
+ } {
24
+ const host = document.createElement('div');
25
+ host.style.width = `${width}px`;
26
+ document.body.appendChild(host);
27
+ const el = document.createElement('adw-status-page') as AdwStatusPage;
28
+ el.setAttribute('icon', 'dialog-error-symbolic');
29
+ el.setAttribute('title', 'Could not connect');
30
+ el.setAttribute('description', 'Check your network connection and try again.');
31
+ for (const [name, value] of Object.entries(attributes)) el.setAttribute(name, value);
32
+ host.appendChild(el);
33
+ return { el, host };
34
+ }
35
+
36
+ /** Tolerance for a computed ratio: Firefox rounds a percentage-derived px size. */
37
+ const near = (actual: number, expected: number, tolerance = 0.005): boolean => Math.abs(actual - expected) <= tolerance;
38
+
39
+ const iconOf = (el: AdwStatusPage): HTMLElement => el.querySelector('.adw-status-page-icon') as HTMLElement;
40
+ const titleOf = (el: AdwStatusPage): HTMLElement => el.querySelector('.adw-status-page-title') as HTMLElement;
41
+ const childOf = (el: AdwStatusPage): HTMLElement => el.querySelector('.adw-status-page-child') as HTMLElement;
42
+
43
+ export const AdwStatusPageTest = async () => {
44
+ await describe('adw-status-page compact (style-classes.md "Compact Status Page")', async () => {
45
+ await it('the compact attribute puts the documented style class on the host', async () => {
46
+ const { el, host } = mount({ compact: '' });
47
+ // The selector is `adw-status-page.compact` — the HOST, not an inner node, which
48
+ // is why this element writes the class itself instead of leaving it to a wrapper.
49
+ expect(el.classList.contains('compact')).toBe(true);
50
+ host.remove();
51
+ });
52
+
53
+ await it('a full status page carries no compact class and keeps the 128px icon', async () => {
54
+ const { el, host } = mount();
55
+ expect(el.classList.contains('compact')).toBe(false);
56
+ expect(getComputedStyle(iconOf(el)).width).toBe('128px');
57
+ expect(getComputedStyle(el).paddingTop).toBe('36px');
58
+ host.remove();
59
+ });
60
+
61
+ await it('compact shrinks the icon to 96px and the margin to 24px (_misc.scss, statuspage.compact)', async () => {
62
+ const { el, host } = mount({ compact: '' });
63
+ const icon = getComputedStyle(iconOf(el));
64
+ expect(icon.width).toBe('96px');
65
+ expect(icon.height).toBe('96px');
66
+ expect(icon.marginBottom).toBe('12px');
67
+ expect(getComputedStyle(el).paddingTop).toBe('24px');
68
+ expect(getComputedStyle(el).paddingBottom).toBe('24px');
69
+ host.remove();
70
+ });
71
+
72
+ await it('compact sets .title-2 (136%), the full page .title-1 (181%) (_labels.scss)', async () => {
73
+ const compact = mount({ compact: '' });
74
+ const full = mount();
75
+ try {
76
+ // The RATIO, not a pixel size: `_misc.scss` picks between `.title-1` and
77
+ // `.title-2`, which are 181% and 136% of the SAME inherited size — the
78
+ // boundary reset re-roots it (`_reset.scss`, ADR 0010), and a px assertion
79
+ // would pin this suite to one font instead of to the ratio the class encodes.
80
+ const compactTitle = parseFloat(getComputedStyle(titleOf(compact.el)).fontSize);
81
+ const fullTitle = parseFloat(getComputedStyle(titleOf(full.el)).fontSize);
82
+ expect(compactTitle < fullTitle).toBe(true);
83
+ expect(near(compactTitle / fullTitle, 136 / 181)).toBe(true);
84
+ // …and the weights both `.title-1`/`.title-2` carry, so this is the
85
+ // heading scale rather than a body label that happens to be smaller.
86
+ expect(getComputedStyle(titleOf(compact.el)).fontWeight).toBe('800');
87
+ } finally {
88
+ compact.host.remove();
89
+ full.host.remove();
90
+ }
91
+ });
92
+
93
+ await it('compact leaves 12px to the child beyond the 12px flex gap (_misc.scss, 24px spacing)', async () => {
94
+ const { el, host } = mount({ compact: '' });
95
+ const button = document.createElement('button');
96
+ button.className = 'adw-button suggested-action';
97
+ button.textContent = 'Try Again';
98
+ el.appendChild(button);
99
+
100
+ expect(getComputedStyle(childOf(el)).marginTop).toBe('12px');
101
+ // The full page's own 24px, so the pair cannot both be the compact rule.
102
+ const full = mount();
103
+ try {
104
+ expect(getComputedStyle(childOf(full.el)).marginTop).toBe('24px');
105
+ } finally {
106
+ host.remove();
107
+ full.host.remove();
108
+ }
109
+ });
110
+
111
+ await it('a .blp\'s styles ["compact"] survives a later attribute change', async () => {
112
+ const { el, host } = mount({ compact: '' });
113
+ el.classList.add('compact');
114
+ el.removeAttribute('compact');
115
+ // `title` is the attribute every real page sets, and setting it re-renders. A
116
+ // `toggle` here would strip the class the shared-tree builder had put there —
117
+ // `shared-tree-builder.ts:296` writes a `.blp`'s style classes onto this host.
118
+ el.setAttribute('title', 'Still compact');
119
+ expect(el.classList.contains('compact')).toBe(true);
120
+ expect(getComputedStyle(iconOf(el)).width).toBe('96px');
121
+ host.remove();
122
+ });
123
+ });
124
+ };
@@ -0,0 +1,287 @@
1
+ // DOM-level tests for <adw-tab-bar>: the strip libadwaita binds to a view through
2
+ // `Adw.TabBar:view`. Everything under test is derived from a live view, so each case
3
+ // drives the view and reads the bar — the same arrangement `adw-tab-button.spec.ts` uses
4
+ // for the one widget that is also a view's companion.
5
+ import { describe, expect, it } from '@gjsify/unit';
6
+
7
+ import type { AdwTabBar } from './elements/adw-tab-bar.js';
8
+ import type { AdwTabView } from './elements/adw-tab-view.js';
9
+
10
+ const settle = () => new Promise<void>((resolve) => setTimeout(resolve, 0));
11
+
12
+ let serial = 0;
13
+
14
+ /** A view with `titles`, a bar bound to it BY ID, and the action widgets' slots empty. */
15
+ function mount(titles: readonly string[]): { bar: AdwTabBar; view: AdwTabView; host: HTMLElement } {
16
+ const id = `bar-view-${(serial += 1)}`;
17
+ const host = document.createElement('div');
18
+ const pages = titles.map((title) => `<adw-tab-page title="${title}"></adw-tab-page>`).join('');
19
+ host.innerHTML = `<adw-tab-view id="${id}">${pages}</adw-tab-view><adw-tab-bar view="${id}"></adw-tab-bar>`;
20
+ document.body.appendChild(host);
21
+ return {
22
+ bar: host.querySelector('adw-tab-bar') as AdwTabBar,
23
+ view: host.querySelector('adw-tab-view') as AdwTabView,
24
+ host,
25
+ };
26
+ }
27
+
28
+ const chips = (bar: AdwTabBar) => [...bar.querySelectorAll('.adw-tab')] as HTMLElement[];
29
+ const chipTitles = (bar: AdwTabBar) => chips(bar).map((chip) => chip.querySelector('.adw-tab-title')?.textContent);
30
+
31
+ export const AdwTabBarTest = async () => {
32
+ await describe('<adw-tab-bar> binds to a view', async () => {
33
+ await it('resolves `view` by element id and draws one chip per page', async () => {
34
+ const { bar, view, host } = mount(['One', 'Two', 'Three']);
35
+ expect(bar.view).toBe(view);
36
+ expect(chipTitles(bar)).toStrictEqual(['One', 'Two', 'Three']);
37
+ host.remove();
38
+ });
39
+
40
+ await it('takes the view directly through the property', async () => {
41
+ const view = document.createElement('adw-tab-view') as AdwTabView;
42
+ document.body.appendChild(view);
43
+ view.appendPage({ id: 'a', title: 'A' });
44
+ const bar = document.createElement('adw-tab-bar') as AdwTabBar;
45
+ document.body.appendChild(bar);
46
+ bar.view = view;
47
+ expect(bar.view).toBe(view);
48
+ expect(chipTitles(bar)).toStrictEqual(['A']);
49
+ bar.remove();
50
+ view.remove();
51
+ });
52
+
53
+ await it('follows pages added, removed and reordered after connect', async () => {
54
+ const { bar, view, host } = mount(['One', 'Two']);
55
+ const first = view.pages[0].id;
56
+ view.appendPage({ id: 'three', title: 'Three' });
57
+ expect(chipTitles(bar)).toStrictEqual(['One', 'Two', 'Three']);
58
+ view.reorderPage('three', 0);
59
+ expect(chipTitles(bar)).toStrictEqual(['Three', 'One', 'Two']);
60
+ view.closePage(first);
61
+ expect(chipTitles(bar)).toStrictEqual(['Three', 'Two']);
62
+ host.remove();
63
+ });
64
+
65
+ await it('reflects a title change without rebuilding the chip', async () => {
66
+ const { bar, view, host } = mount(['One']);
67
+ const chip = chips(bar)[0];
68
+ view.setPageTitle(view.pages[0].id, 'Renamed');
69
+ expect(chips(bar)[0]).toBe(chip);
70
+ expect(chip.querySelector('.adw-tab-title')?.textContent).toBe('Renamed');
71
+ host.remove();
72
+ });
73
+ });
74
+
75
+ await describe('<adw-tab-bar> selection', async () => {
76
+ await it('marks the selected chip and moves the roving tabindex with it', async () => {
77
+ const { bar, view, host } = mount(['One', 'Two']);
78
+ expect(chips(bar).map((chip) => chip.classList.contains('active'))).toStrictEqual([true, false]);
79
+ expect(chips(bar).map((chip) => chip.tabIndex)).toStrictEqual([0, -1]);
80
+
81
+ view.selectNthPage(1);
82
+ expect(chips(bar).map((chip) => chip.classList.contains('active'))).toStrictEqual([false, true]);
83
+ expect(chips(bar).map((chip) => chip.tabIndex)).toStrictEqual([-1, 0]);
84
+ expect(chips(bar).map((chip) => chip.getAttribute('aria-selected'))).toStrictEqual(['false', 'true']);
85
+ host.remove();
86
+ });
87
+
88
+ await it('selects on a chip press and closes on the close affordance', async () => {
89
+ const { bar, view, host } = mount(['One', 'Two']);
90
+ const second = view.pages[1].id;
91
+ chips(bar)[1].dispatchEvent(new MouseEvent('click', { bubbles: true }));
92
+ expect(view.selectedId).toBe(second);
93
+
94
+ const close = chips(bar)[1].querySelector('.adw-tab-close') as HTMLElement;
95
+ // The close affordance is hidden until the tab is hovered or selected, and this
96
+ // one is the selected tab, so `tabCloseVisible` shows it on its own.
97
+ expect(close.hidden).toBe(false);
98
+ close.dispatchEvent(new MouseEvent('click', { bubbles: true }));
99
+ expect(chipTitles(bar)).toStrictEqual(['One']);
100
+ host.remove();
101
+ });
102
+
103
+ await it('ArrowRight and End move the selection from the keyboard', async () => {
104
+ const { bar, view, host } = mount(['One', 'Two', 'Three']);
105
+ const ids = view.pages.map((page) => page.id);
106
+ chips(bar)[0].dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true }));
107
+ expect(view.selectedId).toBe(ids[1]);
108
+ chips(bar)[1].dispatchEvent(new KeyboardEvent('keydown', { key: 'End', bubbles: true }));
109
+ expect(view.selectedId).toBe(ids[2]);
110
+ host.remove();
111
+ });
112
+ });
113
+
114
+ await describe('<adw-tab-bar> tabs-revealed', async () => {
115
+ await it('is up without `autohide`, whatever the page count', async () => {
116
+ const { bar, host } = mount(['One']);
117
+ expect(bar.tabsRevealed).toBe(true);
118
+ expect(bar.hidden).toBe(false);
119
+ host.remove();
120
+ });
121
+
122
+ await it('hides with `autohide` on a single page and comes back for two', async () => {
123
+ const { bar, view, host } = mount(['One']);
124
+ bar.autohide = true;
125
+ // `tabsRevealed` reads the LIVE attribute, so the write is the whole input.
126
+ expect(bar.tabsRevealed).toBe(false);
127
+ expect(bar.hidden).toBe(true);
128
+
129
+ view.appendPage({ id: 'two', title: 'Two' });
130
+ expect(bar.tabsRevealed).toBe(true);
131
+ expect(bar.hidden).toBe(false);
132
+ host.remove();
133
+ });
134
+
135
+ await it('stays up with `autohide` on a single PINNED page', async () => {
136
+ const { bar, host } = mount(['Pinned']);
137
+ bar.autohide = true;
138
+ const page = bar.view?.pages[0];
139
+ expect(page?.pinned).toBe(false);
140
+ // A declared `<adw-tab-page>` with no `page-id` gets a generated one, which an
141
+ // author cannot predict — so the pin goes in through the page's own id.
142
+ bar.view?.setPagePinned(page?.id ?? '', true);
143
+ expect(bar.tabsRevealed).toBe(true);
144
+ expect(bar.hidden).toBe(false);
145
+ host.remove();
146
+ });
147
+ });
148
+
149
+ await describe('<adw-tab-bar> properties', async () => {
150
+ await it('carries expand-tabs and inverted as attributes, and says so', async () => {
151
+ const { bar, host } = mount(['One', 'Two']);
152
+ expect(bar.expandTabs).toBe(false);
153
+ expect(bar.inverted).toBe(false);
154
+ bar.expandTabs = true;
155
+ bar.inverted = true;
156
+ expect(bar.hasAttribute('expand-tabs')).toBe(true);
157
+ expect(bar.hasAttribute('inverted')).toBe(true);
158
+ host.remove();
159
+ });
160
+
161
+ await it('notifies each property on a real change', async () => {
162
+ const { bar, host } = mount(['One']);
163
+ const seen: string[] = [];
164
+ for (const name of ['autohide', 'expand-tabs', 'inverted', 'extra-drag-preload']) {
165
+ bar.addEventListener(`notify::${name}`, () => seen.push(name));
166
+ }
167
+ bar.autohide = true;
168
+ bar.inverted = true;
169
+ expect(seen).toStrictEqual(['autohide', 'inverted']);
170
+ host.remove();
171
+ });
172
+
173
+ await it('notifies `view` when it is rebound', async () => {
174
+ const { bar, view, host } = mount(['One']);
175
+ let notified = false;
176
+ bar.addEventListener('notify::view', () => (notified = true));
177
+ const other = document.createElement('adw-tab-view') as AdwTabView;
178
+ document.body.appendChild(other);
179
+ other.appendPage({ id: 'z', title: 'Z' });
180
+ bar.setView(other);
181
+ expect(notified).toBe(true);
182
+ expect(chipTitles(bar)).toStrictEqual(['Z']);
183
+ bar.setView(view);
184
+ expect(chipTitles(bar)).toStrictEqual(['One']);
185
+ other.remove();
186
+ host.remove();
187
+ });
188
+ });
189
+
190
+ await describe('<adw-tab-bar> action widgets', async () => {
191
+ await it('routes a `slot="start-action-widget"` child into the leading bin', async () => {
192
+ const { bar, host } = mount(['One']);
193
+ const button = document.createElement('button');
194
+ button.slot = 'start-action-widget';
195
+ // Appended AFTER connect on purpose: `bindSlottedChildren` observes the host, so
196
+ // a late child lands where a parse-time one would — on the observer's microtask,
197
+ // which is why the read below waits for one.
198
+ bar.appendChild(button);
199
+ await settle();
200
+ expect(bar.startActionWidget).toBe(button);
201
+ expect(bar.querySelector('.adw-tab-bar-start-action button')).toBe(button);
202
+ // The bin leads the box, which is the order `AdwTabBar`'s template puts the two
203
+ // action bins in (adw-tab-bar.ui).
204
+ expect(bar.querySelector('.adw-tab-box')?.previousElementSibling).toBe(
205
+ bar.querySelector('.adw-tab-bar-start-action'),
206
+ );
207
+ host.remove();
208
+ });
209
+
210
+ await it('routes a `slot="end-action-widget"` child into the trailing bin', async () => {
211
+ const { bar, host } = mount(['One']);
212
+ const button = document.createElement('button');
213
+ button.slot = 'end-action-widget';
214
+ bar.appendChild(button);
215
+ await settle();
216
+ expect(bar.endActionWidget).toBe(button);
217
+ expect(bar.querySelector('.adw-tab-bar-end-action button')).toBe(button);
218
+ host.remove();
219
+ });
220
+
221
+ await it('takes an action widget through the property too', async () => {
222
+ const { bar, host } = mount(['One']);
223
+ const button = document.createElement('button');
224
+ bar.endActionWidget = button;
225
+ expect(bar.querySelector('.adw-tab-bar-end-action')?.lastElementChild).toBe(button);
226
+ bar.endActionWidget = null;
227
+ expect(bar.endActionWidget).toBe(null);
228
+ host.remove();
229
+ });
230
+ });
231
+
232
+ await describe('<adw-tab-bar> overflow and the extra drop target', async () => {
233
+ await it('reports `is-overflowing` off the strip, not off the chip count', async () => {
234
+ const { bar, host } = mount(['One', 'Two']);
235
+ // Nothing is scrolled yet, so the strip fits its chips and does NOT overflow:
236
+ // the indicator is the adjustment's answer, not a threshold on `nPages`.
237
+ expect(bar.isOverflowing).toBe(false);
238
+ expect((bar.querySelector('.adw-tab-box-overflow') as HTMLElement).hidden).toBe(true);
239
+ host.remove();
240
+ });
241
+
242
+ await it('dispatches `extra-drag-value` on hover-start and carries the action back', async () => {
243
+ const { bar, host } = mount(['One']);
244
+ bar.setupExtraDropTarget(['text/plain']);
245
+ let seen: unknown = 'unset';
246
+ bar.addEventListener('extra-drag-value', (event) => {
247
+ seen = (event as CustomEvent).detail.action;
248
+ (event as CustomEvent).detail.action = 'move';
249
+ });
250
+ const transfer = new DataTransfer();
251
+ transfer.setData('text/plain', 'payload');
252
+ bar.dispatchEvent(new DragEvent('dragover', { dataTransfer: transfer, bubbles: true, cancelable: true }));
253
+ // Hover-start emits a NULL action, and `extra-drag-preferred-action` is whatever
254
+ // the handler answered with.
255
+ expect(seen).toBe(null);
256
+ expect(bar.extraDragPreferredAction).toBe('move');
257
+ host.remove();
258
+ });
259
+
260
+ await it('dispatches a cancelable `extra-drag-drop` carrying the dropped data', async () => {
261
+ const { bar, host } = mount(['One']);
262
+ bar.setupExtraDropTarget(['text/plain'], ['copy']);
263
+ let detail: { action: unknown; data: unknown } | null = null;
264
+ bar.addEventListener('extra-drag-drop', (event) => {
265
+ detail = (event as CustomEvent).detail;
266
+ });
267
+ const transfer = new DataTransfer();
268
+ transfer.setData('text/plain', 'payload');
269
+ bar.dispatchEvent(new DragEvent('drop', { dataTransfer: transfer, bubbles: true, cancelable: true }));
270
+ expect(detail?.data).toBe('payload');
271
+ expect(detail?.action).toBe('copy');
272
+ host.remove();
273
+ });
274
+
275
+ await it('leaves a drop of an unlisted type to the page', async () => {
276
+ const { bar, host } = mount(['One']);
277
+ bar.setupExtraDropTarget(['text/plain']);
278
+ let dropped = false;
279
+ bar.addEventListener('extra-drag-drop', () => (dropped = true));
280
+ const transfer = new DataTransfer();
281
+ transfer.setData('application/x-elsewhere', 'payload');
282
+ bar.dispatchEvent(new DragEvent('drop', { dataTransfer: transfer, bubbles: true, cancelable: true }));
283
+ expect(dropped).toBe(false);
284
+ host.remove();
285
+ });
286
+ });
287
+ };
@@ -0,0 +1,101 @@
1
+ // DOM-level tests for <adw-tab-button>: the page counter (`update_icon`,
2
+ // adw-tab-button.c:102-133) and the attention dot (`update_needs_attention`,
3
+ // adw-tab-button.c:135-161). The counter is DERIVED from a view, so every test drives the
4
+ // view and reads the button.
5
+ import { describe, expect, it } from '@gjsify/unit';
6
+
7
+ import type { AdwTabButton } from './elements/adw-tab-button.js';
8
+ import type { AdwTabView } from './elements/adw-tab-view.js';
9
+
10
+ const settle = () => new Promise<void>((resolve) => setTimeout(resolve, 0));
11
+
12
+ let serial = 0;
13
+
14
+ function mount(pages: number): { button: AdwTabButton; view: AdwTabView; host: HTMLElement } {
15
+ const id = `tb-view-${(serial += 1)}`;
16
+ const host = document.createElement('div');
17
+ const rows = Array.from({ length: pages }, (_, i) => `<adw-tab-page title="Page ${i}"></adw-tab-page>`);
18
+ host.innerHTML = `<adw-tab-view id="${id}">${rows.join('')}</adw-tab-view><adw-tab-button view="${id}"></adw-tab-button>`;
19
+ document.body.appendChild(host);
20
+ return {
21
+ button: host.querySelector('adw-tab-button') as AdwTabButton,
22
+ view: host.querySelector('adw-tab-view') as AdwTabView,
23
+ host,
24
+ };
25
+ }
26
+
27
+ const label = (button: AdwTabButton) => button.querySelector('.adw-tab-button-label') as HTMLElement;
28
+ const dot = (button: AdwTabButton) => button.querySelector('.adw-tab-button-indicator') as HTMLElement;
29
+
30
+ export const AdwTabButtonTest = async () => {
31
+ await describe('<adw-tab-button> counter', async () => {
32
+ await it('shows the page count of the view it is bound to', async () => {
33
+ const { button, view, host } = mount(3);
34
+ expect(button.view).toBe(view);
35
+ expect(label(button).textContent).toBe('3');
36
+ expect(label(button).classList.contains('small')).toBe(false);
37
+ host.remove();
38
+ });
39
+
40
+ await it('follows pages added and removed', async () => {
41
+ const { button, view, host } = mount(2);
42
+ // A page added after the view connected goes through its API: declared
43
+ // `<adw-tab-page>` children are adopted on connect only.
44
+ view.appendPage({ id: 'late', title: 'Late' });
45
+ expect(label(button).textContent).toBe('3');
46
+ view.closePage('late');
47
+ expect(label(button).textContent).toBe('2');
48
+ host.remove();
49
+ });
50
+
51
+ await it('is `small` from ten pages and a glyph instead of a number from a hundred', async () => {
52
+ const ten = mount(10);
53
+ expect(label(ten.button).textContent).toBe('10');
54
+ expect(label(ten.button).classList.contains('small')).toBe(true);
55
+ ten.host.remove();
56
+
57
+ const hundred = mount(100);
58
+ expect(label(hundred.button).hidden).toBe(true);
59
+ expect(hundred.button.querySelector('.adw-tab-button-indicatorbin')?.classList.contains('overflow')).toBe(
60
+ true,
61
+ );
62
+ hundred.host.remove();
63
+ });
64
+
65
+ await it('without a view the plate stays, with a zero on it', async () => {
66
+ const host = document.createElement('div');
67
+ host.innerHTML = '<adw-tab-button></adw-tab-button>';
68
+ document.body.appendChild(host);
69
+ const button = host.firstElementChild as AdwTabButton;
70
+ expect(button.view).toBeNull();
71
+ expect(label(button).textContent).toBe('0');
72
+ host.remove();
73
+ });
74
+ });
75
+
76
+ await describe('<adw-tab-button> needs-attention', async () => {
77
+ await it('lights up for a page that is NOT the selected one', async () => {
78
+ const { button, view, host } = mount(3);
79
+ expect(dot(button).hidden).toBe(true);
80
+ view.querySelectorAll('adw-tab-page')[2].setAttribute('needs-attention', '');
81
+ await settle();
82
+ expect(dot(button).hidden).toBe(false);
83
+ host.remove();
84
+ });
85
+
86
+ await it('ignores the selected page, which is already in front of the user', async () => {
87
+ const { button, view, host } = mount(3);
88
+ view.querySelectorAll('adw-tab-page')[0].setAttribute('needs-attention', '');
89
+ await settle();
90
+ expect(dot(button).hidden).toBe(true);
91
+ host.remove();
92
+ });
93
+
94
+ await it('is a real button the platform activates', async () => {
95
+ const { button, host } = mount(1);
96
+ expect(button.getAttribute('role')).toBe('button');
97
+ expect(button.querySelector('button')).not.toBeNull();
98
+ host.remove();
99
+ });
100
+ });
101
+ };