@furystack/shades-common-components 12.4.0 → 12.6.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 (297) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/esm/components/app-bar-link.spec.js +16 -19
  3. package/esm/components/app-bar-link.spec.js.map +1 -1
  4. package/esm/components/app-bar.spec.js +6 -4
  5. package/esm/components/app-bar.spec.js.map +1 -1
  6. package/esm/components/avatar.spec.js +9 -9
  7. package/esm/components/avatar.spec.js.map +1 -1
  8. package/esm/components/breadcrumb.spec.js +2 -2
  9. package/esm/components/breadcrumb.spec.js.map +1 -1
  10. package/esm/components/button-group.d.ts +32 -0
  11. package/esm/components/button-group.d.ts.map +1 -1
  12. package/esm/components/button-group.js +26 -2
  13. package/esm/components/button-group.js.map +1 -1
  14. package/esm/components/button-group.spec.js +127 -11
  15. package/esm/components/button-group.spec.js.map +1 -1
  16. package/esm/components/button.spec.js +4 -4
  17. package/esm/components/button.spec.js.map +1 -1
  18. package/esm/components/cache-view.spec.js +2 -3
  19. package/esm/components/cache-view.spec.js.map +1 -1
  20. package/esm/components/carousel.spec.js +47 -47
  21. package/esm/components/carousel.spec.js.map +1 -1
  22. package/esm/components/circular-progress.spec.js +2 -2
  23. package/esm/components/command-palette/command-palette-input.spec.js +23 -19
  24. package/esm/components/command-palette/command-palette-input.spec.js.map +1 -1
  25. package/esm/components/command-palette/command-palette-suggestion-list.spec.js +27 -27
  26. package/esm/components/command-palette/command-palette-suggestion-list.spec.js.map +1 -1
  27. package/esm/components/command-palette/index.spec.js +64 -51
  28. package/esm/components/command-palette/index.spec.js.map +1 -1
  29. package/esm/components/context-menu/context-menu.spec.js +33 -33
  30. package/esm/components/context-menu/context-menu.spec.js.map +1 -1
  31. package/esm/components/data-grid/body.spec.js +13 -13
  32. package/esm/components/data-grid/body.spec.js.map +1 -1
  33. package/esm/components/data-grid/data-grid-row.spec.js +8 -8
  34. package/esm/components/data-grid/data-grid-row.spec.js.map +1 -1
  35. package/esm/components/data-grid/data-grid.d.ts +47 -3
  36. package/esm/components/data-grid/data-grid.d.ts.map +1 -1
  37. package/esm/components/data-grid/data-grid.js +8 -11
  38. package/esm/components/data-grid/data-grid.js.map +1 -1
  39. package/esm/components/data-grid/data-grid.spec.js +71 -28
  40. package/esm/components/data-grid/data-grid.spec.js.map +1 -1
  41. package/esm/components/data-grid/filters/boolean-filter.d.ts +12 -0
  42. package/esm/components/data-grid/filters/boolean-filter.d.ts.map +1 -0
  43. package/esm/components/data-grid/filters/boolean-filter.js +27 -0
  44. package/esm/components/data-grid/filters/boolean-filter.js.map +1 -0
  45. package/esm/components/data-grid/filters/boolean-filter.spec.d.ts +2 -0
  46. package/esm/components/data-grid/filters/boolean-filter.spec.d.ts.map +1 -0
  47. package/esm/components/data-grid/filters/boolean-filter.spec.js +114 -0
  48. package/esm/components/data-grid/filters/boolean-filter.spec.js.map +1 -0
  49. package/esm/components/data-grid/filters/date-filter.d.ts +12 -0
  50. package/esm/components/data-grid/filters/date-filter.d.ts.map +1 -0
  51. package/esm/components/data-grid/filters/date-filter.js +109 -0
  52. package/esm/components/data-grid/filters/date-filter.js.map +1 -0
  53. package/esm/components/data-grid/filters/date-filter.spec.d.ts +2 -0
  54. package/esm/components/data-grid/filters/date-filter.spec.d.ts.map +1 -0
  55. package/esm/components/data-grid/filters/date-filter.spec.js +145 -0
  56. package/esm/components/data-grid/filters/date-filter.spec.js.map +1 -0
  57. package/esm/components/data-grid/filters/enum-filter.d.ts +16 -0
  58. package/esm/components/data-grid/filters/enum-filter.d.ts.map +1 -0
  59. package/esm/components/data-grid/filters/enum-filter.js +72 -0
  60. package/esm/components/data-grid/filters/enum-filter.js.map +1 -0
  61. package/esm/components/data-grid/filters/enum-filter.spec.d.ts +2 -0
  62. package/esm/components/data-grid/filters/enum-filter.spec.d.ts.map +1 -0
  63. package/esm/components/data-grid/filters/enum-filter.spec.js +136 -0
  64. package/esm/components/data-grid/filters/enum-filter.spec.js.map +1 -0
  65. package/esm/components/data-grid/filters/filter-dropdown.d.ts +6 -0
  66. package/esm/components/data-grid/filters/filter-dropdown.d.ts.map +1 -0
  67. package/esm/components/data-grid/filters/filter-dropdown.js +41 -0
  68. package/esm/components/data-grid/filters/filter-dropdown.js.map +1 -0
  69. package/esm/components/data-grid/filters/filter-dropdown.spec.d.ts +2 -0
  70. package/esm/components/data-grid/filters/filter-dropdown.spec.d.ts.map +1 -0
  71. package/esm/components/data-grid/filters/filter-dropdown.spec.js +69 -0
  72. package/esm/components/data-grid/filters/filter-dropdown.spec.js.map +1 -0
  73. package/esm/components/data-grid/filters/filter-styles.d.ts +24 -0
  74. package/esm/components/data-grid/filters/filter-styles.d.ts.map +1 -0
  75. package/esm/components/data-grid/filters/filter-styles.js +25 -0
  76. package/esm/components/data-grid/filters/filter-styles.js.map +1 -0
  77. package/esm/components/data-grid/filters/index.d.ts +7 -0
  78. package/esm/components/data-grid/filters/index.d.ts.map +1 -0
  79. package/esm/components/data-grid/filters/index.js +7 -0
  80. package/esm/components/data-grid/filters/index.js.map +1 -0
  81. package/esm/components/data-grid/filters/number-filter.d.ts +12 -0
  82. package/esm/components/data-grid/filters/number-filter.d.ts.map +1 -0
  83. package/esm/components/data-grid/filters/number-filter.js +65 -0
  84. package/esm/components/data-grid/filters/number-filter.js.map +1 -0
  85. package/esm/components/data-grid/filters/number-filter.spec.d.ts +2 -0
  86. package/esm/components/data-grid/filters/number-filter.spec.d.ts.map +1 -0
  87. package/esm/components/data-grid/filters/number-filter.spec.js +142 -0
  88. package/esm/components/data-grid/filters/number-filter.spec.js.map +1 -0
  89. package/esm/components/data-grid/filters/string-filter.d.ts +12 -0
  90. package/esm/components/data-grid/filters/string-filter.d.ts.map +1 -0
  91. package/esm/components/data-grid/filters/string-filter.js +63 -0
  92. package/esm/components/data-grid/filters/string-filter.js.map +1 -0
  93. package/esm/components/data-grid/filters/string-filter.spec.d.ts +2 -0
  94. package/esm/components/data-grid/filters/string-filter.spec.d.ts.map +1 -0
  95. package/esm/components/data-grid/filters/string-filter.spec.js +128 -0
  96. package/esm/components/data-grid/filters/string-filter.spec.js.map +1 -0
  97. package/esm/components/data-grid/footer.d.ts +1 -0
  98. package/esm/components/data-grid/footer.d.ts.map +1 -1
  99. package/esm/components/data-grid/footer.js +24 -16
  100. package/esm/components/data-grid/footer.js.map +1 -1
  101. package/esm/components/data-grid/footer.spec.js +111 -71
  102. package/esm/components/data-grid/footer.spec.js.map +1 -1
  103. package/esm/components/data-grid/header.d.ts +6 -9
  104. package/esm/components/data-grid/header.d.ts.map +1 -1
  105. package/esm/components/data-grid/header.js +51 -117
  106. package/esm/components/data-grid/header.js.map +1 -1
  107. package/esm/components/data-grid/header.spec.js +116 -187
  108. package/esm/components/data-grid/header.spec.js.map +1 -1
  109. package/esm/components/data-grid/index.d.ts +1 -0
  110. package/esm/components/data-grid/index.d.ts.map +1 -1
  111. package/esm/components/data-grid/index.js +1 -0
  112. package/esm/components/data-grid/index.js.map +1 -1
  113. package/esm/components/data-grid/selection-cell.spec.js +8 -8
  114. package/esm/components/data-grid/selection-cell.spec.js.map +1 -1
  115. package/esm/components/drawer/drawer-toggle-button.spec.js +22 -22
  116. package/esm/components/drawer/drawer-toggle-button.spec.js.map +1 -1
  117. package/esm/components/drawer/index.spec.js +36 -36
  118. package/esm/components/drawer/index.spec.js.map +1 -1
  119. package/esm/components/dropdown.spec.js +38 -30
  120. package/esm/components/dropdown.spec.js.map +1 -1
  121. package/esm/components/fab.spec.js +4 -4
  122. package/esm/components/fab.spec.js.map +1 -1
  123. package/esm/components/form.spec.js +37 -37
  124. package/esm/components/form.spec.js.map +1 -1
  125. package/esm/components/grid.d.ts +3 -0
  126. package/esm/components/grid.d.ts.map +1 -1
  127. package/esm/components/grid.js +3 -0
  128. package/esm/components/grid.js.map +1 -1
  129. package/esm/components/grid.spec.js +3 -3
  130. package/esm/components/grid.spec.js.map +1 -1
  131. package/esm/components/image.spec.js +55 -52
  132. package/esm/components/image.spec.js.map +1 -1
  133. package/esm/components/inputs/autocomplete.d.ts +3 -0
  134. package/esm/components/inputs/autocomplete.d.ts.map +1 -1
  135. package/esm/components/inputs/autocomplete.js +3 -0
  136. package/esm/components/inputs/autocomplete.js.map +1 -1
  137. package/esm/components/inputs/autocomplete.spec.js +7 -14
  138. package/esm/components/inputs/autocomplete.spec.js.map +1 -1
  139. package/esm/components/inputs/checkbox.spec.js +22 -22
  140. package/esm/components/inputs/checkbox.spec.js.map +1 -1
  141. package/esm/components/inputs/input-number.spec.js +47 -47
  142. package/esm/components/inputs/input-number.spec.js.map +1 -1
  143. package/esm/components/inputs/input.spec.js +53 -53
  144. package/esm/components/inputs/input.spec.js.map +1 -1
  145. package/esm/components/inputs/radio-group.spec.js +14 -14
  146. package/esm/components/inputs/radio-group.spec.js.map +1 -1
  147. package/esm/components/inputs/radio.spec.js +16 -16
  148. package/esm/components/inputs/radio.spec.js.map +1 -1
  149. package/esm/components/inputs/select.spec.js +74 -74
  150. package/esm/components/inputs/select.spec.js.map +1 -1
  151. package/esm/components/inputs/slider.spec.js +16 -16
  152. package/esm/components/inputs/slider.spec.js.map +1 -1
  153. package/esm/components/inputs/switch.spec.js +24 -24
  154. package/esm/components/inputs/switch.spec.js.map +1 -1
  155. package/esm/components/inputs/text-area.spec.js +17 -17
  156. package/esm/components/inputs/text-area.spec.js.map +1 -1
  157. package/esm/components/linear-progress.spec.js +2 -2
  158. package/esm/components/list/list.d.ts +10 -0
  159. package/esm/components/list/list.d.ts.map +1 -1
  160. package/esm/components/list/list.js +23 -2
  161. package/esm/components/list/list.js.map +1 -1
  162. package/esm/components/list/list.spec.js +137 -36
  163. package/esm/components/list/list.spec.js.map +1 -1
  164. package/esm/components/markdown/markdown-display.spec.js +15 -15
  165. package/esm/components/markdown/markdown-display.spec.js.map +1 -1
  166. package/esm/components/markdown/markdown-editor.spec.js +8 -8
  167. package/esm/components/markdown/markdown-editor.spec.js.map +1 -1
  168. package/esm/components/markdown/markdown-input.d.ts +14 -0
  169. package/esm/components/markdown/markdown-input.d.ts.map +1 -1
  170. package/esm/components/markdown/markdown-input.js +48 -2
  171. package/esm/components/markdown/markdown-input.js.map +1 -1
  172. package/esm/components/markdown/markdown-input.spec.js +114 -17
  173. package/esm/components/markdown/markdown-input.spec.js.map +1 -1
  174. package/esm/components/menu/menu.spec.js +28 -28
  175. package/esm/components/menu/menu.spec.js.map +1 -1
  176. package/esm/components/modal.spec.js +15 -18
  177. package/esm/components/modal.spec.js.map +1 -1
  178. package/esm/components/noty-list.spec.js +25 -23
  179. package/esm/components/noty-list.spec.js.map +1 -1
  180. package/esm/components/page-container/index.spec.js +16 -16
  181. package/esm/components/page-container/index.spec.js.map +1 -1
  182. package/esm/components/page-container/page-header.spec.js +16 -16
  183. package/esm/components/page-container/page-header.spec.js.map +1 -1
  184. package/esm/components/page-layout/index.spec.js +29 -29
  185. package/esm/components/page-layout/index.spec.js.map +1 -1
  186. package/esm/components/paper.spec.js +3 -3
  187. package/esm/components/paper.spec.js.map +1 -1
  188. package/esm/components/rating.spec.js +61 -61
  189. package/esm/components/rating.spec.js.map +1 -1
  190. package/esm/components/skeleton.spec.js +10 -6
  191. package/esm/components/skeleton.spec.js.map +1 -1
  192. package/esm/components/suggest/index.d.ts +10 -2
  193. package/esm/components/suggest/index.d.ts.map +1 -1
  194. package/esm/components/suggest/index.js +21 -1
  195. package/esm/components/suggest/index.js.map +1 -1
  196. package/esm/components/suggest/index.spec.js +50 -0
  197. package/esm/components/suggest/index.spec.js.map +1 -1
  198. package/esm/components/suggest/suggest-input.spec.js +4 -10
  199. package/esm/components/suggest/suggest-input.spec.js.map +1 -1
  200. package/esm/components/tabs.spec.js +30 -30
  201. package/esm/components/tabs.spec.js.map +1 -1
  202. package/esm/components/tree/tree.spec.js +27 -27
  203. package/esm/components/tree/tree.spec.js.map +1 -1
  204. package/esm/components/typography.spec.js +3 -3
  205. package/esm/components/typography.spec.js.map +1 -1
  206. package/esm/components/wizard/index.d.ts +8 -0
  207. package/esm/components/wizard/index.d.ts.map +1 -1
  208. package/esm/components/wizard/index.js +90 -0
  209. package/esm/components/wizard/index.js.map +1 -1
  210. package/esm/components/wizard/index.spec.js +84 -7
  211. package/esm/components/wizard/index.spec.js.map +1 -1
  212. package/esm/utils/promisify-animation.d.ts.map +1 -1
  213. package/esm/utils/promisify-animation.js +3 -0
  214. package/esm/utils/promisify-animation.js.map +1 -1
  215. package/package.json +3 -3
  216. package/src/components/app-bar-link.spec.tsx +16 -19
  217. package/src/components/app-bar.spec.tsx +6 -4
  218. package/src/components/avatar.spec.tsx +9 -9
  219. package/src/components/breadcrumb.spec.tsx +2 -2
  220. package/src/components/button-group.spec.tsx +155 -11
  221. package/src/components/button-group.tsx +49 -2
  222. package/src/components/button.spec.tsx +4 -4
  223. package/src/components/cache-view.spec.tsx +3 -3
  224. package/src/components/carousel.spec.tsx +47 -47
  225. package/src/components/circular-progress.spec.tsx +2 -2
  226. package/src/components/command-palette/command-palette-input.spec.tsx +23 -19
  227. package/src/components/command-palette/command-palette-suggestion-list.spec.tsx +27 -27
  228. package/src/components/command-palette/index.spec.tsx +64 -51
  229. package/src/components/context-menu/context-menu.spec.tsx +33 -33
  230. package/src/components/data-grid/body.spec.tsx +13 -13
  231. package/src/components/data-grid/data-grid-row.spec.tsx +8 -8
  232. package/src/components/data-grid/data-grid.spec.tsx +106 -28
  233. package/src/components/data-grid/data-grid.tsx +57 -13
  234. package/src/components/data-grid/filters/boolean-filter.spec.tsx +142 -0
  235. package/src/components/data-grid/filters/boolean-filter.tsx +45 -0
  236. package/src/components/data-grid/filters/date-filter.spec.tsx +181 -0
  237. package/src/components/data-grid/filters/date-filter.tsx +162 -0
  238. package/src/components/data-grid/filters/enum-filter.spec.tsx +168 -0
  239. package/src/components/data-grid/filters/enum-filter.tsx +119 -0
  240. package/src/components/data-grid/filters/filter-dropdown.spec.tsx +89 -0
  241. package/src/components/data-grid/filters/filter-dropdown.tsx +60 -0
  242. package/src/components/data-grid/filters/filter-styles.ts +26 -0
  243. package/src/components/data-grid/filters/index.ts +6 -0
  244. package/src/components/data-grid/filters/number-filter.spec.tsx +174 -0
  245. package/src/components/data-grid/filters/number-filter.tsx +115 -0
  246. package/src/components/data-grid/filters/string-filter.spec.tsx +157 -0
  247. package/src/components/data-grid/filters/string-filter.tsx +112 -0
  248. package/src/components/data-grid/footer.spec.tsx +130 -74
  249. package/src/components/data-grid/footer.tsx +41 -34
  250. package/src/components/data-grid/header.spec.tsx +128 -212
  251. package/src/components/data-grid/header.tsx +95 -183
  252. package/src/components/data-grid/index.tsx +1 -0
  253. package/src/components/data-grid/selection-cell.spec.tsx +8 -8
  254. package/src/components/drawer/drawer-toggle-button.spec.tsx +22 -22
  255. package/src/components/drawer/index.spec.tsx +36 -36
  256. package/src/components/dropdown.spec.tsx +38 -30
  257. package/src/components/fab.spec.tsx +4 -4
  258. package/src/components/form.spec.tsx +37 -37
  259. package/src/components/grid.spec.tsx +3 -3
  260. package/src/components/grid.tsx +3 -0
  261. package/src/components/image.spec.tsx +55 -52
  262. package/src/components/inputs/autocomplete.spec.tsx +7 -14
  263. package/src/components/inputs/autocomplete.tsx +3 -0
  264. package/src/components/inputs/checkbox.spec.tsx +22 -22
  265. package/src/components/inputs/input-number.spec.tsx +47 -47
  266. package/src/components/inputs/input.spec.tsx +53 -53
  267. package/src/components/inputs/radio-group.spec.tsx +14 -14
  268. package/src/components/inputs/radio.spec.tsx +16 -16
  269. package/src/components/inputs/select.spec.tsx +74 -74
  270. package/src/components/inputs/slider.spec.tsx +16 -16
  271. package/src/components/inputs/switch.spec.tsx +24 -24
  272. package/src/components/inputs/text-area.spec.tsx +17 -17
  273. package/src/components/linear-progress.spec.tsx +2 -2
  274. package/src/components/list/list.spec.tsx +209 -36
  275. package/src/components/list/list.tsx +56 -19
  276. package/src/components/markdown/markdown-display.spec.tsx +15 -15
  277. package/src/components/markdown/markdown-editor.spec.tsx +8 -8
  278. package/src/components/markdown/markdown-input.spec.tsx +159 -17
  279. package/src/components/markdown/markdown-input.tsx +65 -1
  280. package/src/components/menu/menu.spec.tsx +28 -28
  281. package/src/components/modal.spec.tsx +15 -18
  282. package/src/components/noty-list.spec.tsx +25 -23
  283. package/src/components/page-container/index.spec.tsx +16 -16
  284. package/src/components/page-container/page-header.spec.tsx +16 -16
  285. package/src/components/page-layout/index.spec.tsx +29 -29
  286. package/src/components/paper.spec.tsx +3 -3
  287. package/src/components/rating.spec.tsx +61 -61
  288. package/src/components/skeleton.spec.tsx +10 -6
  289. package/src/components/suggest/index.spec.tsx +83 -0
  290. package/src/components/suggest/index.tsx +36 -3
  291. package/src/components/suggest/suggest-input.spec.tsx +4 -10
  292. package/src/components/tabs.spec.tsx +30 -30
  293. package/src/components/tree/tree.spec.tsx +27 -27
  294. package/src/components/typography.spec.tsx +3 -3
  295. package/src/components/wizard/index.spec.tsx +123 -6
  296. package/src/components/wizard/index.tsx +125 -0
  297. package/src/utils/promisify-animation.ts +3 -0
