@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,422 @@
1
+ // DOM-level tests for <gtk-list-box>: the rows it collects, the `.activatable` class and
2
+ // `aria-selected` it derives for each of them, and the three selection doors (click,
3
+ // `select_row`, `select_all`) driven with real clicks and real modifiers.
4
+ //
5
+ // The RULES are `box-selection.spec.ts` in @gjsify/adwaita-core, driven from the same
6
+ // conformance tables; what is asserted here is that the element WIRES them — that a click on
7
+ // row 3 reaches `listBoxSelect` with the right position and modifiers, and that the answer
8
+ // comes back onto the row as a class, an ARIA state and a tab stop.
9
+ import { describe, expect, it } from '@gjsify/unit';
10
+
11
+ import { BOX_ALL_VECTORS, BOX_ROW_VECTORS, BOX_SELECT_VECTORS } from '@gjsify/adwaita-core/conformance';
12
+
13
+ import type { GtkListBox } from './elements/gtk-list-box.js';
14
+ import type { GtkListBoxRow } from './elements/gtk-list-box-row.js';
15
+
16
+ /** The labels the hand-written tests use; a vector that names a longer box gets a numbered row. */
17
+ const LABELS = ['Documents', 'Downloads', 'Music', 'Pictures', 'Videos'];
18
+
19
+ /**
20
+ * A box with `count` rows.
21
+ *
22
+ * `count` is the vector's own `length` whenever a table drives this: the tables name the box
23
+ * they are about, and a harness that silently ran every vector against the same five rows
24
+ * turned `select_all` on a four-row box into a five-row assertion and could not express a box
25
+ * longer than the labels.
26
+ */
27
+ function mount(
28
+ attrs: Record<string, string> = {},
29
+ rowAttrs: Record<string, string>[] = [],
30
+ count = LABELS.length,
31
+ ): { el: GtkListBox; host: HTMLElement } {
32
+ const host = document.createElement('div');
33
+ document.body.appendChild(host);
34
+ const el = document.createElement('gtk-list-box') as GtkListBox;
35
+ for (const [name, value] of Object.entries(attrs)) el.setAttribute(name, value);
36
+ for (let index = 0; index < count; index++) {
37
+ const row = document.createElement('gtk-list-box-row') as GtkListBoxRow;
38
+ for (const [name, value] of Object.entries(rowAttrs[index] ?? {})) row.setAttribute(name, value);
39
+ const text = document.createElement('span');
40
+ text.textContent = LABELS[index] ?? `row ${index}`;
41
+ row.appendChild(text);
42
+ el.appendChild(row);
43
+ }
44
+ host.appendChild(el);
45
+ return { el, host };
46
+ }
47
+
48
+ /**
49
+ * Open the box on the state a vector names — its selection AND its anchor
50
+ * (`box->selected_row`, gtklistbox.c:145).
51
+ *
52
+ * The two are independent upstream, and `select_row` alone cannot reach the pair the
53
+ * `<Shift>` rows need: `gtk_list_box_unselect_row_internal` unselects a row in `multiple`
54
+ * mode and LEAVES THE ANCHOR ON IT (gtklistbox.c:1719-1722), so "a selection with an anchor
55
+ * that is not in it" is a state GTK is in, and a later `<Shift>` measures from the anchor.
56
+ * Selecting the anchor row and unselecting it again is the same pair of doors an application
57
+ * has, so the state is built through the element rather than around it.
58
+ */
59
+ function openOn(el: GtkListBox, selection: readonly number[], anchor: number): void {
60
+ for (const position of selection) el.selectRow(el.rows[position]!);
61
+ if (anchor < 0) {
62
+ el.unselectAll();
63
+ return;
64
+ }
65
+ if (selection.includes(anchor)) return;
66
+ el.selectRow(el.rows[anchor]!);
67
+ el.unselectRow(el.rows[anchor]!);
68
+ }
69
+
70
+ /** Row attributes for a vector's length, with `position` marked unselectable, or none. */
71
+ function rowAttrsFor(length: number, position: number | null): Record<string, string>[] {
72
+ const attrs = Array.from({ length }, () => ({}) as Record<string, string>);
73
+ if (position !== null && position >= 0 && position < length) attrs[position] = { selectable: 'false' };
74
+ return attrs;
75
+ }
76
+
77
+ const rowsOf = (el: GtkListBox): GtkListBoxRow[] => [...el.querySelectorAll('gtk-list-box-row')] as GtkListBoxRow[];
78
+ const click = (target: Element, init: MouseEventInit = {}) =>
79
+ target.dispatchEvent(new MouseEvent('click', { bubbles: true, detail: 1, ...init }));
80
+ const key = (target: Element, init: KeyboardEventInit) =>
81
+ target.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, ...init }));
82
+
83
+ export const GtkListBoxTest = async () => {
84
+ await describe('<gtk-list-box> collects its rows and derives their state', async () => {
85
+ await it('is a listbox of listitems while it can select, and a plain list in none', () => {
86
+ const { el, host } = mount();
87
+ expect(el.getAttribute('role')).toBe('listbox');
88
+ expect(rowsOf(el)[0]!.getAttribute('role')).toBe('listitem');
89
+ expect(el.getAttribute('aria-multiselectable')).toBe(null);
90
+ el.selectionMode = 'none';
91
+ expect(el.getAttribute('role')).toBe('list');
92
+ el.selectionMode = 'multiple';
93
+ expect(el.getAttribute('aria-multiselectable')).toBe('true');
94
+ host.remove();
95
+ });
96
+
97
+ await it('gives every row the .activatable class while the box can select', () => {
98
+ // gtk_list_box_update_row (:3611-3615): activatable OR (selectable AND can_select).
99
+ const { el, host } = mount();
100
+ expect(rowsOf(el).every((row) => row.classList.contains('activatable'))).toBe(true);
101
+ host.remove();
102
+ });
103
+
104
+ await it('drops the class off an unactivatable, unselectable row in a none box', () => {
105
+ const { el, host } = mount({ 'selection-mode': 'none' }, [
106
+ {},
107
+ { activatable: 'false', selectable: 'false' },
108
+ ]);
109
+ // Row 0 KEEPS the class: `activatable || (selectable && can_select)` with
110
+ // `can_select` false still answers true through its own `activatable`
111
+ // (gtklistbox.c:3611-3615). Only the row with both flags off loses it.
112
+ expect(rowsOf(el)[0]!.classList.contains('activatable')).toBe(true);
113
+ expect(rowsOf(el)[1]!.classList.contains('activatable')).toBe(false);
114
+ host.remove();
115
+ });
116
+
117
+ await it('keeps the class on an unactivatable row that is still selectable', () => {
118
+ const { el, host } = mount({}, [{}, { activatable: 'false' }]);
119
+ expect(rowsOf(el)[1]!.classList.contains('activatable')).toBe(true);
120
+ host.remove();
121
+ });
122
+
123
+ await it('publishes aria-selected only while the row is selectable and the box can select', () => {
124
+ const { el, host } = mount({ 'selection-mode': 'multiple' }, [{}, { selectable: 'false' }]);
125
+ el.selectAll();
126
+ expect(rowsOf(el)[0]!.getAttribute('aria-selected')).toBe('true');
127
+ // `gtk_accessible_reset_state` (:3622-3623) — not `false`, ABSENT.
128
+ expect(rowsOf(el)[1]!.hasAttribute('aria-selected')).toBe(false);
129
+ el.selectionMode = 'none';
130
+ expect(rowsOf(el).every((row) => row.hasAttribute('aria-selected'))).toBe(false);
131
+ host.remove();
132
+ });
133
+
134
+ await it('takes a row appended after connect into the selection', async () => {
135
+ const { el, host } = mount();
136
+ const row = document.createElement('gtk-list-box-row');
137
+ el.appendChild(row);
138
+ await Promise.resolve();
139
+ expect(el.rows).toHaveLength(LABELS.length + 1);
140
+ host.remove();
141
+ });
142
+ });
143
+
144
+ await describe('<gtk-list-box> drives the select vectors with real clicks', async () => {
145
+ for (const vector of BOX_SELECT_VECTORS) {
146
+ await it(vector.rule, async () => {
147
+ const attrs: Record<string, string> = { 'selection-mode': vector.mode };
148
+ if (vector.step.modify === true) attrs['activate-on-single-click'] = 'false';
149
+ const rowAttrs = rowAttrsFor(vector.length, vector.step.selectable === false ? vector.position : null);
150
+ const { el, host } = mount(attrs, rowAttrs, vector.length);
151
+ openOn(el, vector.selection, vector.anchor);
152
+ if (vector.mode === 'browse') {
153
+ // `browse` refuses `unselect_all` (:986-987), so its starting selection is
154
+ // reached by selecting the one row and then another.
155
+ el.selectRow(el.rows[vector.selection[0] ?? 0]!);
156
+ }
157
+ await Promise.resolve();
158
+ // A position the box does not have is the C's `row == NULL`: the press landed
159
+ // between rows or past the last one, `active_row` stays NULL and the release
160
+ // does nothing at all (gtklistbox.c:1911-1913, :1953-1954). A browser puts
161
+ // that click on the box, which is the DOM's spelling of "in no row" — clicking
162
+ // a clamped row instead would have tested a different click altogether.
163
+ const inBox = vector.position >= 0 && vector.position < vector.length;
164
+ click(inBox ? rowsOf(el)[vector.position]! : el, {
165
+ ctrlKey: vector.step.modify === true,
166
+ shiftKey: vector.step.extend === true,
167
+ });
168
+ expect(el.selectedRows).toStrictEqual(vector.expected.selection);
169
+ host.remove();
170
+ });
171
+ }
172
+ });
173
+
174
+ await describe('<gtk-list-box> select_all / unselect_all refusals', async () => {
175
+ for (const vector of BOX_ALL_VECTORS) {
176
+ await it(vector.rule, async () => {
177
+ const { el, host } = mount({ 'selection-mode': vector.mode }, [], vector.length);
178
+ if (vector.op === 'select-all') {
179
+ el.selectAll();
180
+ expect(el.selectedRows).toStrictEqual(vector.expected ?? []);
181
+ } else {
182
+ el.selectRow(el.rows[0] ?? null);
183
+ el.selectAll();
184
+ el.unselectAll();
185
+ // A refusal leaves the rows selected; a change clears them. The modes that
186
+ // refuse are the ones `select_all` never filled in the first place, so both
187
+ // halves are read off the SAME fact the C reads it from.
188
+ expect(el.selectedRows.length > 0).toBe(vector.mode !== 'multiple');
189
+ }
190
+ host.remove();
191
+ });
192
+ }
193
+ });
194
+
195
+ await describe('<gtk-list-box> drives the programmatic row vectors', async () => {
196
+ for (const vector of BOX_ROW_VECTORS) {
197
+ await it(vector.rule, async () => {
198
+ const rowAttrs = rowAttrsFor(vector.length, vector.step.selectable === false ? vector.position : null);
199
+ // `multiple` to BUILD the selection and the vector's own mode afterwards:
200
+ // `gtk_list_box_set_selection_mode` notifies and updates the rows but does
201
+ // NOT prune what is selected (gtklistbox.c:1219-1225), so a single-mode box
202
+ // holding two rows is a state GTK itself can be in, and one the element can.
203
+ const { el, host } = mount({ 'selection-mode': 'multiple' }, rowAttrs, vector.length);
204
+ openOn(el, vector.selection, vector.anchor);
205
+ await Promise.resolve();
206
+ el.setAttribute('selection-mode', vector.mode);
207
+ await Promise.resolve();
208
+
209
+ const target = vector.position === null ? null : el.rows[vector.position];
210
+ if (vector.op === 'select') el.selectRow(target);
211
+ else if (target !== null) el.unselectRow(target);
212
+ expect(el.selectedRows).toStrictEqual(vector.expected.selection);
213
+ host.remove();
214
+ });
215
+ }
216
+ });
217
+
218
+ await describe('<gtk-list-box> tab-behavior', async () => {
219
+ await it('gives every activatable row its own tab stop by default', () => {
220
+ const { el, host } = mount();
221
+ expect(rowsOf(el).map((row) => row.tabIndex)).toStrictEqual([0, 0, 0, 0, 0]);
222
+ host.remove();
223
+ });
224
+
225
+ await it('hands out one tab stop for the whole box under item and cell', () => {
226
+ const { el, host } = mount({ 'tab-behavior': 'item' });
227
+ expect(rowsOf(el).map((row) => row.tabIndex)).toStrictEqual([0, -1, -1, -1, -1]);
228
+ el.tabBehavior = 'cell';
229
+ expect(rowsOf(el).map((row) => row.tabIndex)).toStrictEqual([0, -1, -1, -1, -1]);
230
+ host.remove();
231
+ });
232
+
233
+ await it('puts the roving tab stop on the first selected row, and moves it on selection', () => {
234
+ const { el, host } = mount({ 'tab-behavior': 'item' });
235
+ el.selectRow(el.rows[3]!);
236
+ expect(rowsOf(el).map((row) => row.tabIndex)).toStrictEqual([-1, -1, -1, 0, -1]);
237
+ host.remove();
238
+ });
239
+ });
240
+
241
+ await describe('<gtk-list-box> activation', async () => {
242
+ await it('activates on one click by default and emits row-activated and activate', async () => {
243
+ const { el, host } = mount();
244
+ let activated = 0;
245
+ let rowActivated = 0;
246
+ el.addEventListener('row-activated', () => {
247
+ rowActivated++;
248
+ });
249
+ rowsOf(el)[2]!.addEventListener('activate', () => {
250
+ activated++;
251
+ });
252
+ click(rowsOf(el)[2]!);
253
+ expect(activated).toBe(1);
254
+ expect(rowActivated).toBe(1);
255
+ host.remove();
256
+ });
257
+
258
+ await it('waits for a double click when activate-on-single-click is off', () => {
259
+ const { el, host } = mount({ 'activate-on-single-click': 'false' });
260
+ let activated = 0;
261
+ rowsOf(el)[2]!.addEventListener('activate', () => {
262
+ activated++;
263
+ });
264
+ click(rowsOf(el)[2]!, { detail: 1 });
265
+ expect(activated).toBe(0);
266
+ click(rowsOf(el)[2]!, { detail: 2 });
267
+ expect(activated).toBe(1);
268
+ host.remove();
269
+ });
270
+
271
+ await it('emits nothing for a row that is not activatable', () => {
272
+ const { el, host } = mount({}, [{}, { activatable: 'false' }]);
273
+ let activated = 0;
274
+ rowsOf(el)[1]!.addEventListener('activate', () => {
275
+ activated++;
276
+ });
277
+ click(rowsOf(el)[1]!);
278
+ expect(activated).toBe(0);
279
+ // The CLICK still selects — activatable is about activation, not selection.
280
+ expect(el.selectedRows).toStrictEqual([1]);
281
+ host.remove();
282
+ });
283
+
284
+ await it('toggles the row on Ctrl+Space, the toggle-cursor-row binding', () => {
285
+ const { el, host } = mount({ 'selection-mode': 'multiple' });
286
+ const row = rowsOf(el)[1]!;
287
+ row.focus();
288
+ key(row, { key: ' ', ctrlKey: true });
289
+ expect(el.selectedRows).toStrictEqual([1]);
290
+ key(row, { key: ' ', ctrlKey: true });
291
+ expect(el.selectedRows).toStrictEqual([]);
292
+ host.remove();
293
+ });
294
+
295
+ await it('emits selected-rows-changed on every change and row-selected once', async () => {
296
+ const { el, host } = mount();
297
+ let changes = 0;
298
+ let rowSelected = 0;
299
+ let notified: number[] = [];
300
+ el.addEventListener('selected-rows-changed', () => {
301
+ changes++;
302
+ });
303
+ el.addEventListener('row-selected', () => {
304
+ rowSelected++;
305
+ });
306
+ el.addEventListener('notify::selected-rows', (event) => {
307
+ notified = (event as CustomEvent<{ selectedRows: number[] }>).detail.selectedRows;
308
+ });
309
+ click(rowsOf(el)[1]!);
310
+ click(rowsOf(el)[2]!);
311
+ expect(changes).toBe(2);
312
+ expect(rowSelected).toBe(2);
313
+ expect(notified).toStrictEqual([2]);
314
+ host.remove();
315
+ });
316
+ });
317
+
318
+ await describe('<gtk-list-box> row-selected names the row the click landed on', async () => {
319
+ await it('and NULL when that row ended up unselected', () => {
320
+ const { el, host } = mount();
321
+ const seen: (HTMLElement | null)[] = [];
322
+ el.addEventListener('row-selected', (event) => {
323
+ seen.push((event as CustomEvent<{ row: HTMLElement | null }>).detail.row);
324
+ });
325
+ click(rowsOf(el)[2]!);
326
+ expect(seen).toStrictEqual([rowsOf(el)[2]!]);
327
+ // Ctrl+click on the SELECTED row in single mode clears it, and the C emits
328
+ // row-selected with NULL there (gtklistbox.c:1826-1828).
329
+ click(rowsOf(el)[2]!, { ctrlKey: true });
330
+ expect(seen[1]).toBe(null);
331
+ expect(el.selectedRows).toStrictEqual([]);
332
+ host.remove();
333
+ });
334
+ });
335
+
336
+ await describe('<gtk-list-box> select_row is the programmatic door, not the click', async () => {
337
+ await it('is idempotent where a click would toggle', () => {
338
+ const { el, host } = mount();
339
+ el.selectRow(el.rows[2]!);
340
+ el.selectRow(el.rows[2]!);
341
+ expect(el.selectedRows).toStrictEqual([2]);
342
+ host.remove();
343
+ });
344
+
345
+ await it('clears everything for a null row and moves the anchor with it', async () => {
346
+ const { el, host } = mount({ 'selection-mode': 'multiple' });
347
+ el.selectRow(el.rows[1]!);
348
+ el.selectRow(el.rows[3]!);
349
+ el.selectRow(null);
350
+ expect(el.selectedRows).toStrictEqual([]);
351
+ host.remove();
352
+ });
353
+
354
+ await it('ignores an unselectable row', () => {
355
+ const { el, host } = mount({}, [{}, { selectable: 'false' }]);
356
+ el.selectRow(el.rows[1]!);
357
+ expect(el.selectedRows).toStrictEqual([]);
358
+ host.remove();
359
+ });
360
+ });
361
+
362
+ await describe('<gtk-list-box-row> on its own', async () => {
363
+ await it('reads activatable and selectable by PRESENCE, defaulting both to true', () => {
364
+ const host = document.createElement('div');
365
+ document.body.appendChild(host);
366
+ const row = document.createElement('gtk-list-box-row') as GtkListBoxRow;
367
+ host.appendChild(row);
368
+ expect(row.activatable).toBe(true);
369
+ expect(row.selectable).toBe(true);
370
+ row.setAttribute('activatable', 'false');
371
+ row.setAttribute('selectable', 'false');
372
+ expect(row.activatable).toBe(false);
373
+ expect(row.selectable).toBe(false);
374
+ host.remove();
375
+ });
376
+
377
+ await it('is the tab stop only while activatable, and answers Enter and Space', () => {
378
+ const host = document.createElement('div');
379
+ document.body.appendChild(host);
380
+ const row = document.createElement('gtk-list-box-row') as GtkListBoxRow;
381
+ host.appendChild(row);
382
+ let activations = 0;
383
+ row.addEventListener('activate', () => {
384
+ activations++;
385
+ });
386
+ expect(row.tabIndex).toBe(0);
387
+ key(row, { key: 'Enter' });
388
+ key(row, { key: ' ' });
389
+ expect(activations).toBe(2);
390
+ // `activatable="false"` alone does NOT stop the class: the row is still
391
+ // SELECTABLE, and the C's rule is `activatable || (selectable && can_select)`
392
+ // (gtklistbox.c:3611-3615). Both flags off is what takes the tab stop away.
393
+ row.setAttribute('activatable', 'false');
394
+ expect(row.classList.contains('activatable')).toBe(true);
395
+ row.setAttribute('selectable', 'false');
396
+ // Not activatable means NOT FOCUSABLE upstream, and `attachRowActivation` removes
397
+ // the attribute rather than writing -1 (row-activation.ts).
398
+ expect(row.hasAttribute('tabindex')).toBe(false);
399
+ key(row, { key: 'Enter' });
400
+ expect(activations).toBe(2);
401
+ host.remove();
402
+ });
403
+
404
+ await it('puts an authored child in the contents node, after connect too', async () => {
405
+ const host = document.createElement('div');
406
+ document.body.appendChild(host);
407
+ const row = document.createElement('gtk-list-box-row') as GtkListBoxRow;
408
+ const first = document.createElement('span');
409
+ first.textContent = 'first';
410
+ row.appendChild(first);
411
+ host.appendChild(row);
412
+ expect(row.child).toBe(first);
413
+ const late = document.createElement('span');
414
+ late.textContent = 'late';
415
+ row.appendChild(late);
416
+ await Promise.resolve();
417
+ expect(row.child).toBe(first);
418
+ expect(row.textContent).toContain('late');
419
+ host.remove();
420
+ });
421
+ });
422
+ };
@@ -0,0 +1,203 @@
1
+ // DOM-level tests for <gtk-list-view>: the two model doors, the factory, the
2
+ // click-and-modifier selection, and the one thing a pure core test cannot reach — that a
3
+ // splice leaves the OTHER rows standing, which is what the whole items-changed path
4
+ // exists for.
5
+ //
6
+ // The THIRD model door, the string-list child a `.blp` authors (ADR 0072), has no case
7
+ // here for the reason `<gtk-drop-down>`'s has none: the tag it is written as is markup
8
+ // the shared-tree builder emits and no `customElements.define` registers, so a spec that
9
+ // constructed one would be asserting against an inert `HTMLElement`. It is covered where
10
+ // that markup is produced, in `value-lists.spec.ts`.
11
+ import { describe, expect, it } from '@gjsify/unit';
12
+
13
+ import type { AdwListItemContext } from '@gjsify/adwaita-core';
14
+ import { LIST_VIEW_SELECT_VECTORS } from '@gjsify/adwaita-core/conformance';
15
+
16
+ import type { GtkListView } from './elements/gtk-list-view.js';
17
+
18
+ function mount(attrs: Record<string, string> = {}): { el: GtkListView; host: HTMLElement } {
19
+ const host = document.createElement('div');
20
+ document.body.appendChild(host);
21
+ const el = document.createElement('gtk-list-view') as GtkListView;
22
+ for (const [name, value] of Object.entries(attrs)) el.setAttribute(name, value);
23
+ host.appendChild(el);
24
+ return { el, host };
25
+ }
26
+
27
+ const rows = (el: GtkListView): HTMLElement[] => [...el.querySelectorAll<HTMLElement>('.adw-list-view-row')];
28
+ const labels = (el: GtkListView): string[] => rows(el).map((row) => row.textContent ?? '');
29
+
30
+ const click = (row: HTMLElement, init: MouseEventInit = {}) =>
31
+ row.dispatchEvent(new MouseEvent('click', { bubbles: true, ...init }));
32
+
33
+ export const GtkListViewTest = async () => {
34
+ await describe('<gtk-list-view> model', async () => {
35
+ await it('draws one row per item from the JSON attribute', () => {
36
+ const { el, host } = mount({ model: '["Alpha","Beta","Gamma"]' });
37
+ expect(labels(el)).toStrictEqual(['Alpha', 'Beta', 'Gamma']);
38
+ expect(el.getAttribute('role')).toBe('listbox');
39
+ host.remove();
40
+ });
41
+
42
+ await it('takes the property too, in every item spelling the port accepts', () => {
43
+ const { el, host } = mount();
44
+ el.model = ['Alpha', { label: 'Beta' }, { value: 'c', label: 'Gamma' }];
45
+ expect(labels(el)).toStrictEqual(['Alpha', 'Beta', 'Gamma']);
46
+ expect(el.model[2]!.value).toBe('c');
47
+ host.remove();
48
+ });
49
+
50
+ await it('survives a malformed attribute instead of failing to upgrade', () => {
51
+ const { el, host } = mount({ model: '{' });
52
+ expect(labels(el)).toStrictEqual([]);
53
+ host.remove();
54
+ });
55
+
56
+ await it('SPLICES: an appended item leaves the existing row nodes identical', () => {
57
+ const { el, host } = mount({ model: '["Alpha","Beta"]' });
58
+ const before = rows(el);
59
+ el.model = ['Alpha', 'Beta', 'Gamma'];
60
+ const after = rows(el);
61
+ expect(after.length).toBe(3);
62
+ expect(after[0]).toBe(before[0]);
63
+ expect(after[1]).toBe(before[1]);
64
+ host.remove();
65
+ });
66
+ });
67
+
68
+ await describe('<gtk-list-view> factory', async () => {
69
+ await it('draws the item label when no factory was set', () => {
70
+ const { el, host } = mount({ model: '["Alpha"]' });
71
+ expect(el.querySelector('.adw-list-item-label')?.textContent).toBe('Alpha');
72
+ host.remove();
73
+ });
74
+
75
+ await it('hands a factory the item, its position and its selected state', () => {
76
+ const { el, host } = mount({ model: '["Alpha","Beta"]' });
77
+ const seen: AdwListItemContext[] = [];
78
+ el.factory = (context) => {
79
+ seen.push(context);
80
+ const node = document.createElement('b');
81
+ node.textContent = `${context.position}:${context.item.label}`;
82
+ return node;
83
+ };
84
+ expect(labels(el)).toStrictEqual(['0:Alpha', '1:Beta']);
85
+ expect(seen[1]!.selected).toBe(false);
86
+ host.remove();
87
+ });
88
+ });
89
+
90
+ await describe('<gtk-list-view> drives the selection vectors with real clicks', async () => {
91
+ // The core suite drives the same rows through `ListViewState`; this is the half
92
+ // that proves the ELEMENT reads the two modifiers off the event and routes them to
93
+ // it, which no pure test of the rules can reach.
94
+ for (const vector of LIST_VIEW_SELECT_VECTORS) {
95
+ await it(vector.rule, () => {
96
+ const { el, host } = mount({
97
+ model: JSON.stringify(Array.from({ length: vector.length }, (_, i) => `${i}`)),
98
+ 'selection-mode': vector.mode,
99
+ });
100
+ for (const step of vector.clicks) {
101
+ const row = rows(el)[step.position];
102
+ if (row !== undefined) click(row, { ctrlKey: step.modify, shiftKey: step.extend });
103
+ }
104
+ expect(el.selected).toStrictEqual([...vector.selection]);
105
+ host.remove();
106
+ });
107
+ }
108
+ });
109
+
110
+ await describe('<gtk-list-view> selection', async () => {
111
+ await it('selects on click and notifies once per change', () => {
112
+ const { el, host } = mount({ model: '["Alpha","Beta","Gamma"]' });
113
+ const notified: number[][] = [];
114
+ el.addEventListener('notify::selected', (event) => {
115
+ notified.push((event as CustomEvent).detail.selected as number[]);
116
+ });
117
+ click(rows(el)[1]!);
118
+ click(rows(el)[1]!);
119
+ expect(el.selected).toStrictEqual([1]);
120
+ expect(notified).toStrictEqual([[1]]);
121
+ expect(rows(el)[1]!.getAttribute('aria-selected')).toBe('true');
122
+ host.remove();
123
+ });
124
+
125
+ await it('Ctrl and Shift add and extend in multiple mode', () => {
126
+ const { el, host } = mount({ model: '["A","B","C","D"]', 'selection-mode': 'multiple' });
127
+ expect(el.getAttribute('aria-multiselectable')).toBe('true');
128
+ click(rows(el)[0]!);
129
+ click(rows(el)[2]!, { ctrlKey: true });
130
+ expect(el.selected).toStrictEqual([0, 2]);
131
+ click(rows(el)[0]!);
132
+ click(rows(el)[2]!, { shiftKey: true });
133
+ expect(el.selected).toStrictEqual([0, 1, 2]);
134
+ host.remove();
135
+ });
136
+
137
+ await it('selects nothing at all in none mode, and says so to assistive technology', () => {
138
+ const { el, host } = mount({ model: '["A","B"]', 'selection-mode': 'none' });
139
+ click(rows(el)[1]!);
140
+ expect(el.selected).toStrictEqual([]);
141
+ expect(el.getAttribute('role')).toBe('list');
142
+ expect(rows(el)[1]!.getAttribute('role')).toBe('listitem');
143
+ host.remove();
144
+ });
145
+
146
+ await it('drops a selected position the replaced model no longer has', () => {
147
+ const { el, host } = mount({ model: '["A","B","C"]' });
148
+ click(rows(el)[2]!);
149
+ el.model = ['A'];
150
+ expect(el.selected).toStrictEqual([]);
151
+ host.remove();
152
+ });
153
+ });
154
+
155
+ await describe('<gtk-list-view> activation and keyboard', async () => {
156
+ await it('activates on double click, and on a single one only when asked', () => {
157
+ const { el, host } = mount({ model: '["A","B"]' });
158
+ const activated: number[] = [];
159
+ el.addEventListener('activate', (event) => {
160
+ activated.push((event as CustomEvent).detail.position as number);
161
+ });
162
+ click(rows(el)[1]!);
163
+ expect(activated).toStrictEqual([]);
164
+ rows(el)[1]!.dispatchEvent(new MouseEvent('dblclick', { bubbles: true }));
165
+ el.singleClickActivate = true;
166
+ click(rows(el)[0]!);
167
+ expect(activated).toStrictEqual([1, 0]);
168
+ host.remove();
169
+ });
170
+
171
+ await it('hands out a roving tabindex for tab-behavior item, and every stop for all', () => {
172
+ const { el, host } = mount({ model: '["A","B","C"]', 'tab-behavior': 'item' });
173
+ expect(rows(el).map((row) => row.tabIndex)).toStrictEqual([0, -1, -1]);
174
+ click(rows(el)[2]!);
175
+ expect(rows(el).map((row) => row.tabIndex)).toStrictEqual([-1, -1, 0]);
176
+ el.tabBehavior = 'all';
177
+ expect(rows(el).map((row) => row.tabIndex)).toStrictEqual([0, 0, 0]);
178
+ host.remove();
179
+ });
180
+
181
+ await it('moves the selection with ArrowDown, which is what the roving tabindex needs', () => {
182
+ const { el, host } = mount({ model: '["A","B","C"]', 'tab-behavior': 'item' });
183
+ click(rows(el)[0]!);
184
+ // Dispatched ON THE ROW: `attachRovingFocus` listens on the host and finds the
185
+ // item the key came from, so a press with the host itself as the target is one
186
+ // no user can produce and the helper correctly ignores.
187
+ rows(el)[0]!.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true }));
188
+ expect(el.selected).toStrictEqual([1]);
189
+ host.remove();
190
+ });
191
+ });
192
+
193
+ await describe('<gtk-list-view> separators', async () => {
194
+ await it('carries the .separators class only while show-separators is set', () => {
195
+ const { el, host } = mount({ model: '["A","B"]' });
196
+ expect(el.classList.contains('separators')).toBe(false);
197
+ el.showSeparators = true;
198
+ expect(el.classList.contains('separators')).toBe(true);
199
+ expect(getComputedStyle(rows(el)[0]!).borderBottomWidth).toBe('1px');
200
+ host.remove();
201
+ });
202
+ });
203
+ };