@furystack/shades-common-components 13.5.0 → 14.0.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 (356) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +3 -3
  3. package/esm/components/accordion/accordion-item.js +1 -1
  4. package/esm/components/accordion/accordion-item.js.map +1 -1
  5. package/esm/components/accordion/accordion.js +1 -1
  6. package/esm/components/accordion/accordion.js.map +1 -1
  7. package/esm/components/alert.js +1 -1
  8. package/esm/components/alert.js.map +1 -1
  9. package/esm/components/app-bar-link.js +1 -1
  10. package/esm/components/app-bar-link.js.map +1 -1
  11. package/esm/components/app-bar.js +1 -1
  12. package/esm/components/app-bar.js.map +1 -1
  13. package/esm/components/avatar.js +1 -1
  14. package/esm/components/avatar.js.map +1 -1
  15. package/esm/components/badge.js +1 -1
  16. package/esm/components/badge.js.map +1 -1
  17. package/esm/components/breadcrumb.js +1 -1
  18. package/esm/components/breadcrumb.js.map +1 -1
  19. package/esm/components/breadcrumb.spec.js +3 -3
  20. package/esm/components/breadcrumb.spec.js.map +1 -1
  21. package/esm/components/button-group.js +4 -4
  22. package/esm/components/button-group.js.map +1 -1
  23. package/esm/components/button.js +1 -1
  24. package/esm/components/button.js.map +1 -1
  25. package/esm/components/button.spec.js +1 -1
  26. package/esm/components/button.spec.js.map +1 -1
  27. package/esm/components/cache-view.js +1 -1
  28. package/esm/components/cache-view.js.map +1 -1
  29. package/esm/components/cache-view.spec.js +2 -2
  30. package/esm/components/cache-view.spec.js.map +1 -1
  31. package/esm/components/card.js +5 -5
  32. package/esm/components/card.js.map +1 -1
  33. package/esm/components/carousel.js +1 -1
  34. package/esm/components/carousel.js.map +1 -1
  35. package/esm/components/chip.js +1 -1
  36. package/esm/components/chip.js.map +1 -1
  37. package/esm/components/circular-progress.d.ts +2 -4
  38. package/esm/components/circular-progress.d.ts.map +1 -1
  39. package/esm/components/circular-progress.js +3 -6
  40. package/esm/components/circular-progress.js.map +1 -1
  41. package/esm/components/circular-progress.spec.js +19 -14
  42. package/esm/components/circular-progress.spec.js.map +1 -1
  43. package/esm/components/command-palette/command-palette-input.js +1 -1
  44. package/esm/components/command-palette/command-palette-input.js.map +1 -1
  45. package/esm/components/command-palette/command-palette-suggestion-list.js +1 -1
  46. package/esm/components/command-palette/command-palette-suggestion-list.js.map +1 -1
  47. package/esm/components/command-palette/command-palette-suggestion-list.spec.js +1 -1
  48. package/esm/components/command-palette/command-palette-suggestion-list.spec.js.map +1 -1
  49. package/esm/components/command-palette/index.js +1 -1
  50. package/esm/components/command-palette/index.js.map +1 -1
  51. package/esm/components/context-menu/context-menu-item.js +1 -1
  52. package/esm/components/context-menu/context-menu-item.js.map +1 -1
  53. package/esm/components/context-menu/context-menu.js +1 -1
  54. package/esm/components/context-menu/context-menu.js.map +1 -1
  55. package/esm/components/data-grid/body.js +1 -1
  56. package/esm/components/data-grid/body.js.map +1 -1
  57. package/esm/components/data-grid/data-grid-row.js +1 -1
  58. package/esm/components/data-grid/data-grid-row.js.map +1 -1
  59. package/esm/components/data-grid/data-grid.d.ts +5 -2
  60. package/esm/components/data-grid/data-grid.d.ts.map +1 -1
  61. package/esm/components/data-grid/data-grid.js +5 -3
  62. package/esm/components/data-grid/data-grid.js.map +1 -1
  63. package/esm/components/data-grid/data-grid.spec.js +60 -60
  64. package/esm/components/data-grid/data-grid.spec.js.map +1 -1
  65. package/esm/components/data-grid/filters/boolean-filter.d.ts +2 -2
  66. package/esm/components/data-grid/filters/boolean-filter.d.ts.map +1 -1
  67. package/esm/components/data-grid/filters/boolean-filter.js +4 -4
  68. package/esm/components/data-grid/filters/boolean-filter.js.map +1 -1
  69. package/esm/components/data-grid/filters/boolean-filter.spec.js +18 -17
  70. package/esm/components/data-grid/filters/boolean-filter.spec.js.map +1 -1
  71. package/esm/components/data-grid/filters/date-filter.d.ts +2 -2
  72. package/esm/components/data-grid/filters/date-filter.d.ts.map +1 -1
  73. package/esm/components/data-grid/filters/date-filter.js +6 -6
  74. package/esm/components/data-grid/filters/date-filter.js.map +1 -1
  75. package/esm/components/data-grid/filters/date-filter.spec.js +26 -21
  76. package/esm/components/data-grid/filters/date-filter.spec.js.map +1 -1
  77. package/esm/components/data-grid/filters/enum-filter.d.ts +2 -2
  78. package/esm/components/data-grid/filters/enum-filter.d.ts.map +1 -1
  79. package/esm/components/data-grid/filters/enum-filter.js +5 -5
  80. package/esm/components/data-grid/filters/enum-filter.js.map +1 -1
  81. package/esm/components/data-grid/filters/enum-filter.spec.js +21 -19
  82. package/esm/components/data-grid/filters/enum-filter.spec.js.map +1 -1
  83. package/esm/components/data-grid/filters/filter-dropdown.js +1 -1
  84. package/esm/components/data-grid/filters/filter-dropdown.js.map +1 -1
  85. package/esm/components/data-grid/filters/number-filter.d.ts +2 -2
  86. package/esm/components/data-grid/filters/number-filter.d.ts.map +1 -1
  87. package/esm/components/data-grid/filters/number-filter.js +5 -5
  88. package/esm/components/data-grid/filters/number-filter.js.map +1 -1
  89. package/esm/components/data-grid/filters/number-filter.spec.js +23 -21
  90. package/esm/components/data-grid/filters/number-filter.spec.js.map +1 -1
  91. package/esm/components/data-grid/filters/string-filter.d.ts +2 -2
  92. package/esm/components/data-grid/filters/string-filter.d.ts.map +1 -1
  93. package/esm/components/data-grid/filters/string-filter.js +5 -5
  94. package/esm/components/data-grid/filters/string-filter.js.map +1 -1
  95. package/esm/components/data-grid/filters/string-filter.spec.js +21 -19
  96. package/esm/components/data-grid/filters/string-filter.spec.js.map +1 -1
  97. package/esm/components/data-grid/footer.d.ts +2 -2
  98. package/esm/components/data-grid/footer.d.ts.map +1 -1
  99. package/esm/components/data-grid/footer.js +8 -13
  100. package/esm/components/data-grid/footer.js.map +1 -1
  101. package/esm/components/data-grid/footer.spec.js +38 -27
  102. package/esm/components/data-grid/footer.spec.js.map +1 -1
  103. package/esm/components/data-grid/header.d.ts +6 -6
  104. package/esm/components/data-grid/header.d.ts.map +1 -1
  105. package/esm/components/data-grid/header.js +16 -17
  106. package/esm/components/data-grid/header.js.map +1 -1
  107. package/esm/components/data-grid/header.spec.js +66 -60
  108. package/esm/components/data-grid/header.spec.js.map +1 -1
  109. package/esm/components/data-grid/selection-cell.js +1 -1
  110. package/esm/components/data-grid/selection-cell.js.map +1 -1
  111. package/esm/components/dialog.js +1 -1
  112. package/esm/components/dialog.js.map +1 -1
  113. package/esm/components/divider.js +1 -1
  114. package/esm/components/divider.js.map +1 -1
  115. package/esm/components/drawer/drawer-toggle-button.js +1 -1
  116. package/esm/components/drawer/drawer-toggle-button.js.map +1 -1
  117. package/esm/components/drawer/index.js +1 -1
  118. package/esm/components/drawer/index.js.map +1 -1
  119. package/esm/components/dropdown.js +1 -1
  120. package/esm/components/dropdown.js.map +1 -1
  121. package/esm/components/fab.js +1 -1
  122. package/esm/components/fab.js.map +1 -1
  123. package/esm/components/form.js +1 -1
  124. package/esm/components/form.js.map +1 -1
  125. package/esm/components/grid.js +1 -1
  126. package/esm/components/grid.js.map +1 -1
  127. package/esm/components/icons/icon.js +1 -1
  128. package/esm/components/icons/icon.js.map +1 -1
  129. package/esm/components/image.js +2 -2
  130. package/esm/components/image.js.map +1 -1
  131. package/esm/components/inputs/autocomplete.js +1 -1
  132. package/esm/components/inputs/autocomplete.js.map +1 -1
  133. package/esm/components/inputs/checkbox.js +1 -1
  134. package/esm/components/inputs/checkbox.js.map +1 -1
  135. package/esm/components/inputs/checkbox.spec.js +1 -1
  136. package/esm/components/inputs/checkbox.spec.js.map +1 -1
  137. package/esm/components/inputs/input-number.js +1 -1
  138. package/esm/components/inputs/input-number.js.map +1 -1
  139. package/esm/components/inputs/input-number.spec.js +1 -1
  140. package/esm/components/inputs/input-number.spec.js.map +1 -1
  141. package/esm/components/inputs/input.js +1 -1
  142. package/esm/components/inputs/input.js.map +1 -1
  143. package/esm/components/inputs/input.spec.js +1 -1
  144. package/esm/components/inputs/input.spec.js.map +1 -1
  145. package/esm/components/inputs/radio-group.js +1 -1
  146. package/esm/components/inputs/radio-group.js.map +1 -1
  147. package/esm/components/inputs/radio-group.spec.js +1 -1
  148. package/esm/components/inputs/radio-group.spec.js.map +1 -1
  149. package/esm/components/inputs/radio.js +1 -1
  150. package/esm/components/inputs/radio.js.map +1 -1
  151. package/esm/components/inputs/radio.spec.js +1 -1
  152. package/esm/components/inputs/radio.spec.js.map +1 -1
  153. package/esm/components/inputs/select.js +1 -1
  154. package/esm/components/inputs/select.js.map +1 -1
  155. package/esm/components/inputs/slider.js +1 -1
  156. package/esm/components/inputs/slider.js.map +1 -1
  157. package/esm/components/inputs/switch.js +1 -1
  158. package/esm/components/inputs/switch.js.map +1 -1
  159. package/esm/components/inputs/switch.spec.js +1 -1
  160. package/esm/components/inputs/switch.spec.js.map +1 -1
  161. package/esm/components/inputs/text-area.js +1 -1
  162. package/esm/components/inputs/text-area.js.map +1 -1
  163. package/esm/components/inputs/text-area.spec.js +1 -1
  164. package/esm/components/inputs/text-area.spec.js.map +1 -1
  165. package/esm/components/linear-progress.d.ts +2 -4
  166. package/esm/components/linear-progress.d.ts.map +1 -1
  167. package/esm/components/linear-progress.js +3 -6
  168. package/esm/components/linear-progress.js.map +1 -1
  169. package/esm/components/linear-progress.spec.js +21 -18
  170. package/esm/components/linear-progress.spec.js.map +1 -1
  171. package/esm/components/list/list-item.js +1 -1
  172. package/esm/components/list/list-item.js.map +1 -1
  173. package/esm/components/list/list.js +1 -1
  174. package/esm/components/list/list.js.map +1 -1
  175. package/esm/components/loader.js +1 -1
  176. package/esm/components/loader.js.map +1 -1
  177. package/esm/components/loader.spec.js +1 -1
  178. package/esm/components/loader.spec.js.map +1 -1
  179. package/esm/components/markdown/markdown-display.js +1 -1
  180. package/esm/components/markdown/markdown-display.js.map +1 -1
  181. package/esm/components/markdown/markdown-display.spec.js +1 -1
  182. package/esm/components/markdown/markdown-display.spec.js.map +1 -1
  183. package/esm/components/markdown/markdown-editor.js +1 -1
  184. package/esm/components/markdown/markdown-editor.js.map +1 -1
  185. package/esm/components/markdown/markdown-editor.spec.js +1 -1
  186. package/esm/components/markdown/markdown-editor.spec.js.map +1 -1
  187. package/esm/components/markdown/markdown-input.js +1 -1
  188. package/esm/components/markdown/markdown-input.js.map +1 -1
  189. package/esm/components/markdown/markdown-input.spec.js +1 -1
  190. package/esm/components/markdown/markdown-input.spec.js.map +1 -1
  191. package/esm/components/menu/menu.js +1 -1
  192. package/esm/components/menu/menu.js.map +1 -1
  193. package/esm/components/modal.js +1 -1
  194. package/esm/components/modal.js.map +1 -1
  195. package/esm/components/modal.spec.js +3 -3
  196. package/esm/components/modal.spec.js.map +1 -1
  197. package/esm/components/noty-list.js +2 -2
  198. package/esm/components/noty-list.js.map +1 -1
  199. package/esm/components/page-container/index.js +1 -1
  200. package/esm/components/page-container/index.js.map +1 -1
  201. package/esm/components/page-container/page-header.js +1 -1
  202. package/esm/components/page-container/page-header.js.map +1 -1
  203. package/esm/components/page-layout/index.js +1 -1
  204. package/esm/components/page-layout/index.js.map +1 -1
  205. package/esm/components/pagination.js +1 -1
  206. package/esm/components/pagination.js.map +1 -1
  207. package/esm/components/paper.js +1 -1
  208. package/esm/components/paper.js.map +1 -1
  209. package/esm/components/rating.js +1 -1
  210. package/esm/components/rating.js.map +1 -1
  211. package/esm/components/rating.spec.js +1 -1
  212. package/esm/components/rating.spec.js.map +1 -1
  213. package/esm/components/result.js +1 -1
  214. package/esm/components/result.js.map +1 -1
  215. package/esm/components/skeleton.js +1 -1
  216. package/esm/components/skeleton.js.map +1 -1
  217. package/esm/components/suggest/index.js +1 -1
  218. package/esm/components/suggest/index.js.map +1 -1
  219. package/esm/components/suggest/index.spec.js +1 -1
  220. package/esm/components/suggest/index.spec.js.map +1 -1
  221. package/esm/components/suggest/suggest-input.js +1 -1
  222. package/esm/components/suggest/suggest-input.js.map +1 -1
  223. package/esm/components/suggest/suggest-input.spec.js +1 -1
  224. package/esm/components/suggest/suggest-input.spec.js.map +1 -1
  225. package/esm/components/suggest/suggestion-list.js +1 -1
  226. package/esm/components/suggest/suggestion-list.js.map +1 -1
  227. package/esm/components/suggest/suggestion-list.spec.js +1 -1
  228. package/esm/components/suggest/suggestion-list.spec.js.map +1 -1
  229. package/esm/components/tabs.js +2 -2
  230. package/esm/components/tabs.js.map +1 -1
  231. package/esm/components/timeline.js +2 -2
  232. package/esm/components/timeline.js.map +1 -1
  233. package/esm/components/tooltip.js +1 -1
  234. package/esm/components/tooltip.js.map +1 -1
  235. package/esm/components/tree/tree-item.js +1 -1
  236. package/esm/components/tree/tree-item.js.map +1 -1
  237. package/esm/components/tree/tree.js +1 -1
  238. package/esm/components/tree/tree.js.map +1 -1
  239. package/esm/components/typography.js +1 -1
  240. package/esm/components/typography.js.map +1 -1
  241. package/esm/components/wizard/index.js +1 -1
  242. package/esm/components/wizard/index.js.map +1 -1
  243. package/esm/components/wizard/index.spec.js +3 -3
  244. package/esm/components/wizard/index.spec.js.map +1 -1
  245. package/package.json +3 -3
  246. package/src/components/accordion/accordion-item.tsx +1 -1
  247. package/src/components/accordion/accordion.tsx +1 -1
  248. package/src/components/alert.tsx +1 -1
  249. package/src/components/app-bar-link.tsx +1 -1
  250. package/src/components/app-bar.tsx +1 -1
  251. package/src/components/avatar.tsx +1 -1
  252. package/src/components/badge.tsx +1 -1
  253. package/src/components/breadcrumb.spec.tsx +3 -3
  254. package/src/components/breadcrumb.tsx +1 -1
  255. package/src/components/button-group.tsx +4 -4
  256. package/src/components/button.spec.tsx +1 -1
  257. package/src/components/button.tsx +1 -1
  258. package/src/components/cache-view.spec.tsx +2 -2
  259. package/src/components/cache-view.tsx +3 -3
  260. package/src/components/card.tsx +5 -5
  261. package/src/components/carousel.tsx +1 -1
  262. package/src/components/chip.tsx +1 -1
  263. package/src/components/circular-progress.spec.tsx +20 -14
  264. package/src/components/circular-progress.tsx +5 -11
  265. package/src/components/command-palette/command-palette-input.tsx +1 -1
  266. package/src/components/command-palette/command-palette-suggestion-list.spec.tsx +1 -1
  267. package/src/components/command-palette/command-palette-suggestion-list.tsx +1 -1
  268. package/src/components/command-palette/index.tsx +1 -1
  269. package/src/components/context-menu/context-menu-item.tsx +1 -1
  270. package/src/components/context-menu/context-menu.tsx +1 -1
  271. package/src/components/data-grid/body.tsx +1 -1
  272. package/src/components/data-grid/data-grid-row.tsx +1 -1
  273. package/src/components/data-grid/data-grid.spec.tsx +62 -36
  274. package/src/components/data-grid/data-grid.tsx +14 -8
  275. package/src/components/data-grid/filters/boolean-filter.spec.tsx +29 -18
  276. package/src/components/data-grid/filters/boolean-filter.tsx +6 -6
  277. package/src/components/data-grid/filters/date-filter.spec.tsx +35 -22
  278. package/src/components/data-grid/filters/date-filter.tsx +8 -8
  279. package/src/components/data-grid/filters/enum-filter.spec.tsx +35 -20
  280. package/src/components/data-grid/filters/enum-filter.tsx +7 -7
  281. package/src/components/data-grid/filters/filter-dropdown.tsx +1 -1
  282. package/src/components/data-grid/filters/number-filter.spec.tsx +32 -22
  283. package/src/components/data-grid/filters/number-filter.tsx +7 -7
  284. package/src/components/data-grid/filters/string-filter.spec.tsx +32 -20
  285. package/src/components/data-grid/filters/string-filter.tsx +7 -7
  286. package/src/components/data-grid/footer.spec.tsx +79 -31
  287. package/src/components/data-grid/footer.tsx +10 -15
  288. package/src/components/data-grid/header.spec.tsx +152 -68
  289. package/src/components/data-grid/header.tsx +64 -27
  290. package/src/components/data-grid/selection-cell.tsx +1 -1
  291. package/src/components/dialog.tsx +1 -1
  292. package/src/components/divider.tsx +1 -1
  293. package/src/components/drawer/drawer-toggle-button.tsx +1 -1
  294. package/src/components/drawer/index.tsx +1 -1
  295. package/src/components/dropdown.tsx +1 -1
  296. package/src/components/fab.tsx +1 -1
  297. package/src/components/form.tsx +1 -1
  298. package/src/components/grid.tsx +1 -1
  299. package/src/components/icons/icon.tsx +1 -1
  300. package/src/components/image.tsx +2 -2
  301. package/src/components/inputs/autocomplete.tsx +1 -1
  302. package/src/components/inputs/checkbox.spec.tsx +1 -1
  303. package/src/components/inputs/checkbox.tsx +1 -1
  304. package/src/components/inputs/input-number.spec.tsx +1 -1
  305. package/src/components/inputs/input-number.tsx +1 -1
  306. package/src/components/inputs/input.spec.tsx +1 -1
  307. package/src/components/inputs/input.tsx +1 -1
  308. package/src/components/inputs/radio-group.spec.tsx +1 -1
  309. package/src/components/inputs/radio-group.tsx +1 -1
  310. package/src/components/inputs/radio.spec.tsx +1 -1
  311. package/src/components/inputs/radio.tsx +1 -1
  312. package/src/components/inputs/select.tsx +1 -1
  313. package/src/components/inputs/slider.tsx +1 -1
  314. package/src/components/inputs/switch.spec.tsx +1 -1
  315. package/src/components/inputs/switch.tsx +1 -1
  316. package/src/components/inputs/text-area.spec.tsx +1 -1
  317. package/src/components/inputs/text-area.tsx +1 -1
  318. package/src/components/linear-progress.spec.tsx +22 -18
  319. package/src/components/linear-progress.tsx +5 -11
  320. package/src/components/list/list-item.tsx +1 -1
  321. package/src/components/list/list.tsx +1 -1
  322. package/src/components/loader.spec.tsx +1 -1
  323. package/src/components/loader.tsx +1 -1
  324. package/src/components/markdown/markdown-display.spec.tsx +1 -1
  325. package/src/components/markdown/markdown-display.tsx +1 -1
  326. package/src/components/markdown/markdown-editor.spec.tsx +1 -1
  327. package/src/components/markdown/markdown-editor.tsx +1 -1
  328. package/src/components/markdown/markdown-input.spec.tsx +1 -1
  329. package/src/components/markdown/markdown-input.tsx +1 -1
  330. package/src/components/menu/menu.tsx +1 -1
  331. package/src/components/modal.spec.tsx +3 -3
  332. package/src/components/modal.tsx +1 -1
  333. package/src/components/noty-list.tsx +2 -2
  334. package/src/components/page-container/index.tsx +1 -1
  335. package/src/components/page-container/page-header.tsx +1 -1
  336. package/src/components/page-layout/index.tsx +1 -1
  337. package/src/components/pagination.tsx +1 -1
  338. package/src/components/paper.tsx +1 -1
  339. package/src/components/rating.spec.tsx +1 -1
  340. package/src/components/rating.tsx +1 -1
  341. package/src/components/result.tsx +1 -1
  342. package/src/components/skeleton.tsx +1 -1
  343. package/src/components/suggest/index.spec.tsx +1 -1
  344. package/src/components/suggest/index.tsx +1 -1
  345. package/src/components/suggest/suggest-input.spec.tsx +1 -1
  346. package/src/components/suggest/suggest-input.tsx +1 -1
  347. package/src/components/suggest/suggestion-list.spec.tsx +1 -1
  348. package/src/components/suggest/suggestion-list.tsx +1 -1
  349. package/src/components/tabs.tsx +2 -2
  350. package/src/components/timeline.tsx +2 -2
  351. package/src/components/tooltip.tsx +1 -1
  352. package/src/components/tree/tree-item.tsx +1 -1
  353. package/src/components/tree/tree.tsx +1 -1
  354. package/src/components/typography.tsx +1 -1
  355. package/src/components/wizard/index.spec.tsx +3 -3
  356. package/src/components/wizard/index.tsx +1 -1