@@ -1,6 +1,6 @@
1
1
  import { Injector } from '@furystack/inject'
2
- import { createComponent, initializeShadeRoot } from '@furystack/shades'
3
- import { sleepAsync, usingAsync } from '@furystack/utils'
2
+ import { createComponent, flushUpdates, initializeShadeRoot } from '@furystack/shades'
3
+ import { usingAsync } from '@furystack/utils'
4
4
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
5
5
  import { ListService } from '../../services/list-service.js'
6
6
  import { List } from './list.js'
@@ -40,7 +40,7 @@ describe('List', () => {
40
40
  ),
41
41
  })
42
42
 
43
- await sleepAsync(50)
43
+ await flushUpdates()
44
44
 
45
45
  const list = document.querySelector('shade-list')
46
46
  expect(list).not.toBeNull()
@@ -62,7 +62,7 @@ describe('List', () => {
62
62
  ),
63
63
  })
64
64
 
65
- await sleepAsync(50)
65
+ await flushUpdates()
66
66
 
67
67
  const list = document.querySelector('shade-list')
68
68
  const items = list?.querySelectorAll('shade-list-item')
@@ -85,7 +85,7 @@ describe('List', () => {
85
85
  ),
86
86
  })
87
87
 
88
- await sleepAsync(50)
88
+ await flushUpdates()
89
89
 
