@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,546 @@
1
+ // DOM-level tests for <gtk-notebook>. The widget is four separable things and each gets
2
+ // its own describe: WHICH PAGE is current (the guards in `set_current_page` and
3
+ // `real_switch_page`), the PAGE DESCRIPTOR (a GtkNotebookPage is a GObject, so its
4
+ // properties are attributes on the child), THE TAB STRIP (which the notebook builds
5
+ // itself, and what `tab-pos` does to it) and the NOTIFICATION contract.
6
+ import { describe, expect, it } from '@gjsify/unit';
7
+
8
+ import type { GtkNotebook } from './elements/gtk-notebook.js';
9
+ import { buildSharedTree } from './shared-tree-builder.js';
10
+
11
+ interface Page {
12
+ label?: string;
13
+ menu?: string;
14
+ fill?: boolean;
15
+ expand?: boolean;
16
+ reorderable?: boolean;
17
+ detachable?: boolean;
18
+ hidden?: boolean;
19
+ body?: string;
20
+ }
21
+
22
+ /** A notebook with its pages, `<gtk-notebook>` already upgraded and rendered. */
23
+ function mount(pages: Page[] = [], attrs: Record<string, string> = {}): { el: GtkNotebook; host: HTMLElement } {
24
+ const host = document.createElement('div');
25
+ document.body.appendChild(host);
26
+ const el = document.createElement('gtk-notebook') as GtkNotebook;
27
+ for (const [name, value] of Object.entries(attrs)) el.setAttribute(name, value);
28
+ for (const page of pages) {
29
+ const child = document.createElement('div');
30
+ if (page.label !== undefined) child.setAttribute('tab-label', page.label);
31
+ if (page.menu !== undefined) child.setAttribute('menu-label', page.menu);
32
+ if (page.fill === false) child.setAttribute('tab-fill', 'false');
33
+ if (page.expand) child.setAttribute('tab-expand', '');
34
+ if (page.reorderable) child.setAttribute('reorderable', '');
35
+ if (page.detachable) child.setAttribute('detachable', '');
36
+ if (page.hidden) child.setAttribute('hidden', '');
37
+ child.textContent = page.body ?? page.label ?? 'page';
38
+ el.appendChild(child);
39
+ }
40
+ host.appendChild(el);
41
+ return { el, host };
42
+ }
43
+
44
+ /** The tab buttons the render pass produced, as the strip holds them. */
45
+ function tabs(el: GtkNotebook): HTMLButtonElement[] {
46
+ return Array.from(el.querySelectorAll('.adw-notebook-tab')) as HTMLButtonElement[];
47
+ }
48
+
49
+ /** The children in DOM order, with `visible-page` marked, as the render pass sees them. */
50
+ function marks(el: GtkNotebook): string[] {
51
+ return (Array.from(el.children) as HTMLElement[])
52
+ .filter((child) => child !== el.querySelector('.adw-notebook-header'))
53
+ .map(
54
+ (child) =>
55
+ `${child.getAttribute('tab-label') ?? '?'}${child.classList.contains('visible-page') ? ':visible' : ''}`,
56
+ );
57
+ }
58
+
59
+ export const GtkNotebookTest = async () => {
60
+ await describe('<gtk-notebook> defaults', async () => {
61
+ await it('shows the first page and marks only that one visible', () => {
62
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two' }]);
63
+ expect(el.nPages).toBe(2);
64
+ expect(el.page).toBe(0);
65
+ expect(marks(el)).toStrictEqual(['One:visible', 'Two']);
66
+ host.remove();
67
+ });
68
+
69
+ await it('labels a page with no tab-label "Page N", counting from one', () => {
70
+ // gtk_notebook_update_labels (gtknotebook.c:4353-4373): `g_snprintf (string,
71
+ // …, _("Page %u"), page_num++)` and `text = page->tab_text ?: string`, so the
72
+ // number is the FALLBACK rather than a reason to have no tab.
73
+ const { el, host } = mount([{ label: 'One' }, {}, {}]);
74
+ expect(tabs(el).map((tab) => tab.textContent)).toStrictEqual(['One', 'Page 2', 'Page 3']);
75
+ host.remove();
76
+ });
77
+
78
+ await it('counts the numbering over the VISIBLE pages only', () => {
79
+ // `gtk_notebook_search_page (…, STEP_NEXT, FALSE)` walks the visible pages, so
80
+ // a hidden one takes no number and does not shift the ones after it
81
+ // (gtknotebook.c:4361-4364).
82
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two', hidden: true }, {}]);
83
+ expect(tabs(el).map((tab) => tab.textContent)).toStrictEqual(['One', 'Two', 'Page 2']);
84
+ expect(tabs(el)[1].hidden).toBe(true);
85
+ host.remove();
86
+ });
87
+
88
+ await it('a hidden page does not make the observer spin', async () => {
89
+ // The regression that HANGS the whole browser suite rather than failing one
90
+ // test: `setAttribute` queues a mutation record even when the value is
91
+ // unchanged, and the render pass writes `hidden` on the tabs, so an observer
92
+ // that watched the whole subtree fed its own writes straight back in. The
93
+ // await is the assertion — a spin starves the macrotask queue and the run
94
+ // never finishes, which is how the bug presented.
95
+ const { el, host } = mount([{ label: 'One', hidden: true }, { label: 'Two' }]);
96
+ await new Promise((resolve) => setTimeout(resolve, 50));
97
+ expect(tabs(el).map((tab) => tab.hidden)).toStrictEqual([true, false]);
98
+ expect(el.page).toBe(1);
99
+ host.remove();
100
+ });
101
+
102
+ await it('gives the current tab the checked state and aria-selected', () => {
103
+ // `gtk_widget_set_state_flags (page->tab_widget, GTK_STATE_FLAG_CHECKED)` and
104
+ // the accessible SELECTED that follows it (gtknotebook.c:5466, :5472-5474).
105
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two' }]);
106
+ expect(tabs(el).map((tab) => tab.classList.contains('active'))).toStrictEqual([true, false]);
107
+ expect(tabs(el).map((tab) => tab.getAttribute('aria-selected'))).toStrictEqual(['true', 'false']);
108
+ host.remove();
109
+ });
110
+
111
+ await it('reports the tab position as top, and reflects it', () => {
112
+ const { el, host } = mount([{ label: 'One' }]);
113
+ expect(el.tabPos).toBe('top');
114
+ expect(el.getAttribute('data-tab-pos')).toBe('top');
115
+ host.remove();
116
+ });
117
+
118
+ await it('swaps left and right under RTL, which is what data-tab-pos is for', () => {
119
+ // get_effective_tab_pos (gtknotebook.c:1741-1757) swaps only the two horizontal
120
+ // edges; `top` and `bottom` are unaffected.
121
+ const rtl = mount([{ label: 'One' }], { 'tab-pos': 'left' });
122
+ rtl.el.setAttribute('dir', 'rtl');
123
+ rtl.el.refresh();
124
+ expect(rtl.el.getAttribute('data-tab-pos')).toBe('right');
125
+ rtl.host.remove();
126
+
127
+ const ltr = mount([{ label: 'One' }], { 'tab-pos': 'bottom' });
128
+ ltr.el.setAttribute('dir', 'rtl');
129
+ ltr.el.refresh();
130
+ expect(ltr.el.getAttribute('data-tab-pos')).toBe('bottom');
131
+ ltr.host.remove();
132
+ });
133
+ });
134
+
135
+ await describe('<gtk-notebook> switching', async () => {
136
+ await it('a negative page means the LAST page, not "none"', () => {
137
+ // `if (page_num < 0) page_num = g_list_length (notebook->children) - 1`
138
+ // (gtknotebook.c:6054).
139
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two' }, { label: 'Three' }]);
140
+ expect(el.setCurrentPage(-1)).toBe(true);
141
+ expect(el.page).toBe(2);
142
+ host.remove();
143
+ });
144
+
145
+ await it('an out-of-range page does nothing at all', () => {
146
+ // `list = g_list_nth (…); if (list) gtk_notebook_switch_page (…)`
147
+ // (gtknotebook.c:6056-6058) — the guard, not a clamp and not a throw.
148
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two' }]);
149
+ expect(el.setCurrentPage(7)).toBe(false);
150
+ expect(el.page).toBe(0);
151
+ host.remove();
152
+ });
153
+
154
+ await it('refuses a page whose child is hidden', () => {
155
+ // `notebook->cur_page == page || !gtk_widget_get_visible (GTK_WIDGET (child))`
156
+ // is ONE condition (gtknotebook.c:5444), so a hidden page is not switchable
157
+ // however it is asked for.
158
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two', hidden: true }]);
159
+ expect(el.setCurrentPage(1)).toBe(false);
160
+ expect(el.page).toBe(0);
161
+ host.remove();
162
+ });
163
+
164
+ await it('switching to the current page is not a change', () => {
165
+ // The other half of :5444 — the guard returns before anything is notified.
166
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two' }]);
167
+ let switches = 0;
168
+ el.addEventListener('switch-page', () => (switches += 1));
169
+ expect(el.setCurrentPage(0)).toBe(false);
170
+ expect(switches).toBe(0);
171
+ host.remove();
172
+ });
173
+
174
+ await it('next and prev step OVER a hidden page', () => {
175
+ // `gtk_notebook_search_page (…, STEP_NEXT, TRUE)` — the TRUE is `find_visible`
176
+ // (gtknotebook.c:6078), which is why a hidden page does not end the walk.
177
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two', hidden: true }, { label: 'Three' }]);
178
+ expect(el.nextPage()).toBe(true);
179
+ expect(el.page).toBe(2);
180
+ expect(el.prevPage()).toBe(true);
181
+ expect(el.page).toBe(0);
182
+ host.remove();
183
+ });
184
+
185
+ await it('off either end next and prev do nothing', () => {
186
+ // `if (!list) return` after the search (gtknotebook.c:6079-6080).
187
+ const { el, host } = mount([{ label: 'One' }]);
188
+ expect(el.prevPage()).toBe(false);
189
+ expect(el.nextPage()).toBe(false);
190
+ expect(el.page).toBe(0);
191
+ host.remove();
192
+ });
193
+
194
+ await it('a click on a tab switches to that page', () => {
195
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two' }]);
196
+ tabs(el)[1].click();
197
+ expect(el.page).toBe(1);
198
+ expect(el.currentPage?.getAttribute('tab-label')).toBe('Two');
199
+ host.remove();
200
+ });
201
+
202
+ await it('reads page LIVE, and leaves the attribute as the input it was', () => {
203
+ // `get_property` returns `gtk_notebook_get_current_page (notebook)`
204
+ // (gtknotebook.c:1977-1979) rather than the value last written, so a switch
205
+ // that reached the notebook by clicking a tab is visible through the property
206
+ // even though nothing wrote the attribute.
207
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two' }]);
208
+ el.setAttribute('page', '1');
209
+ expect(el.page).toBe(1);
210
+ tabs(el)[0].click();
211
+ expect(el.page).toBe(0);
212
+ expect(el.getAttribute('page')).toBe('1');
213
+ host.remove();
214
+ });
215
+
216
+ await it('switch-page is emitted BEFORE the switch, with the page switched TO', () => {
217
+ // `gtk_notebook_switch_page` emits and the class handler `real_switch_page`
218
+ // performs (gtknotebook.c:5518-5523), so a handler that vetoes by not drawing
219
+ // is the documented use — the page still reads the OLD one at that moment.
220
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two' }]);
221
+ const seen: number[] = [];
222
+ el.addEventListener('switch-page', (e) => seen.push((e as CustomEvent).detail.page, el.page));
223
+ el.setCurrentPage(1);
224
+ expect(seen).toStrictEqual([1, 0]);
225
+ expect(el.page).toBe(1);
226
+ host.remove();
227
+ });
228
+
229
+ await it('a handler that vetoes by not drawing leaves the page alone', async () => {
230
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two' }]);
231
+ el.addEventListener('switch-page', (e) => e.preventDefault());
232
+ el.setCurrentPage(1);
233
+ expect(el.page).toBe(0);
234
+ host.remove();
235
+ });
236
+ });
237
+
238
+ await describe('<gtk-notebook> the page descriptor', async () => {
239
+ await it('carries the five scalar page properties as attributes', () => {
240
+ // A GtkNotebookPage is a GObject (gtknotebook.c:423), so its properties are the
241
+ // attributes on the child and `getPage()` hands back the descriptor.
242
+ const { el, host } = mount([{ label: 'One', menu: 'First', reorderable: true, detachable: true }]);
243
+ const info = el.getPage(el.children[1] as HTMLElement);
244
+ expect(info?.tabLabel).toBe('One');
245
+ expect(info?.menuLabel).toBe('First');
246
+ expect(info?.reorderable).toBe(true);
247
+ expect(info?.detachable).toBe(true);
248
+ host.remove();
249
+ });
250
+
251
+ await it('tab-fill defaults TRUE and tab-expand FALSE', () => {
252
+ // `g_param_spec_boolean ("tab-expand", …, FALSE, …)` and `("tab-fill", …,
253
+ // TRUE, …)` (gtknotebook.c:667-681), and `page->fill = TRUE` in the page's
254
+ // `init` (gtknotebook.c:430).
255
+ const { el, host } = mount([
256
+ { label: 'One' },
257
+ { label: 'Two', fill: false },
258
+ { label: 'Three', expand: true },
259
+ ]);
260
+ expect(el.getPage(el.children[1] as HTMLElement)?.tabFill).toBe(true);
261
+ expect(el.getPage(el.children[2] as HTMLElement)?.tabFill).toBe(false);
262
+ expect(el.getPage(el.children[3] as HTMLElement)?.tabExpand).toBe(true);
263
+ expect(tabs(el).map((tab) => tab.classList.contains('fill'))).toStrictEqual([true, false, true]);
264
+ expect(tabs(el).map((tab) => tab.classList.contains('expand'))).toStrictEqual([false, false, true]);
265
+ host.remove();
266
+ });
267
+
268
+ await it('maps the child index to a page number, both ways', () => {
269
+ // `gtk_notebook_page_num` (gtknotebook.c:6006) and
270
+ // `gtk_notebook_get_nth_page` (gtknotebook.c:5956), the latter counting from
271
+ // the END for a negative index.
272
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two' }, { label: 'Three' }]);
273
+ const pages = Array.from(el.children).filter((child) => child !== el.querySelector('.adw-notebook-header'));
274
+ expect(el.pageNum(pages[2] as HTMLElement)).toBe(2);
275
+ expect(el.getNthPage(-1)).toBe(pages[2]);
276
+ expect(el.getNthPage(9)).toBeNull();
277
+ host.remove();
278
+ });
279
+
280
+ await it('appends a page, which becomes the current one only if there was none', () => {
281
+ // `if (!gtk_notebook_has_current_page (notebook)) gtk_notebook_switch_page (…)`
282
+ // (gtknotebook.c:4131-4134).
283
+ const empty = mount([]);
284
+ const added = document.createElement('div');
285
+ added.setAttribute('tab-label', 'One');
286
+ empty.el.appendPage(added);
287
+ expect(empty.el.page).toBe(0);
288
+ empty.host.remove();
289
+
290
+ const { el, host } = mount([{ label: 'One' }]);
291
+ const second = document.createElement('div');
292
+ second.setAttribute('tab-label', 'Two');
293
+ el.appendPage(second);
294
+ expect(el.page).toBe(0);
295
+ expect(el.nPages).toBe(2);
296
+ host.remove();
297
+ });
298
+
299
+ await it('removing the current page hands over to the NEXT one, not the first', () => {
300
+ // C takes `search_page (…, STEP_NEXT)` and falls back to `STEP_PREV` at the end
301
+ // (gtknotebook.c:4281-4290) — so from the middle it is the page that follows.
302
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two' }, { label: 'Three' }]);
303
+ const pages = Array.from(el.children).filter((child) => child !== el.querySelector('.adw-notebook-header'));
304
+ el.setCurrentPage(1);
305
+ expect(el.removePage(pages[1] as HTMLElement)).toBe(true);
306
+ expect(el.page).toBe(1);
307
+ expect(el.getNthPage(1)?.getAttribute('tab-label')).toBe('Three');
308
+ host.remove();
309
+ });
310
+
311
+ await it('removing the LAST current page falls back backwards', () => {
312
+ // The `STEP_PREV` half of gtknotebook.c:4280-4282.
313
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two' }]);
314
+ const pages = Array.from(el.children).filter((child) => child !== el.querySelector('.adw-notebook-header'));
315
+ el.setCurrentPage(1);
316
+ el.removePage(pages[1] as HTMLElement);
317
+ expect(el.page).toBe(0);
318
+ expect(el.nPages).toBe(1);
319
+ host.remove();
320
+ });
321
+ });
322
+
323
+ await describe('<gtk-notebook> the tab strip', async () => {
324
+ await it("builds the strip as header > tabs > tab, libadwaita's own tree", () => {
325
+ // refs/libadwaita/src/stylesheet/widgets/_notebook.scss:4-10.
326
+ const { el, host } = mount([{ label: 'One' }]);
327
+ const header = el.querySelector('.adw-notebook-header') as HTMLElement;
328
+ const strip = el.querySelector('.adw-notebook-tabs') as HTMLElement;
329
+ expect(header.parentElement).toBe(el);
330
+ expect(strip.parentElement).toBe(header);
331
+ expect(tabs(el)[0].parentElement).toBe(strip);
332
+ expect(strip.getAttribute('role')).toBe('tablist');
333
+ expect(tabs(el)[0].getAttribute('role')).toBe('tab');
334
+ host.remove();
335
+ });
336
+
337
+ await it('takes its tabs away with show-tabs, and puts them back', () => {
338
+ // `gtk_widget_set_visible (header_widget, show_tabs)` (gtknotebook.c:5467).
339
+ const { el, host } = mount([{ label: 'One' }]);
340
+ const header = el.querySelector('.adw-notebook-header') as HTMLElement;
341
+ expect(header.hidden).toBe(false);
342
+ el.showTabs = false;
343
+ expect(header.hidden).toBe(true);
344
+ expect(el.nPages).toBe(1);
345
+ el.showTabs = true;
346
+ expect(header.hidden).toBe(false);
347
+ host.remove();
348
+ });
349
+
350
+ await it('show-border is the .frame class, because in C it is a CSS class', () => {
351
+ // `gtk_notebook_set_show_border` adds `.frame` and clearing the property removes
352
+ // it (gtknotebook.c:6134-6146); it defaults TRUE (gtknotebook.c:1505). The
353
+ // property is still an attribute — the CLASS is what the border hangs off, which
354
+ // is the distinction a boolean attribute would have lost.
355
+ const { el, host } = mount([{ label: 'One' }]);
356
+ expect(el.showBorder).toBe(true);
357
+ expect(el.classList.contains('frame')).toBe(true);
358
+ el.showBorder = false;
359
+ expect(el.classList.contains('frame')).toBe(false);
360
+ expect(el.showBorder).toBe(false);
361
+ expect(el.getAttribute('show-border')).toBe('false');
362
+ host.remove();
363
+ });
364
+
365
+ await it('marks the page area, which is a GtkStack inside the C', () => {
366
+ // `gtk_stack_set_visible_child (…, cur_page->child)` (gtknotebook.c:5476) — one
367
+ // visible child, and libadwaita's `> stack:not(:only-child)` page background
368
+ // (_notebook.scss:213-215) is what `.adw-notebook-page` is styled as.
369
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two' }]);
370
+ const area = Array.from(el.children).filter((child) => child !== el.querySelector('.adw-notebook-header'));
371
+ expect(area.every((child) => child.classList.contains('adw-notebook-page'))).toBe(true);
372
+ expect(area.map((child) => child.classList.contains('visible-page'))).toStrictEqual([true, false]);
373
+ host.remove();
374
+ });
375
+ });
376
+
377
+ await describe('<gtk-notebook> the popup menu', async () => {
378
+ await it('opens a listbox of the pages on contextmenu, when enable-popup is set', async () => {
379
+ // `gtk_notebook_popup_enable` builds a GtkPopoverMenu of the pages
380
+ // (gtknotebook.c:6420-6462) and a right-click pops it up (gtknotebook.c:2684).
381
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two', menu: 'The second' }]);
382
+ el.enablePopup = true;
383
+ el.dispatchEvent(new MouseEvent('contextmenu', { bubbles: true, cancelable: true }));
384
+ await new Promise((resolve) => setTimeout(resolve, 0));
385
+ const menu = el.querySelector('.adw-notebook-menu') as HTMLElement;
386
+ const items = Array.from(menu.querySelectorAll('.adw-notebook-menu-item')) as HTMLElement[];
387
+ expect(menu.getAttribute('role')).toBe('listbox');
388
+ expect(items.map((item) => item.textContent)).toStrictEqual(['One', 'The second']);
389
+ expect(items[0].getAttribute('aria-selected')).toBe('true');
390
+ host.remove();
391
+ });
392
+
393
+ await it("falls back to the tab label, and hides a hidden page's item", async () => {
394
+ // `gtk_notebook_menu_item_create` builds the label from `menu_label`, falls
395
+ // back to the tab label when it is a GtkLabel, and hides the item for a page
396
+ // whose child is hidden (gtknotebook.c:5624-5649).
397
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two', hidden: true }, {}]);
398
+ el.enablePopup = true;
399
+ el.dispatchEvent(new MouseEvent('contextmenu', { bubbles: true, cancelable: true }));
400
+ await new Promise((resolve) => setTimeout(resolve, 0));
401
+ const items = Array.from(el.querySelectorAll('.adw-notebook-menu-item')) as HTMLElement[];
402
+ expect(items.map((item) => item.textContent)).toStrictEqual(['One', 'Two', 'Page 2']);
403
+ expect(items[1].hidden).toBe(true);
404
+ host.remove();
405
+ });
406
+
407
+ await it('does nothing at all while enable-popup is off', () => {
408
+ const { el, host } = mount([{ label: 'One' }]);
409
+ expect(el.enablePopup).toBe(false);
410
+ const event = new MouseEvent('contextmenu', { bubbles: true, cancelable: true });
411
+ el.dispatchEvent(event);
412
+ expect(el.querySelector('.adw-notebook-menu')).toBeNull();
413
+ expect(event.defaultPrevented).toBe(false);
414
+ host.remove();
415
+ });
416
+
417
+ await it('closes on disconnect, which is where the menu goes', () => {
418
+ const { el, host } = mount([{ label: 'One' }]);
419
+ el.enablePopup = true;
420
+ el.dispatchEvent(new MouseEvent('contextmenu', { bubbles: true, cancelable: true }));
421
+ expect(el.querySelector('.adw-notebook-menu')).not.toBeNull();
422
+ host.remove();
423
+ expect(el.querySelector('.adw-notebook-menu')).toBeNull();
424
+ });
425
+ });
426
+
427
+ await describe('<gtk-notebook> re-parent', async () => {
428
+ await it('keeps ONE header across a move, and never grows a second one', async () => {
429
+ // The header is built once and stays a child of the notebook even while the
430
+ // notebook itself is detached, because a GTK widget keeps its children across
431
+ // a re-parent. Rebuilding it on the way back in would leave the old one behind,
432
+ // and `_pages()` — which filters the header out BY IDENTITY — would read that
433
+ // leftover as a page.
434
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two' }]);
435
+ const elsewhere = document.createElement('div');
436
+ document.body.appendChild(elsewhere);
437
+ elsewhere.appendChild(el);
438
+ await new Promise((resolve) => setTimeout(resolve, 0));
439
+ expect(el.querySelectorAll('.adw-notebook-header')).toHaveLength(1);
440
+ expect(el.nPages).toBe(2);
441
+ expect(marks(el)).toStrictEqual(['One:visible', 'Two']);
442
+ host.remove();
443
+ elsewhere.remove();
444
+ });
445
+
446
+ await it('keeps the current page across a move', async () => {
447
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two' }]);
448
+ tabs(el)[1].click();
449
+ const elsewhere = document.createElement('div');
450
+ document.body.appendChild(elsewhere);
451
+ elsewhere.appendChild(el);
452
+ await new Promise((resolve) => setTimeout(resolve, 0));
453
+ expect(el.page).toBe(1);
454
+ host.remove();
455
+ elsewhere.remove();
456
+ });
457
+ });
458
+
459
+ await describe('<gtk-notebook> notification', async () => {
460
+ await it('notifies page once per real switch', () => {
461
+ // `g_object_notify_by_pspec (G_OBJECT (notebook), properties[PROP_PAGE])`
462
+ // (gtknotebook.c:5497) is at the END of `real_switch_page`, so it fires once and
463
+ // only after the switch took.
464
+ const { el, host } = mount([{ label: 'One' }, { label: 'Two' }]);
465
+ let notifies = 0;
466
+ el.addEventListener('notify::page', () => (notifies += 1));
467
+ el.setCurrentPage(1);
468
+ expect(notifies).toBe(1);
469
+ el.setCurrentPage(0);
470
+ expect(notifies).toBe(2);
471
+ host.remove();
472
+ });
473
+
474
+ await it('reports the parsed property, not the raw attribute', () => {
475
+ const { el, host } = mount([{ label: 'One' }]);
476
+ const details: unknown[] = [];
477
+ el.addEventListener('notify::tab-pos', (e) => details.push((e as CustomEvent).detail));
478
+ el.tabPos = 'bottom';
479
+ expect(details).toStrictEqual([{ 'tab-pos': 'bottom' }]);
480
+ host.remove();
481
+ });
482
+
483
+ await it('reports an inserted page as page-added and a removed one as page-removed', () => {
484
+ // The two page-list signals the C raises around an insert and a remove
485
+ // (gtknotebook.c:1380-1391 emitted at :4129, :1358-1369 emitted at :3551).
486
+ const { el, host } = mount([{ label: 'One' }]);
487
+ const events: string[] = [];
488
+ el.addEventListener('page-added', () => events.push('added'));
489
+ el.addEventListener('page-removed', () => events.push('removed'));
490
+ const second = document.createElement('div');
491
+ second.setAttribute('tab-label', 'Two');
492
+ el.appendPage(second);
493
+ el.removePage(second);
494
+ expect(events).toStrictEqual(['added', 'removed']);
495
+ host.remove();
496
+ });
497
+
498
+ await it('carries the position and the new page count with each of them', () => {
499
+ const { el, host } = mount([{ label: 'One' }]);
500
+ const details: unknown[] = [];
501
+ el.addEventListener('page-added', (e) => details.push((e as CustomEvent).detail));
502
+ const second = document.createElement('div');
503
+ el.appendPage(second, 'Two');
504
+ expect(details).toStrictEqual([{ position: 1, nPages: 2 }]);
505
+ host.remove();
506
+ });
507
+ });
508
+
509
+ await describe('geometry measured on a native Gtk.Notebook', async () => {
510
+ await it('has 37 px tabs under a 37 px header, 8 px in, and does not stretch them', () => {
511
+ const { el, host } = mount([{ label: 'A' }, { label: 'Longer label' }]);
512
+ const header = el.querySelector('.adw-notebook-header') as HTMLElement;
513
+ const tabs = [...el.querySelectorAll('.adw-notebook-tab')] as HTMLElement[];
514
+ const box = el.getBoundingClientRect();
515
+ expect(header.getBoundingClientRect().height).toBe(37);
516
+ expect(tabs.map((tab) => tab.getBoundingClientRect().height)).toStrictEqual([37, 37]);
517
+ // The 1 px frame, then the 8 px of header padding, strip margin, strip padding and tab margin.
518
+ expect(tabs[0].getBoundingClientRect().left - box.left).toBe(9);
519
+ expect(tabs[0].getBoundingClientRect().width).toBeLessThan(tabs[1].getBoundingClientRect().width);
520
+ expect(tabs[1].getBoundingClientRect().right).toBeLessThan(box.right - 100);
521
+ host.remove();
522
+ });
523
+ });
524
+
525
+ await describe('a shared-tree page', async () => {
526
+ await it('is written as the tab-label the notebook reads, and a stack reads name and title', () => {
527
+ const notebook = buildSharedTree({
528
+ tag: 'GtkNotebook',
529
+ children: [{ tag: 'GtkLabel', page: { label: 'One' }, props: { label: 'first' } }],
530
+ });
531
+ expect(notebook.children[0].getAttribute('tab-label')).toBe('One');
532
+ const stack = buildSharedTree({
533
+ tag: 'GtkStack',
534
+ children: [{ tag: 'GtkLabel', page: { name: 'a', label: 'A' } }],
535
+ });
536
+ expect(stack.children[0].getAttribute('name')).toBe('a');
537
+ expect(stack.children[0].getAttribute('title')).toBe('A');
538
+ });
539
+
540
+ await it('is refused BY NAME on a parent with no pages', () => {
541
+ expect(() =>
542
+ buildSharedTree({ tag: 'GtkBox', children: [{ tag: 'GtkLabel', page: { label: 'X' } }] }),
543
+ ).toThrow('has no pages');
544
+ });
545
+ });
546
+ };