@@ -1,6 +1,6 @@
1
1
  import { Injector } from '@furystack/inject'
2
2
  import { createComponent, flushUpdates, initializeShadeRoot } from '@furystack/shades'
3
- import { ObservableValue, usingAsync } from '@furystack/utils'
3
+ import { usingAsync } from '@furystack/utils'
4
4
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
5
5
  import type { FilterableFindOptions } from '../data-grid.js'
6
6
  import { BooleanFilter } from './boolean-filter.js'
@@ -14,24 +14,32 @@ describe('BooleanFilter', () => {
14
14
  document.body.innerHTML = ''
15
15
  })
16
16
 
17
- const createFindOptions = (options: Partial<FilterableFindOptions> = {}): ObservableValue<FilterableFindOptions> => {
18
- return new ObservableValue<FilterableFindOptions>(options)
17
+ const createFindOptions = (options: Partial<FilterableFindOptions> = {}): FilterableFindOptions => {
18
+ return options as FilterableFindOptions
19
19
  }
20
20
 
21
21
  const renderBooleanFilter = async (
22
- findOptions: ObservableValue<FilterableFindOptions>,
22
+ findOptions: FilterableFindOptions,
23
23
  field = 'isActive',
24
24
  onClose = vi.fn(),
25
+ onFindOptionsChange = vi.fn(),
25
26
  ) => {
26
27
  const injector = new Injector()
27
28
  const rootElement = document.getElementById('root')!
28
29
  initializeShadeRoot({
29
30
  injector,
30
31
  rootElement,
31
- jsxElement: <BooleanFilter field={field} findOptions={findOptions} onClose={onClose} />,
32
+ jsxElement: (
33
+ <BooleanFilter
34
+ field={field}
35
+ findOptions={findOptions}
36
+ onFindOptionsChange={onFindOptionsChange}
37
+ onClose={onClose}
38
+ />
39
+ ),
32
40
  })
33
41
  await flushUpdates()
34
- return { injector, onClose }
42
+ return { injector, onClose, onFindOptionsChange }
35
43
  }
36
44
 
37
45
  it('should render three options: True, False, Any', async () => {
@@ -70,7 +78,7 @@ describe('BooleanFilter', () => {
70
78
 
71
79
  it('should set filter to $eq: true when "True" is clicked', async () => {
72
80
  const findOptions = createFindOptions()
73
- const { injector, onClose } = await renderBooleanFilter(findOptions)
81
+ const { injector, onClose, onFindOptionsChange } = await renderBooleanFilter(findOptions)
74
82
  await usingAsync(injector, async () => {
75
83
  const trueButton = document.querySelector(
76
84
  'shade-segmented-control button[data-value="true"]',
@@ -78,14 +86,14 @@ describe('BooleanFilter', () => {
78
86
  trueButton?.click()
79
87
  await flushUpdates()
80
88
 
81
- expect(findOptions.getValue().filter).toEqual({ isActive: { $eq: true } })
89
+ expect(onFindOptionsChange).toHaveBeenCalledWith(expect.objectContaining({ filter: { isActive: { $eq: true } } }))
82
90
  expect(onClose).toHaveBeenCalled()
83
91
  })
84
92
  })
85
93
 
86
94
  it('should set filter to $eq: false when "False" is clicked', async () => {
87
95
  const findOptions = createFindOptions()
88
- const { injector, onClose } = await renderBooleanFilter(findOptions)
96
+ const { injector, onClose, onFindOptionsChange } = await renderBooleanFilter(findOptions)
89
97
  await usingAsync(injector, async () => {
90
98
  const falseButton = document.querySelector(
91
99
  'shade-segmented-control button[data-value="false"]',
@@ -93,42 +101,45 @@ describe('BooleanFilter', () => {
93
101
  falseButton?.click()
94
102
  await flushUpdates()
95
103
 
96
- expect(findOptions.getValue().filter).toEqual({ isActive: { $eq: false } })
104
+ expect(onFindOptionsChange).toHaveBeenCalledWith(
105
+ expect.objectContaining({ filter: { isActive: { $eq: false } } }),
106
+ )
97
107
  expect(onClose).toHaveBeenCalled()
98
108
  })
99
109
  })
100
110
 
101
111
  it('should remove filter when "Any" is clicked', async () => {
102
112
  const findOptions = createFindOptions({ filter: { isActive: { $eq: true } } })
103
- const { injector, onClose } = await renderBooleanFilter(findOptions)
113
+ const { injector, onClose, onFindOptionsChange } = await renderBooleanFilter(findOptions)
104
114
  await usingAsync(injector, async () => {
105
115
  const anyButton = document.querySelector('shade-segmented-control button[data-value="any"]') as HTMLButtonElement
106
116
  anyButton?.click()
107
117
  await flushUpdates()
108
118
 
109
- expect(findOptions.getValue().filter?.isActive).toBeUndefined()
119
+ const updatedOptions = onFindOptionsChange.mock.lastCall?.[0] as FilterableFindOptions
120
+ expect(updatedOptions.filter?.isActive).toBeUndefined()
110
121
  expect(onClose).toHaveBeenCalled()
111
122
  })
112
123
  })
113
124
 
114
125
  it('should preserve filters on other fields', async () => {
115
126
  const findOptions = createFindOptions({ filter: { isActive: { $eq: true }, name: { $regex: 'test' } } })
116
- const { injector, onClose } = await renderBooleanFilter(findOptions)
127
+ const { injector, onClose, onFindOptionsChange } = await renderBooleanFilter(findOptions)
117
128
  await usingAsync(injector, async () => {
118
129
  const anyButton = document.querySelector('shade-segmented-control button[data-value="any"]') as HTMLButtonElement
119
130
  anyButton?.click()
120
131
  await flushUpdates()
121
132
 
122
- const updatedFilter = findOptions.getValue().filter
123
- expect(updatedFilter?.isActive).toBeUndefined()
124
- expect(updatedFilter?.name).toEqual({ $regex: 'test' })
133
+ const updatedOptions = onFindOptionsChange.mock.lastCall?.[0] as FilterableFindOptions
134
+ expect(updatedOptions.filter?.isActive).toBeUndefined()
135
+ expect(updatedOptions.filter?.name).toEqual({ $regex: 'test' })
125
136
  expect(onClose).toHaveBeenCalled()
126
137
  })
127
138
  })
128
139
 
129
140
  it('should reset skip to 0 when applying filter', async () => {
130
141
  const findOptions = createFindOptions({ skip: 20 })
131
- const { injector } = await renderBooleanFilter(findOptions)
142
+ const { injector, onFindOptionsChange } = await renderBooleanFilter(findOptions)
132
143
  await usingAsync(injector, async () => {
133
144
  const trueButton = document.querySelector(
134
145
  'shade-segmented-control button[data-value="true"]',
@@ -136,7 +147,7 @@ describe('BooleanFilter', () => {
136
147
  trueButton?.click()
137
148
  await flushUpdates()
138
149
 
139
- expect(findOptions.getValue().skip).toBe(0)
150
+ expect(onFindOptionsChange).toHaveBeenCalledWith(expect.objectContaining({ skip: 0 }))
140
151
  })
141
152
  })
142
153
  })
@@ -1,5 +1,4 @@
1
1
  import { createComponent, Shade } from '@furystack/shades'
2
- import type { ObservableValue } from '@furystack/utils'
3
2
  import { SegmentedControl } from '../../button-group.js'
4
3
  import { cssVariableTheme } from '../../../services/css-variable-theme.js'
5
4
  import type { FilterableFindOptions } from '../data-grid.js'
@@ -8,13 +7,14 @@ type BooleanFilterValue = 'true' | 'false' | 'any'
8
7
 
9
8
  export const BooleanFilter = Shade<{
10
9
  field: string
11
- findOptions: ObservableValue<FilterableFindOptions>
10
+ findOptions: FilterableFindOptions
11
+ onFindOptionsChange: (options: FilterableFindOptions) => void
12
12
  onClose: () => void
13
13
  }>({
14
- shadowDomName: 'data-grid-boolean-filter',
14
+ customElementName: 'data-grid-boolean-filter',
15
15
  css: { fontFamily: cssVariableTheme.typography.fontFamily },
16
- render: ({ props, useObservable }) => {
17
- const [findOptions, setFindOptions] = useObservable('findOptions', props.findOptions)
16
+ render: ({ props }) => {
17
+ const { findOptions } = props
18
18
 
19
19
  const currentFilter = findOptions.filter?.[props.field] as { $eq?: boolean } | undefined
20
20
  const currentValue: BooleanFilterValue =
@@ -27,7 +27,7 @@ export const BooleanFilter = Shade<{
27
27
  } else {
28
28
  filter[props.field] = { $eq: value === 'true' }
29
29
  }
30
- setFindOptions({ ...findOptions, filter, skip: 0 })
30
+ props.onFindOptionsChange({ ...findOptions, filter, skip: 0 })
31
31
  props.onClose()
32
32
  }
33
33
 
@@ -1,6 +1,6 @@
1
1
  import { Injector } from '@furystack/inject'
2
2
  import { createComponent, flushUpdates, initializeShadeRoot } from '@furystack/shades'
3
- import { ObservableValue, usingAsync } from '@furystack/utils'
3
+ import { usingAsync } from '@furystack/utils'
4
4
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
5
5
  import type { FilterableFindOptions } from '../data-grid.js'
6
6
  import { DateFilter } from './date-filter.js'
@@ -14,24 +14,32 @@ describe('DateFilter', () => {
14
14
  document.body.innerHTML = ''
15
15
  })
16
16
 
17
- const createFindOptions = (options: Partial<FilterableFindOptions> = {}): ObservableValue<FilterableFindOptions> => {
18
- return new ObservableValue<FilterableFindOptions>(options)
17
+ const createFindOptions = (options: Partial<FilterableFindOptions> = {}): FilterableFindOptions => {
18
+ return options as FilterableFindOptions
19
19
  }
20
20
 
21
21
  const renderDateFilter = async (
22
- findOptions: ObservableValue<FilterableFindOptions>,
22
+ findOptions: FilterableFindOptions,
23
23
  field = 'createdAt',
24
24
  onClose = vi.fn(),
25
+ onFindOptionsChange = vi.fn(),
25
26
  ) => {
26
27
  const injector = new Injector()
27
28
  const rootElement = document.getElementById('root')!
28
29
  initializeShadeRoot({
29
30
  injector,
30
31
  rootElement,
31
- jsxElement: <DateFilter field={field} findOptions={findOptions} onClose={onClose} />,
32
+ jsxElement: (
33
+ <DateFilter
34
+ field={field}
35
+ findOptions={findOptions}
36
+ onFindOptionsChange={onFindOptionsChange}
37
+ onClose={onClose}
38
+ />
39
+ ),
32
40
  })
33
41
  await flushUpdates()
34
- return { injector, onClose }
42
+ return { injector, onClose, onFindOptionsChange }
35
43
  }
36
44
 
37
45
  it('should render mode segmented control and date input', async () => {
@@ -47,7 +55,7 @@ describe('DateFilter', () => {
47
55
 
48
56
  it('should apply "before" filter on submit', async () => {
49
57
  const findOptions = createFindOptions()
50
- const { injector, onClose } = await renderDateFilter(findOptions)
58
+ const { injector, onClose, onFindOptionsChange } = await renderDateFilter(findOptions)
51
59
  await usingAsync(injector, async () => {
52
60
  const input = document.querySelector('[data-testid="date-filter-value"]') as HTMLInputElement
53
61
  input.value = '2025-06-15T10:30'
@@ -57,7 +65,8 @@ describe('DateFilter', () => {
57
65
  form.dispatchEvent(new Event('submit', { bubbles: true }))
58
66
  await flushUpdates()
59
67
 
60
- const filter = findOptions.getValue().filter?.createdAt as Record<string, Date>
68
+ const updatedOptions = onFindOptionsChange.mock.lastCall?.[0] as FilterableFindOptions
69
+ const filter = updatedOptions.filter?.createdAt as Record<string, Date>
61
70
  expect(filter.$lt).toBeInstanceOf(Date)
62
71
  expect(filter.$lt.toISOString()).toBe(new Date('2025-06-15T10:30').toISOString())
63
72
  expect(onClose).toHaveBeenCalled()
@@ -66,7 +75,7 @@ describe('DateFilter', () => {
66
75
 
67
76
  it('should apply "after" filter when mode is changed', async () => {
68
77
  const findOptions = createFindOptions()
69
- const { injector, onClose } = await renderDateFilter(findOptions)
78
+ const { injector, onClose, onFindOptionsChange } = await renderDateFilter(findOptions)
70
79
  await usingAsync(injector, async () => {
71
80
  const afterButton = document.querySelector(
72
81
  'shade-segmented-control button[data-value="after"]',
@@ -82,7 +91,8 @@ describe('DateFilter', () => {
82
91
  form.dispatchEvent(new Event('submit', { bubbles: true }))
83
92
  await flushUpdates()
84
93
 
85
- const filter = findOptions.getValue().filter?.createdAt as Record<string, Date>
94
+ const updatedOptions = onFindOptionsChange.mock.lastCall?.[0] as FilterableFindOptions
95
+ const filter = updatedOptions.filter?.createdAt as Record<string, Date>
86
96
  expect(filter.$gt).toBeInstanceOf(Date)
87
97
  expect(onClose).toHaveBeenCalled()
88
98
  })
@@ -90,7 +100,7 @@ describe('DateFilter', () => {
90
100
 
91
101
  it('should apply "between" filter with both dates', async () => {
92
102
  const findOptions = createFindOptions()
93
- const { injector, onClose } = await renderDateFilter(findOptions)
103
+ const { injector, onClose, onFindOptionsChange } = await renderDateFilter(findOptions)
94
104
  await usingAsync(injector, async () => {
95
105
  const betweenButton = document.querySelector(
96
106
  'shade-segmented-control button[data-value="between"]',
@@ -110,7 +120,8 @@ describe('DateFilter', () => {
110
120
  form.dispatchEvent(new Event('submit', { bubbles: true }))
111
121
  await flushUpdates()
112
122
 
113
- const filter = findOptions.getValue().filter?.createdAt as Record<string, Date>
123
+ const updatedOptions = onFindOptionsChange.mock.lastCall?.[0] as FilterableFindOptions
124
+ const filter = updatedOptions.filter?.createdAt as Record<string, Date>
114
125
  expect(filter.$gte).toBeInstanceOf(Date)
115
126
  expect(filter.$lte).toBeInstanceOf(Date)
116
127
  expect(onClose).toHaveBeenCalled()
@@ -119,20 +130,21 @@ describe('DateFilter', () => {
119
130
 
120
131
  it('should clear filter when Clear button is clicked', async () => {
121
132
  const findOptions = createFindOptions({ filter: { createdAt: { $lt: new Date() } } })
122
- const { injector, onClose } = await renderDateFilter(findOptions)
133
+ const { injector, onClose, onFindOptionsChange } = await renderDateFilter(findOptions)
123
134
  await usingAsync(injector, async () => {
124
135
  const clearButton = Array.from(document.querySelectorAll('button')).find((b) => b.textContent?.trim() === 'Clear')
125
136
  clearButton?.click()
126
137
  await flushUpdates()
127
138
 
128
- expect(findOptions.getValue().filter?.createdAt).toBeUndefined()
139
+ const updatedOptions = onFindOptionsChange.mock.lastCall?.[0] as FilterableFindOptions
140
+ expect(updatedOptions.filter?.createdAt).toBeUndefined()
129
141
  expect(onClose).toHaveBeenCalled()
130
142
  })
131
143
  })
132
144
 
133
145
  it('should remove filter when submitting empty date', async () => {
134
146
  const findOptions = createFindOptions({ filter: { createdAt: { $lt: new Date() } } })
135
- const { injector, onClose } = await renderDateFilter(findOptions)
147
+ const { injector, onClose, onFindOptionsChange } = await renderDateFilter(findOptions)
136
148
  await usingAsync(injector, async () => {
137
149
  const input = document.querySelector('[data-testid="date-filter-value"]') as HTMLInputElement
138
150
  input.value = ''
@@ -142,7 +154,8 @@ describe('DateFilter', () => {
142
154
  form.dispatchEvent(new Event('submit', { bubbles: true }))
143
155
  await flushUpdates()
144
156
 
145
- expect(findOptions.getValue().filter?.createdAt).toBeUndefined()
157
+ const updatedOptions = onFindOptionsChange.mock.lastCall?.[0] as FilterableFindOptions
158
+ expect(updatedOptions.filter?.createdAt).toBeUndefined()
146
159
  expect(onClose).toHaveBeenCalled()
147
160
  })
148
161
  })
@@ -151,21 +164,21 @@ describe('DateFilter', () => {
151
164
  const findOptions = createFindOptions({
152
165
  filter: { createdAt: { $lt: new Date() }, name: { $regex: 'keep' } },
153
166
  })
154
- const { injector } = await renderDateFilter(findOptions)
167
+ const { injector, onFindOptionsChange } = await renderDateFilter(findOptions)
155
168
  await usingAsync(injector, async () => {
156
169
  const clearButton = Array.from(document.querySelectorAll('button')).find((b) => b.textContent?.trim() === 'Clear')
157
170
  clearButton?.click()
158
171
  await flushUpdates()
159
172
 
160
- const updatedFilter = findOptions.getValue().filter
161
- expect(updatedFilter?.createdAt).toBeUndefined()
162
- expect(updatedFilter?.name).toEqual({ $regex: 'keep' })
173
+ const updatedOptions = onFindOptionsChange.mock.lastCall?.[0] as FilterableFindOptions
174
+ expect(updatedOptions.filter?.createdAt).toBeUndefined()
175
+ expect(updatedOptions.filter?.name).toEqual({ $regex: 'keep' })
163
176
  })
164
177
  })
165
178
 
166
179
  it('should reset skip to 0 when applying filter', async () => {
167
180
  const findOptions = createFindOptions({ skip: 20 })
168
- const { injector } = await renderDateFilter(findOptions)
181
+ const { injector, onFindOptionsChange } = await renderDateFilter(findOptions)
169
182
  await usingAsync(injector, async () => {
170
183
  const input = document.querySelector('[data-testid="date-filter-value"]') as HTMLInputElement
171
184
  input.value = '2025-06-15T10:30'
@@ -175,7 +188,7 @@ describe('DateFilter', () => {
175
188
  form.dispatchEvent(new Event('submit', { bubbles: true }))
176
189
  await flushUpdates()
177
190
 
178
- expect(findOptions.getValue().skip).toBe(0)
191
+ expect(onFindOptionsChange).toHaveBeenCalledWith(expect.objectContaining({ skip: 0 }))
179
192
  })
180
193
  })
181
194
  })
@@ -1,5 +1,4 @@
1
1
  import { createComponent, Shade } from '@furystack/shades'
2
- import type { ObservableValue } from '@furystack/utils'
3
2
  import { SegmentedControl } from '../../button-group.js'
4
3
  import { Button } from '../../button.js'
5
4
  import { close as closeIcon, search as searchIcon } from '../../icons/icon-definitions.js'
@@ -12,17 +11,18 @@ type DateMode = 'before' | 'after' | 'between'
12
11
 
13
12
  export const DateFilter = Shade<{
14
13
  field: string
15
- findOptions: ObservableValue<FilterableFindOptions>
14
+ findOptions: FilterableFindOptions
15
+ onFindOptionsChange: (options: FilterableFindOptions) => void
16
16
  onClose: () => void
17
17
  }>({
18
- shadowDomName: 'data-grid-date-filter',
18
+ customElementName: 'data-grid-date-filter',
19
19
  css: {
20
20
  ...filterBaseCss,
21
21
  fontFamily: cssVariableTheme.typography.fontFamily,
22
22
  '& input[type="datetime-local"]': filterInputCss,
23
23
  },
24
- render: ({ props, useObservable, useState }) => {
25
- const [findOptions, setFindOptions] = useObservable('findOptions', props.findOptions)
24
+ render: ({ props, useState }) => {
25
+ const { findOptions } = props
26
26
 
27
27
  const currentFilter = findOptions.filter?.[props.field] as
28
28
  | { $lt?: Date; $gt?: Date; $gte?: Date; $lte?: Date }
@@ -63,7 +63,7 @@ export const DateFilter = Shade<{
63
63
  const filter = { ...findOptions.filter }
64
64
  if (!dateValue) {
65
65
  delete filter[props.field]
66
- setFindOptions({ ...findOptions, filter, skip: 0 })
66
+ props.onFindOptionsChange({ ...findOptions, filter, skip: 0 })
67
67
  props.onClose()
68
68
  return
69
69
  }
@@ -87,14 +87,14 @@ export const DateFilter = Shade<{
87
87
  }
88
88
 
89
89
  filter[props.field] = filterValue
90
- setFindOptions({ ...findOptions, filter, skip: 0 })
90
+ props.onFindOptionsChange({ ...findOptions, filter, skip: 0 })
91
91
  props.onClose()
92
92
  }
93
93
 
94
94
  const clearFilter = () => {
95
95
  const filter = { ...findOptions.filter }
96
96
  delete filter[props.field]
97
- setFindOptions({ ...findOptions, filter, skip: 0 })
97
+ props.onFindOptionsChange({ ...findOptions, filter, skip: 0 })
98
98
  props.onClose()
99
99
  }
100
100
 
@@ -1,6 +1,6 @@
1
1
  import { Injector } from '@furystack/inject'
2
2
  import { createComponent, flushUpdates, initializeShadeRoot } from '@furystack/shades'
3
- import { ObservableValue, usingAsync } from '@furystack/utils'
3
+ import { usingAsync } from '@furystack/utils'
4
4
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
5
5
  import type { FilterableFindOptions } from '../data-grid.js'
6
6
  import { EnumFilter } from './enum-filter.js'
@@ -20,25 +20,34 @@ describe('EnumFilter', () => {
20
20
  document.body.innerHTML = ''
21
21
  })
22
22
 
23
- const createFindOptions = (options: Partial<FilterableFindOptions> = {}): ObservableValue<FilterableFindOptions> => {
24
- return new ObservableValue<FilterableFindOptions>(options)
23
+ const createFindOptions = (options: Partial<FilterableFindOptions> = {}): FilterableFindOptions => {
24
+ return options as FilterableFindOptions
25
25
  }
26
26
 
27
27
  const renderEnumFilter = async (
28
- findOptions: ObservableValue<FilterableFindOptions>,
28
+ findOptions: FilterableFindOptions,
29
29
  field = 'role',
30
30
  values = enumValues,
31
31
  onClose = vi.fn(),
32
+ onFindOptionsChange = vi.fn(),
32
33
  ) => {
33
34
  const injector = new Injector()
34
35
  const rootElement = document.getElementById('root')!
35
36
  initializeShadeRoot({
36
37
  injector,
37
38
  rootElement,
38
- jsxElement: <EnumFilter field={field} values={values} findOptions={findOptions} onClose={onClose} />,
39
+ jsxElement: (
40
+ <EnumFilter
41
+ field={field}
42
+ values={values}
43
+ findOptions={findOptions}
44
+ onFindOptionsChange={onFindOptionsChange}
45
+ onClose={onClose}
46
+ />
47
+ ),
39
48
  })
40
49
  await flushUpdates()
41
- return { injector, onClose }
50
+ return { injector, onClose, onFindOptionsChange }
42
51
  }
43
52
 
44
53
  it('should render mode control and checkboxes for each value', async () => {
@@ -54,7 +63,7 @@ describe('EnumFilter', () => {
54
63
 
55
64
  it('should apply $in filter when values are selected and Apply is clicked', async () => {
56
65
  const findOptions = createFindOptions()
57
- const { injector, onClose } = await renderEnumFilter(findOptions)
66
+ const { injector, onClose, onFindOptionsChange } = await renderEnumFilter(findOptions)
58
67
  await usingAsync(injector, async () => {
59
68
  const checkboxes = document.querySelectorAll('shade-checkbox input[type="checkbox"]')
60
69
  const adminCheckbox = checkboxes[0] as HTMLInputElement
@@ -65,14 +74,16 @@ describe('EnumFilter', () => {
65
74
  applyButton?.click()
66
75
  await flushUpdates()
67
76
 
68
- expect(findOptions.getValue().filter).toEqual({ role: { $in: ['admin'] } })
77
+ expect(onFindOptionsChange).toHaveBeenCalledWith(
78
+ expect.objectContaining({ filter: { role: { $in: ['admin'] } } }),
79
+ )
69
80
  expect(onClose).toHaveBeenCalled()
70
81
  })
71
82
  })
72
83
 
73
84
  it('should apply $nin filter when exclude mode is selected', async () => {
74
85
  const findOptions = createFindOptions()
75
- const { injector, onClose } = await renderEnumFilter(findOptions)
86
+ const { injector, onClose, onFindOptionsChange } = await renderEnumFilter(findOptions)
76
87
  await usingAsync(injector, async () => {
77
88
  const excludeButton = document.querySelector(
78
89
  'shade-segmented-control button[data-value="exclude"]',
@@ -89,14 +100,16 @@ describe('EnumFilter', () => {
89
100
  applyButton?.click()
90
101
  await flushUpdates()
91
102
 
92
- expect(findOptions.getValue().filter).toEqual({ role: { $nin: ['guest'] } })
103
+ expect(onFindOptionsChange).toHaveBeenCalledWith(
104
+ expect.objectContaining({ filter: { role: { $nin: ['guest'] } } }),
105
+ )
93
106
  expect(onClose).toHaveBeenCalled()
94
107
  })
95
108
  })
96
109
 
97
110
  it('should remove filter when no values are selected', async () => {
98
111
  const findOptions = createFindOptions({ filter: { role: { $in: ['admin'] } } })
99
- const { injector, onClose } = await renderEnumFilter(findOptions)
112
+ const { injector, onClose, onFindOptionsChange } = await renderEnumFilter(findOptions)
100
113
  await usingAsync(injector, async () => {
101
114
  const checkboxes = document.querySelectorAll('shade-checkbox input[type="checkbox"]')
102
115
  const adminCheckbox = checkboxes[0] as HTMLInputElement
@@ -107,41 +120,43 @@ describe('EnumFilter', () => {
107
120
  applyButton?.click()
108
121
  await flushUpdates()
109
122
 
110
- expect(findOptions.getValue().filter?.role).toBeUndefined()
123
+ const updatedOptions = onFindOptionsChange.mock.lastCall?.[0] as FilterableFindOptions
124
+ expect(updatedOptions.filter?.role).toBeUndefined()
111
125
  expect(onClose).toHaveBeenCalled()
112
126
  })
113
127
  })
114
128
 
115
129
  it('should clear filter when Clear button is clicked', async () => {
116
130
  const findOptions = createFindOptions({ filter: { role: { $in: ['admin', 'user'] } } })
117
- const { injector, onClose } = await renderEnumFilter(findOptions)
131
+ const { injector, onClose, onFindOptionsChange } = await renderEnumFilter(findOptions)
118
132
  await usingAsync(injector, async () => {
119
133
  const clearButton = Array.from(document.querySelectorAll('button')).find((b) => b.textContent?.trim() === 'Clear')
120
134
  clearButton?.click()
121
135
  await flushUpdates()
122
136
 
123
- expect(findOptions.getValue().filter?.role).toBeUndefined()
137
+ const updatedOptions = onFindOptionsChange.mock.lastCall?.[0] as FilterableFindOptions
138
+ expect(updatedOptions.filter?.role).toBeUndefined()
124
139
  expect(onClose).toHaveBeenCalled()
125
140
  })
126
141
  })
127
142
 
128
143
  it('should preserve filters on other fields', async () => {
129
144
  const findOptions = createFindOptions({ filter: { role: { $in: ['admin'] }, name: { $regex: 'keep' } } })
130
- const { injector } = await renderEnumFilter(findOptions)
145
+ const { injector, onFindOptionsChange } = await renderEnumFilter(findOptions)
131
146
  await usingAsync(injector, async () => {
132
147
  const clearButton = Array.from(document.querySelectorAll('button')).find((b) => b.textContent?.trim() === 'Clear')
133
148
  clearButton?.click()
134
149
  await flushUpdates()
135
150
 
136
- const updatedFilter = findOptions.getValue().filter
137
- expect(updatedFilter?.role).toBeUndefined()
138
- expect(updatedFilter?.name).toEqual({ $regex: 'keep' })
151
+ const updatedOptions = onFindOptionsChange.mock.lastCall?.[0] as FilterableFindOptions
152
+ expect(updatedOptions.filter?.role).toBeUndefined()
153
+ expect(updatedOptions.filter?.name).toEqual({ $regex: 'keep' })
139
154
  })
140
155
  })
141
156
 
142
157
  it('should reset skip to 0 when applying filter', async () => {
143
158
  const findOptions = createFindOptions({ skip: 20 })
144
- const { injector } = await renderEnumFilter(findOptions)
159
+ const { injector, onFindOptionsChange } = await renderEnumFilter(findOptions)
145
160
  await usingAsync(injector, async () => {
146
161
  const checkboxes = document.querySelectorAll('shade-checkbox input[type="checkbox"]')
147
162
  const checkbox = checkboxes[0] as HTMLInputElement
@@ -152,7 +167,7 @@ describe('EnumFilter', () => {
152
167
  applyButton?.click()
153
168
  await flushUpdates()
154
169
 
155
- expect(findOptions.getValue().skip).toBe(0)
170
+ expect(onFindOptionsChange).toHaveBeenCalledWith(expect.objectContaining({ skip: 0 }))
156
171
  })
157
172
  })
158
173
 
@@ -1,5 +1,4 @@
1
1
  import { createComponent, Shade } from '@furystack/shades'
2
- import type { ObservableValue } from '@furystack/utils'
3
2
  import { SegmentedControl } from '../../button-group.js'
4
3
  import { Button } from '../../button.js'
5
4
  import { close as closeIcon, search as searchIcon } from '../../icons/icon-definitions.js'
@@ -14,10 +13,11 @@ type EnumMode = 'include' | 'exclude'
14
13
  export const EnumFilter = Shade<{
15
14
  field: string
16
15
  values: Array<{ label: string; value: string }>
17
- findOptions: ObservableValue<FilterableFindOptions>
16
+ findOptions: FilterableFindOptions
17
+ onFindOptionsChange: (options: FilterableFindOptions) => void
18
18
  onClose: () => void
19
19
  }>({
20
- shadowDomName: 'data-grid-enum-filter',
20
+ customElementName: 'data-grid-enum-filter',
21
21
  css: {
22
22
  ...filterBaseCss,
23
23
  fontFamily: cssVariableTheme.typography.fontFamily,
@@ -36,8 +36,8 @@ export const EnumFilter = Shade<{
36
36
  marginBottom: '0',
37
37
  },
38
38
  },
39
- render: ({ props, useObservable, useState }) => {
40
- const [findOptions, setFindOptions] = useObservable('findOptions', props.findOptions)
39
+ render: ({ props, useState }) => {
40
+ const { findOptions } = props
41
41
 
42
42
  const currentFilter = findOptions.filter?.[props.field] as { $in?: string[]; $nin?: string[] } | undefined
43
43
  const isExcludeMode = !!currentFilter?.$nin
@@ -54,14 +54,14 @@ export const EnumFilter = Shade<{
54
54
  const operator = mode === 'include' ? '$in' : '$nin'
55
55
  filter[props.field] = { [operator]: Array.from(selected) }
56
56
  }
57
- setFindOptions({ ...findOptions, filter, skip: 0 })
57
+ props.onFindOptionsChange({ ...findOptions, filter, skip: 0 })
58
58
  props.onClose()
59
59
  }
60
60
 
61
61
  const clearFilter = () => {
62
62
  const filter = { ...findOptions.filter }
63
63
  delete filter[props.field]
64
- setFindOptions({ ...findOptions, filter, skip: 0 })
64
+ props.onFindOptionsChange({ ...findOptions, filter, skip: 0 })
65
65
  props.onClose()
66
66
  }
67
67
 
@@ -8,7 +8,7 @@ export type FilterDropdownProps = {
8
8
  }
9
9
 
10
10
  export const FilterDropdown: (props: FilterDropdownProps, children: ChildrenList) => JSX.Element = Shade({
11
- shadowDomName: 'data-grid-filter-dropdown',
11
+ customElementName: 'data-grid-filter-dropdown',
12
12
  css: {
13
13
  fontFamily: cssVariableTheme.typography.fontFamily,
14
14
  display: 'block',