90
90
  const list = document.querySelector('shade-list')
91
91
  const listbox = list?.querySelector('[role="listbox"]')
@@ -109,7 +109,7 @@ describe('List', () => {
109
109
  ),
110
110
  })
111
111
 
112
- await sleepAsync(50)
112
+ await flushUpdates()
113
113
 
114
114
  const list = document.querySelector('shade-list')
115
115
  const options = list?.querySelectorAll('[role="option"]')
@@ -137,7 +137,7 @@ describe('List', () => {
137
137
  ),
138
138
  })
139
139
 
140
- await sleepAsync(50)
140
+ await flushUpdates()
141
141
 
142
142
  const list = document.querySelector('shade-list')
143
143
  const icons = list?.querySelectorAll('[data-testid="icon"]')
@@ -165,7 +165,7 @@ describe('List', () => {
165
165
  ),
166
166
  })
167
167
 
168
- await sleepAsync(50)
168
+ await flushUpdates()
169
169
 
170
170
  const list = document.querySelector('shade-list')
171
171
  const actions = list?.querySelectorAll('[data-testid="action"]')
@@ -193,7 +193,7 @@ describe('List', () => {
193
193
  ),
194
194
  })
195
195
 
196
- await sleepAsync(50)
196
+ await flushUpdates()
197
197
 
198
198
  const list = document.querySelector('shade-list') as HTMLElement
199
199
  expect(list?.getAttribute('data-variant')).toBe('contained')
@@ -215,7 +215,7 @@ describe('List', () => {
215
215
  ),
216
216
  })
217
217
 
218
- await sleepAsync(50)
218
+ await flushUpdates()
219
219
 
220
220
  expect(service.items.getValue()).toEqual(testItems)
221
221
 
@@ -240,7 +240,7 @@ describe('List', () => {
240
240
  ),
241
241
  })
242
242
 
243
- await sleepAsync(50)
243
+ await flushUpdates()
244
244
 
245
245
  const list = document.querySelector('shade-list')
246
246
  const wrapper = list?.querySelector('.shade-list-wrapper') as HTMLElement
@@ -268,7 +268,7 @@ describe('List', () => {
268
268
  ),
269
269
  })
270
270
 
271
- await sleepAsync(50)
271
+ await flushUpdates()
272
272
 
273
273
  const list = document.querySelector('shade-list')
274
274
  const wrapper = list?.querySelector('.shade-list-wrapper') as HTMLElement
@@ -298,7 +298,7 @@ describe('List', () => {
298
298
  ),
299
299
  })
300
300
 
301
- await sleepAsync(50)
301
+ await flushUpdates()
302
302
 
303
303
  const list = document.querySelector('shade-list')
304
304
  const listItems = list?.querySelectorAll('shade-list-item') as NodeListOf<HTMLElement>
@@ -323,10 +323,10 @@ describe('List', () => {
323
323
  ),
324
324
  })
325
325
 
326
- await sleepAsync(50)
326
+ await flushUpdates()
327
327
 
328
328
  service.focusedItem.setValue(testItems[1])
329
- await sleepAsync(10)
329
+ await flushUpdates()
330
330
 
331
331
  const list = document.querySelector('shade-list')
332
332
  const listItems = list?.querySelectorAll('shade-list-item') as NodeListOf<HTMLElement>
@@ -352,10 +352,10 @@ describe('List', () => {
352
352
  ),
353
353
  })
354
354
 
355
- await sleepAsync(50)
355
+ await flushUpdates()
356
356
 
357
357
  service.selection.setValue([testItems[0], testItems[2]])
358
- await sleepAsync(10)
358
+ await flushUpdates()
359
359
 
360
360
  const list = document.querySelector('shade-list')
361
361
  const listItems = list?.querySelectorAll('shade-list-item') as NodeListOf<HTMLElement>
@@ -380,10 +380,10 @@ describe('List', () => {
380
380
  ),
381
381
  })
382
382
 
383
- await sleepAsync(50)
383
+ await flushUpdates()
384
384
 
385
385
  service.selection.setValue([testItems[0]])
386
- await sleepAsync(10)
386
+ await flushUpdates()
387
387
 
388
388
  const list = document.querySelector('shade-list')
389
389
  const listItems = list?.querySelectorAll('shade-list-item') as NodeListOf<HTMLElement>
@@ -413,10 +413,10 @@ describe('List', () => {
413
413
  ),
414
414
  })
415
415
 
416
- await sleepAsync(50)
416
+ await flushUpdates()
417
417
 
418
418
  service.selection.setValue([testItems[0]])
419
- await sleepAsync(10)
419
+ await flushUpdates()
420
420
 
421
421
  expect(onSelectionChange).toHaveBeenCalledWith([testItems[0]])
422
422
 
@@ -442,7 +442,7 @@ describe('List', () => {
442
442
  ),
443
443
  })
444
444
 
445
- await sleepAsync(50)
445
+ await flushUpdates()
446
446
 
447
447
  window.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true }))
448
448
 
@@ -468,7 +468,7 @@ describe('List', () => {
468
468
  ),
469
469
  })
470
470
 
471
- await sleepAsync(50)
471
+ await flushUpdates()
472
472
 
473
473
  window.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp', bubbles: true }))
474
474
 
@@ -494,7 +494,7 @@ describe('List', () => {
494
494
  ),
495
495
  })
496
496
 
497
- await sleepAsync(50)
497
+ await flushUpdates()
498
498
 
499
499
  window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home', bubbles: true }))
500
500
 
@@ -520,7 +520,7 @@ describe('List', () => {
520
520
  ),
521
521
  })
522
522
 
523
- await sleepAsync(50)
523
+ await flushUpdates()
524
524
 
525
525
  window.dispatchEvent(new KeyboardEvent('keydown', { key: 'End', bubbles: true }))
526
526
 
@@ -546,7 +546,7 @@ describe('List', () => {
546
546
  ),
547
547
  })
548
548
 
549
- await sleepAsync(50)
549
+ await flushUpdates()
550
550
 
551
551
  window.dispatchEvent(new KeyboardEvent('keydown', { key: ' ', bubbles: true }))
552
552
  expect(service.selection.getValue()).toContain(testItems[0])
@@ -573,7 +573,7 @@ describe('List', () => {
573
573
  ),
574
574
  })
575
575
 
576
- await sleepAsync(50)
576
+ await flushUpdates()
577
577
 
578
578
  window.dispatchEvent(new KeyboardEvent('keydown', { key: '+', bubbles: true }))
579
579
 
@@ -599,7 +599,7 @@ describe('List', () => {
599
599
  ),
600
600
  })
601
601
 
602
- await sleepAsync(50)
602
+ await flushUpdates()
603
603
 
604
604
  window.dispatchEvent(new KeyboardEvent('keydown', { key: '-', bubbles: true }))
605
605
 
@@ -625,7 +625,7 @@ describe('List', () => {
625
625
  ),
626
626
  })
627
627
 
628
- await sleepAsync(50)
628
+ await flushUpdates()
629
629
 
630
630
  window.dispatchEvent(new KeyboardEvent('keydown', { key: '*', bubbles: true }))
631
631
 
@@ -655,7 +655,7 @@ describe('List', () => {
655
655
  ),
656
656
  })
657
657
 
658
- await sleepAsync(50)
658
+ await flushUpdates()
659
659
 
660
660
  window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))
661
661
 
@@ -681,7 +681,7 @@ describe('List', () => {
681
681
  ),
682
682
  })
683
683
 
684
- await sleepAsync(50)
684
+ await flushUpdates()
685
685
 
686
686
  window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab', bubbles: true }))
687
687
 
@@ -707,7 +707,7 @@ describe('List', () => {
707
707
  ),
708
708
  })
709
709
 
710
- await sleepAsync(50)
710
+ await flushUpdates()
711
711
 
712
712
  window.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true }))
713
713
 
@@ -741,7 +741,7 @@ describe('List', () => {
741
741
  ),
742
742
  })
743
743
 
744
- await sleepAsync(50)
744
+ await flushUpdates()
745
745
 
746
746
  window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }))
747
747
 
@@ -770,7 +770,7 @@ describe('List', () => {
770
770
  ),
771
771
  })
772
772
 
773
- await sleepAsync(50)
773
+ await flushUpdates()
774
774
 
775
775
  const list = document.querySelector('shade-list')
776
776
  const listItems = list?.querySelectorAll('shade-list-item') as NodeListOf<HTMLElement>
@@ -783,6 +783,179 @@ describe('List', () => {
783
783
  })
784
784
  })
785
785
 
786
+ describe('pagination', () => {
787
+ const manyItems: TestItem[] = Array.from({ length: 25 }, (_, i) => ({ id: i + 1, name: `Item ${i + 1}` }))
788
+
789
+ it('should render only current page items when pagination is provided', async () => {
790
+ await usingAsync(new Injector(), async (injector) => {
791
+ const rootElement = document.getElementById('root') as HTMLDivElement
792
+ const service = createTestService()
793
+
794
+ initializeShadeRoot({
795
+ injector,
796
+ rootElement,
797
+ jsxElement: (
798
+ <List<TestItem>
799
+ items={manyItems}
800
+ listService={service}
801
+ renderItem={(item) => <span>{item.name}</span>}
802
+ pagination={{ itemsPerPage: 10, page: 1, onPageChange: () => {} }}
803
+ />
804
+ ),
805
+ })
806
+
807
+ await flushUpdates()
808
+
809
+ const list = document.querySelector('shade-list')
810
+ const listItems = list?.querySelectorAll('shade-list-item')
811
+ expect(listItems?.length).toBe(10)
812
+
813
+ service[Symbol.dispose]()
814
+ })
815
+ })
816
+
817
+ it('should render the Pagination component', async () => {
818
+ await usingAsync(new Injector(), async (injector) => {
819
+ const rootElement = document.getElementById('root') as HTMLDivElement
820
+ const service = createTestService()
821
+
822
+ initializeShadeRoot({
823
+ injector,
824
+ rootElement,
825
+ jsxElement: (
826
+ <List<TestItem>
827
+ items={manyItems}
828
+ listService={service}
829
+ renderItem={(item) => <span>{item.name}</span>}
830
+ pagination={{ itemsPerPage: 10, page: 1, onPageChange: () => {} }}
831
+ />
832
+ ),
833
+ })
834
+
835
+ await flushUpdates()
836
+
837
+ const pagination = document.querySelector('shade-list shade-pagination')
838
+ expect(pagination).not.toBeNull()
839
+
840
+ service[Symbol.dispose]()
841
+ })
842
+ })
843
+
844
+ it('should show last page items correctly', async () => {
845
+ await usingAsync(new Injector(), async (injector) => {
846
+ const rootElement = document.getElementById('root') as HTMLDivElement
847
+ const service = createTestService()
848
+
849
+ initializeShadeRoot({
850
+ injector,
851
+ rootElement,
852
+ jsxElement: (
853
+ <List<TestItem>
854
+ items={manyItems}
855
+ listService={service}
856
+ renderItem={(item) => <span>{item.name}</span>}
857
+ pagination={{ itemsPerPage: 10, page: 3, onPageChange: () => {} }}
858
+ />
859
+ ),
860
+ })
861
+
862
+ await flushUpdates()
863
+
864
+ const list = document.querySelector('shade-list')
865
+ const listItems = list?.querySelectorAll('shade-list-item')
866
+ expect(listItems?.length).toBe(5)
867
+
868
+ service[Symbol.dispose]()
869
+ })
870
+ })
871
+
872
+ it('should not render Pagination when all items fit on one page', async () => {
873
+ await usingAsync(new Injector(), async (injector) => {
874
+ const rootElement = document.getElementById('root') as HTMLDivElement
875
+ const service = createTestService()
876
+
877
+ initializeShadeRoot({
878
+ injector,
879
+ rootElement,
880
+ jsxElement: (
881
+ <List<TestItem>
882
+ items={testItems}
883
+ listService={service}
884
+ renderItem={(item) => <span>{item.name}</span>}
885
+ pagination={{ itemsPerPage: 10, page: 1, onPageChange: () => {} }}
886
+ />
887
+ ),
888
+ })
889
+
890
+ await flushUpdates()
891
+
892
+ const pagination = document.querySelector('shade-list shade-pagination')
893
+ expect(pagination).toBeNull()
894
+
895
+ service[Symbol.dispose]()
896
+ })
897
+ })
898
+
899
+ it('should call onPageChange when a pagination button is clicked', async () => {
900
+ const onPageChange = vi.fn()
901
+ await usingAsync(new Injector(), async (injector) => {
902
+ const rootElement = document.getElementById('root') as HTMLDivElement
903
+ const service = createTestService()
904
+
905
+ initializeShadeRoot({
906
+ injector,
907
+ rootElement,
908
+ jsxElement: (
909
+ <List<TestItem>
910
+ items={manyItems}
911
+ listService={service}
912
+ renderItem={(item) => <span>{item.name}</span>}
913
+ pagination={{ itemsPerPage: 10, page: 1, onPageChange }}
914
+ />
915
+ ),
916
+ })
917
+
918
+ await flushUpdates()
919
+
920
+ const nextButton = document.querySelector(
921
+ 'shade-list shade-pagination [aria-label="Go to next page"]',
922
+ ) as HTMLButtonElement
923
+ expect(nextButton).not.toBeNull()
924
+ nextButton.click()
925
+
926
+ expect(onPageChange).toHaveBeenCalledWith(2)
927
+
928
+ service[Symbol.dispose]()
929
+ })
930
+ })
931
+
932
+ it('should render all items when pagination is not provided', async () => {
933
+ await usingAsync(new Injector(), async (injector) => {
934
+ const rootElement = document.getElementById('root') as HTMLDivElement
935
+ const service = createTestService()
936
+
937
+ initializeShadeRoot({
938
+ injector,
939
+ rootElement,
940
+ jsxElement: (
941
+ <List<TestItem> items={manyItems} listService={service} renderItem={(item) => <span>{item.name}</span>} />
942
+ ),
943
+ })
944
+
945
+ await flushUpdates()
946
+
947
+ const list = document.querySelector('shade-list')
948
+ const listItems = list?.querySelectorAll('shade-list-item')
949
+ expect(listItems?.length).toBe(25)
950
+
951
+ const pagination = document.querySelector('shade-list shade-pagination')
952
+ expect(pagination).toBeNull()
953
+
954
+ service[Symbol.dispose]()
955
+ })
956
+ })
957
+ })
958
+
786
959
  describe('keyboard listener cleanup', () => {
787
960
  it('should remove keyboard listener when component is disconnected', async () => {
788
961
  await usingAsync(new Injector(), async (injector) => {
@@ -800,12 +973,12 @@ describe('List', () => {
800
973
  ),
801
974
  })
802
975
 
803
- await sleepAsync(50)
976
+ await flushUpdates()
804
977
 
805
978
  const list = document.querySelector('shade-list') as HTMLElement
806
979
  list.remove()
807
980
 
808
- await sleepAsync(10)
981
+ await flushUpdates()
809
982
 
810
983
  window.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true }))
811
984
  expect(service.focusedItem.getValue()).toEqual(testItems[0])
@@ -2,6 +2,7 @@ import type { ChildrenList, PartialElement } from '@furystack/shades'
2
2
  import { createComponent, Shade } from '@furystack/shades'
3
3
  import { ClickAwayService } from '../../services/click-away-service.js'
4
4
  import type { ListService } from '../../services/list-service.js'
5
+ import { Pagination } from '../pagination.js'
5
6
  import { ListItem } from './list-item.js'
6
7
 
7
8
  export type ListItemState = {
@@ -9,6 +10,15 @@ export type ListItemState = {
9
10
  isSelected: boolean
10
11
  }
11
12
 
13
+ export type ListPaginationProps = {
14
+ /** Number of items to display per page */
15
+ itemsPerPage: number
16
+ /** Current page (1-indexed) */
17
+ page: number
18
+ /** Callback fired when the page changes */
19
+ onPageChange: (page: number) => void
20
+ }
21
+
12
22
  export type ListProps<T> = {
13
23
  items: T[]
14
24
  listService: ListService<T>
@@ -18,6 +28,8 @@ export type ListProps<T> = {
18
28
  variant?: 'contained' | 'outlined'
19
29
  onItemActivate?: (item: T) => void
20
30
  onSelectionChange?: (selected: T[]) => void
31
+ /** Optional pagination configuration. When provided, items are sliced and a Pagination control is rendered. */
32
+ pagination?: ListPaginationProps
21
33
  } & PartialElement<HTMLDivElement>
22
34
 
23
35
  export const List: <T>(props: ListProps<T>, children: ChildrenList) => JSX.Element<any> = Shade({
@@ -26,6 +38,11 @@ export const List: <T>(props: ListProps<T>, children: ChildrenList) => JSX.Eleme
26
38
  display: 'block',
27
39
  width: '100%',
28
40
  overflow: 'auto',
41
+ '& .shade-list-pagination': {
42
+ display: 'flex',
43
+ justifyContent: 'center',
44
+ padding: '8px 0',
45
+ },
29
46
  },
30
47
  render: ({ props, useDisposable, useHostProps, useRef }) => {
31
48
  const wrapperRef = useRef<HTMLDivElement>('listWrapper')
@@ -45,7 +62,20 @@ export const List: <T>(props: ListProps<T>, children: ChildrenList) => JSX.Eleme
45
62
  return { [Symbol.dispose]: () => window.removeEventListener('keydown', listener) }
46
63
  })
47
64
 
48
- props.listService.items.setValue(props.items)
65
+ const { pagination } = props
66
+ let visibleItems: typeof props.items
67
+ let pageCount = 1
68
+
69
+ if (pagination) {
70
+ const { itemsPerPage, page } = pagination
71
+ pageCount = Math.ceil(props.items.length / itemsPerPage)
72
+ const startIndex = (page - 1) * itemsPerPage
73
+ visibleItems = props.items.slice(startIndex, startIndex + itemsPerPage)
74
+ } else {
75
+ visibleItems = props.items
76
+ }
77
+
78
+ props.listService.items.setValue(visibleItems)
49
79
 
50
80
  useDisposable(
51
81
  'clickAway',
@@ -69,24 +99,31 @@ export const List: <T>(props: ListProps<T>, children: ChildrenList) => JSX.Eleme
69
99
  })
70
100
 
71
101
  return (
72
- <div
73
- ref={wrapperRef}
74
- role="listbox"
75
- ariaMultiSelectable="true"
76
- className="shade-list-wrapper"
77
- onclick={() => props.listService.hasFocus.setValue(true)}
78
- >
79
- {props.items.map((item) => (
80
- <ListItem
81
- item={item}
82
- listService={props.listService}
83
- renderItem={props.renderItem}
84
- renderIcon={props.renderIcon}
85
- renderSecondaryActions={props.renderSecondaryActions}
86
- onActivate={props.onItemActivate}
87
- />
88
- ))}
89
- </div>
102
+ <>
103
+ <div
104
+ ref={wrapperRef}
105
+ role="listbox"
106
+ ariaMultiSelectable="true"
107
+ className="shade-list-wrapper"
108
+ onclick={() => props.listService.hasFocus.setValue(true)}
109
+ >
110
+ {visibleItems.map((item) => (
111
+ <ListItem
112
+ item={item}
113
+ listService={props.listService}
114
+ renderItem={props.renderItem}
115
+ renderIcon={props.renderIcon}
116
+ renderSecondaryActions={props.renderSecondaryActions}
117
+ onActivate={props.onItemActivate}
118
+ />
119
+ ))}
120
+ </div>
121
+ {pagination && pageCount > 1 && (
122
+ <div className="shade-list-pagination">
123
+ <Pagination count={pageCount} page={pagination.page} onPageChange={pagination.onPageChange} size="small" />
124
+ </div>
125
+ )}
126
+ </>
90
127
  )
91
128
  },
92
129
  })
@@ -1,6 +1,6 @@
1
1
  import { Injector } from '@furystack/inject'
2
- import { createComponent, initializeShadeRoot } from '@furystack/shades'
3
- import { sleepAsync, usingAsync } from '@furystack/utils'
2
+ import { createComponent, flushUpdates, initializeShadeRoot } from '@furystack/shades'
3
+ import { usingAsync } from '@furystack/utils'
4
4
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
5
5
  import { MarkdownDisplay } from './markdown-display.js'
6
6
 
@@ -24,7 +24,7 @@ describe('MarkdownDisplay', () => {
24
24
  jsxElement: <MarkdownDisplay content="Hello" />,
25
25
  })
26
26
 
27
- await sleepAsync(50)
27
+ await flushUpdates()
28
28
 
29
29
  const el = document.querySelector('shade-markdown-display')
30
30
  expect(el).not.toBeNull()
@@ -41,7 +41,7 @@ describe('MarkdownDisplay', () => {
41
41
  jsxElement: <MarkdownDisplay content="# Hello World" />,
42
42
  })
43
43
 
44
- await sleepAsync(50)
44
+ await flushUpdates()
45
45
 
46
46
  const typography = document.querySelector('shade-markdown-display shade-typography')
47
47
  expect(typography).not.toBeNull()
@@ -60,7 +60,7 @@ describe('MarkdownDisplay', () => {
60
60
  jsxElement: <MarkdownDisplay content="Just a paragraph." />,
61
61
  })
62
62
 
63
- await sleepAsync(50)
63
+ await flushUpdates()
64
64
 
65
65
  const typography = document.querySelector('shade-markdown-display shade-typography[data-variant="body1"]')
66
66
  expect(typography).not.toBeNull()
@@ -78,7 +78,7 @@ describe('MarkdownDisplay', () => {
78
78
  jsxElement: <MarkdownDisplay content={'```js\nconsole.log("hi")\n```'} />,
79
79
  })
80
80
 
81
- await sleepAsync(50)
81
+ await flushUpdates()
82
82
 
83
83
  const codeBlock = document.querySelector('shade-markdown-display .md-code-block')
84
84
  expect(codeBlock).not.toBeNull()
@@ -96,7 +96,7 @@ describe('MarkdownDisplay', () => {
96
96
  jsxElement: <MarkdownDisplay content={'- Item A\n- Item B'} />,
97
97
  })
98
98
 
99
- await sleepAsync(50)
99
+ await flushUpdates()
100
100
 
101
101
  const list = document.querySelector('shade-markdown-display ul')
102
102
  expect(list).not.toBeNull()
@@ -115,7 +115,7 @@ describe('MarkdownDisplay', () => {
115
115
  jsxElement: <MarkdownDisplay content="- [ ] Task" />,
116
116
  })
117
117
 
118
- await sleepAsync(50)
118
+ await flushUpdates()
119
119
 
120
120
  const checkbox = document.querySelector('shade-markdown-display shade-checkbox')
121
121
  expect(checkbox).not.toBeNull()
@@ -134,7 +134,7 @@ describe('MarkdownDisplay', () => {
134
134
  jsxElement: <MarkdownDisplay content="- [ ] Task" readOnly={false} onChange={onChange} />,
135
135
  })
136
136
 
137
- await sleepAsync(50)
137
+ await flushUpdates()
138
138
 
139
139
  const checkbox = document.querySelector('shade-markdown-display shade-checkbox')
140
140
  expect(checkbox).not.toBeNull()
@@ -144,7 +144,7 @@ describe('MarkdownDisplay', () => {
144
144
  expect(input).not.toBeNull()
145
145
  input.click()
146
146
 
147
- await sleepAsync(50)
147
+ await flushUpdates()
148
148
 
149
149
  expect(onChange).toHaveBeenCalledOnce()
150
150
  expect(onChange).toHaveBeenCalledWith('- [x] Task')
@@ -161,7 +161,7 @@ describe('MarkdownDisplay', () => {
161
161
  jsxElement: <MarkdownDisplay content="> Quote text" />,
162
162
  })
163
163
 
164
- await sleepAsync(50)
164
+ await flushUpdates()
165
165
 
166
166
  const bq = document.querySelector('shade-markdown-display .md-blockquote')
167
167
  expect(bq).not.toBeNull()
@@ -179,7 +179,7 @@ describe('MarkdownDisplay', () => {
179
179
  jsxElement: <MarkdownDisplay content="---" />,
180
180
  })
181
181
 
182
- await sleepAsync(50)
182
+ await flushUpdates()
183
183
 
184
184
  const hr = document.querySelector('shade-markdown-display .md-hr')
185
185
  expect(hr).not.toBeNull()
@@ -196,7 +196,7 @@ describe('MarkdownDisplay', () => {
196
196
  jsxElement: <MarkdownDisplay content="[Click here](https://example.com)" />,
197
197
  })
198
198
 
199
- await sleepAsync(50)
199
+ await flushUpdates()
200
200
 
201
201
  const link = document.querySelector('shade-markdown-display .md-link') as HTMLAnchorElement
202
202
  expect(link).not.toBeNull()
@@ -215,7 +215,7 @@ describe('MarkdownDisplay', () => {
215
215
  jsxElement: <MarkdownDisplay content="![alt text](image.png)" />,
216
216
  })
217
217
 
218
- await sleepAsync(50)
218
+ await flushUpdates()
219
219
 
220
220
  const img = document.querySelector('shade-markdown-display .md-image') as HTMLImageElement
221
221
  expect(img).not.toBeNull()
@@ -233,7 +233,7 @@ describe('MarkdownDisplay', () => {
233
233
  jsxElement: <MarkdownDisplay content="" />,
234
234
  })
235
235
 
236
- await sleepAsync(50)
236
+ await flushUpdates()
237
237
 
238
238
  const root = document.querySelector('shade-markdown-display .md-root')
239
239
  expect(root).not.toBeNull()