@furystack/shades-common-components 10.0.35 → 11.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 (295) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/esm/components/animations.spec.d.ts +2 -0
  3. package/esm/components/animations.spec.d.ts.map +1 -0
  4. package/esm/components/animations.spec.js +201 -0
  5. package/esm/components/animations.spec.js.map +1 -0
  6. package/esm/components/app-bar-link.js +21 -20
  7. package/esm/components/app-bar-link.js.map +1 -1
  8. package/esm/components/app-bar-link.spec.d.ts +2 -0
  9. package/esm/components/app-bar-link.spec.d.ts.map +1 -0
  10. package/esm/components/app-bar-link.spec.js +252 -0
  11. package/esm/components/app-bar-link.spec.js.map +1 -0
  12. package/esm/components/app-bar.js +21 -21
  13. package/esm/components/app-bar.js.map +1 -1
  14. package/esm/components/app-bar.spec.d.ts +2 -0
  15. package/esm/components/app-bar.spec.d.ts.map +1 -0
  16. package/esm/components/app-bar.spec.js +117 -0
  17. package/esm/components/app-bar.spec.js.map +1 -0
  18. package/esm/components/avatar.d.ts.map +1 -1
  19. package/esm/components/avatar.js +15 -19
  20. package/esm/components/avatar.js.map +1 -1
  21. package/esm/components/avatar.spec.d.ts +2 -0
  22. package/esm/components/avatar.spec.d.ts.map +1 -0
  23. package/esm/components/avatar.spec.js +114 -0
  24. package/esm/components/avatar.spec.js.map +1 -0
  25. package/esm/components/button.d.ts.map +1 -1
  26. package/esm/components/button.js +145 -156
  27. package/esm/components/button.js.map +1 -1
  28. package/esm/components/button.spec.d.ts +2 -0
  29. package/esm/components/button.spec.d.ts.map +1 -0
  30. package/esm/components/button.spec.js +155 -0
  31. package/esm/components/button.spec.js.map +1 -0
  32. package/esm/components/command-palette/command-palette-input.d.ts.map +1 -1
  33. package/esm/components/command-palette/command-palette-input.js +18 -16
  34. package/esm/components/command-palette/command-palette-input.js.map +1 -1
  35. package/esm/components/command-palette/command-palette-input.spec.d.ts +2 -0
  36. package/esm/components/command-palette/command-palette-input.spec.d.ts.map +1 -0
  37. package/esm/components/command-palette/command-palette-input.spec.js +233 -0
  38. package/esm/components/command-palette/command-palette-input.spec.js.map +1 -0
  39. package/esm/components/command-palette/command-palette-manager.spec.d.ts +2 -0
  40. package/esm/components/command-palette/command-palette-manager.spec.d.ts.map +1 -0
  41. package/esm/components/command-palette/command-palette-manager.spec.js +362 -0
  42. package/esm/components/command-palette/command-palette-manager.spec.js.map +1 -0
  43. package/esm/components/command-palette/command-palette-suggestion-list.d.ts.map +1 -1
  44. package/esm/components/command-palette/command-palette-suggestion-list.js +42 -46
  45. package/esm/components/command-palette/command-palette-suggestion-list.js.map +1 -1
  46. package/esm/components/command-palette/command-palette-suggestion-list.spec.d.ts +2 -0
  47. package/esm/components/command-palette/command-palette-suggestion-list.spec.d.ts.map +1 -0
  48. package/esm/components/command-palette/command-palette-suggestion-list.spec.js +376 -0
  49. package/esm/components/command-palette/command-palette-suggestion-list.spec.js.map +1 -0
  50. package/esm/components/command-palette/index.d.ts.map +1 -1
  51. package/esm/components/command-palette/index.js +100 -110
  52. package/esm/components/command-palette/index.js.map +1 -1
  53. package/esm/components/command-palette/index.spec.d.ts +2 -0
  54. package/esm/components/command-palette/index.spec.d.ts.map +1 -0
  55. package/esm/components/command-palette/index.spec.js +509 -0
  56. package/esm/components/command-palette/index.spec.js.map +1 -0
  57. package/esm/components/data-grid/body.js +1 -1
  58. package/esm/components/data-grid/body.js.map +1 -1
  59. package/esm/components/data-grid/body.spec.d.ts +2 -0
  60. package/esm/components/data-grid/body.spec.d.ts.map +1 -0
  61. package/esm/components/data-grid/body.spec.js +228 -0
  62. package/esm/components/data-grid/body.spec.js.map +1 -0
  63. package/esm/components/data-grid/data-grid-row.d.ts.map +1 -1
  64. package/esm/components/data-grid/data-grid-row.js +49 -73
  65. package/esm/components/data-grid/data-grid-row.js.map +1 -1
  66. package/esm/components/data-grid/data-grid-row.spec.d.ts +2 -0
  67. package/esm/components/data-grid/data-grid-row.spec.d.ts.map +1 -0
  68. package/esm/components/data-grid/data-grid-row.spec.js +296 -0
  69. package/esm/components/data-grid/data-grid-row.spec.js.map +1 -0
  70. package/esm/components/data-grid/data-grid.d.ts.map +1 -1
  71. package/esm/components/data-grid/data-grid.js +35 -28
  72. package/esm/components/data-grid/data-grid.js.map +1 -1
  73. package/esm/components/data-grid/data-grid.spec.d.ts +2 -0
  74. package/esm/components/data-grid/data-grid.spec.d.ts.map +1 -0
  75. package/esm/components/data-grid/data-grid.spec.js +544 -0
  76. package/esm/components/data-grid/data-grid.spec.js.map +1 -0
  77. package/esm/components/data-grid/footer.js +21 -15
  78. package/esm/components/data-grid/footer.js.map +1 -1
  79. package/esm/components/data-grid/footer.spec.d.ts +2 -0
  80. package/esm/components/data-grid/footer.spec.d.ts.map +1 -0
  81. package/esm/components/data-grid/footer.spec.js +264 -0
  82. package/esm/components/data-grid/footer.spec.js.map +1 -0
  83. package/esm/components/data-grid/header.d.ts.map +1 -1
  84. package/esm/components/data-grid/header.js +55 -33
  85. package/esm/components/data-grid/header.js.map +1 -1
  86. package/esm/components/data-grid/header.spec.d.ts +2 -0
  87. package/esm/components/data-grid/header.spec.d.ts.map +1 -0
  88. package/esm/components/data-grid/header.spec.js +421 -0
  89. package/esm/components/data-grid/header.spec.js.map +1 -0
  90. package/esm/components/data-grid/selection-cell.d.ts.map +1 -1
  91. package/esm/components/data-grid/selection-cell.js +13 -6
  92. package/esm/components/data-grid/selection-cell.js.map +1 -1
  93. package/esm/components/data-grid/selection-cell.spec.d.ts +2 -0
  94. package/esm/components/data-grid/selection-cell.spec.d.ts.map +1 -0
  95. package/esm/components/data-grid/selection-cell.spec.js +118 -0
  96. package/esm/components/data-grid/selection-cell.spec.js.map +1 -0
  97. package/esm/components/fab.d.ts.map +1 -1
  98. package/esm/components/fab.js +10 -1
  99. package/esm/components/fab.js.map +1 -1
  100. package/esm/components/fab.spec.d.ts +2 -0
  101. package/esm/components/fab.spec.d.ts.map +1 -0
  102. package/esm/components/fab.spec.js +95 -0
  103. package/esm/components/fab.spec.js.map +1 -0
  104. package/esm/components/form.spec.d.ts +2 -0
  105. package/esm/components/form.spec.d.ts.map +1 -0
  106. package/esm/components/form.spec.js +314 -0
  107. package/esm/components/form.spec.js.map +1 -0
  108. package/esm/components/grid.d.ts.map +1 -1
  109. package/esm/components/grid.js +40 -37
  110. package/esm/components/grid.js.map +1 -1
  111. package/esm/components/grid.spec.d.ts +2 -0
  112. package/esm/components/grid.spec.d.ts.map +1 -0
  113. package/esm/components/grid.spec.js +316 -0
  114. package/esm/components/grid.spec.js.map +1 -0
  115. package/esm/components/inputs/autocomplete.spec.d.ts +2 -0
  116. package/esm/components/inputs/autocomplete.spec.d.ts.map +1 -0
  117. package/esm/components/inputs/autocomplete.spec.js +194 -0
  118. package/esm/components/inputs/autocomplete.spec.js.map +1 -0
  119. package/esm/components/inputs/input.d.ts.map +1 -1
  120. package/esm/components/inputs/input.js +141 -109
  121. package/esm/components/inputs/input.js.map +1 -1
  122. package/esm/components/inputs/input.spec.d.ts +2 -0
  123. package/esm/components/inputs/input.spec.d.ts.map +1 -0
  124. package/esm/components/inputs/input.spec.js +577 -0
  125. package/esm/components/inputs/input.spec.js.map +1 -0
  126. package/esm/components/inputs/text-area.d.ts.map +1 -1
  127. package/esm/components/inputs/text-area.js +54 -58
  128. package/esm/components/inputs/text-area.js.map +1 -1
  129. package/esm/components/inputs/text-area.spec.d.ts +2 -0
  130. package/esm/components/inputs/text-area.spec.d.ts.map +1 -0
  131. package/esm/components/inputs/text-area.spec.js +214 -0
  132. package/esm/components/inputs/text-area.spec.js.map +1 -0
  133. package/esm/components/loader.js +1 -1
  134. package/esm/components/loader.js.map +1 -1
  135. package/esm/components/loader.spec.d.ts +2 -0
  136. package/esm/components/loader.spec.d.ts.map +1 -0
  137. package/esm/components/loader.spec.js +251 -0
  138. package/esm/components/loader.spec.js.map +1 -0
  139. package/esm/components/modal.d.ts.map +1 -1
  140. package/esm/components/modal.js +11 -9
  141. package/esm/components/modal.js.map +1 -1
  142. package/esm/components/modal.spec.d.ts +2 -0
  143. package/esm/components/modal.spec.d.ts.map +1 -0
  144. package/esm/components/modal.spec.js +227 -0
  145. package/esm/components/modal.spec.js.map +1 -0
  146. package/esm/components/noty-list.d.ts.map +1 -1
  147. package/esm/components/noty-list.js +39 -40
  148. package/esm/components/noty-list.js.map +1 -1
  149. package/esm/components/noty-list.spec.d.ts +2 -0
  150. package/esm/components/noty-list.spec.d.ts.map +1 -0
  151. package/esm/components/noty-list.spec.js +486 -0
  152. package/esm/components/noty-list.spec.js.map +1 -0
  153. package/esm/components/paper.d.ts.map +1 -1
  154. package/esm/components/paper.js +15 -12
  155. package/esm/components/paper.js.map +1 -1
  156. package/esm/components/paper.spec.d.ts +2 -0
  157. package/esm/components/paper.spec.d.ts.map +1 -0
  158. package/esm/components/paper.spec.js +63 -0
  159. package/esm/components/paper.spec.js.map +1 -0
  160. package/esm/components/skeleton.js +1 -1
  161. package/esm/components/skeleton.js.map +1 -1
  162. package/esm/components/skeleton.spec.d.ts +2 -0
  163. package/esm/components/skeleton.spec.d.ts.map +1 -0
  164. package/esm/components/skeleton.spec.js +159 -0
  165. package/esm/components/skeleton.spec.js.map +1 -0
  166. package/esm/components/styles.spec.d.ts +2 -0
  167. package/esm/components/styles.spec.d.ts.map +1 -0
  168. package/esm/components/styles.spec.js +56 -0
  169. package/esm/components/styles.spec.js.map +1 -0
  170. package/esm/components/suggest/index.d.ts.map +1 -1
  171. package/esm/components/suggest/index.js +74 -83
  172. package/esm/components/suggest/index.js.map +1 -1
  173. package/esm/components/suggest/index.spec.d.ts +2 -0
  174. package/esm/components/suggest/index.spec.d.ts.map +1 -0
  175. package/esm/components/suggest/index.spec.js +515 -0
  176. package/esm/components/suggest/index.spec.js.map +1 -0
  177. package/esm/components/suggest/suggest-input.d.ts.map +1 -1
  178. package/esm/components/suggest/suggest-input.js +16 -17
  179. package/esm/components/suggest/suggest-input.js.map +1 -1
  180. package/esm/components/suggest/suggest-input.spec.d.ts +2 -0
  181. package/esm/components/suggest/suggest-input.spec.d.ts.map +1 -0
  182. package/esm/components/suggest/suggest-input.spec.js +138 -0
  183. package/esm/components/suggest/suggest-input.spec.js.map +1 -0
  184. package/esm/components/suggest/suggest-manager.spec.d.ts +2 -0
  185. package/esm/components/suggest/suggest-manager.spec.d.ts.map +1 -0
  186. package/esm/components/suggest/suggest-manager.spec.js +308 -0
  187. package/esm/components/suggest/suggest-manager.spec.js.map +1 -0
  188. package/esm/components/suggest/suggestion-list.d.ts.map +1 -1
  189. package/esm/components/suggest/suggestion-list.js +43 -48
  190. package/esm/components/suggest/suggestion-list.js.map +1 -1
  191. package/esm/components/suggest/suggestion-list.spec.d.ts +2 -0
  192. package/esm/components/suggest/suggestion-list.spec.d.ts.map +1 -0
  193. package/esm/components/suggest/suggestion-list.spec.js +252 -0
  194. package/esm/components/suggest/suggestion-list.spec.js.map +1 -0
  195. package/esm/components/tabs.d.ts.map +1 -1
  196. package/esm/components/tabs.js +32 -18
  197. package/esm/components/tabs.js.map +1 -1
  198. package/esm/components/tabs.spec.d.ts +2 -0
  199. package/esm/components/tabs.spec.d.ts.map +1 -0
  200. package/esm/components/tabs.spec.js +187 -0
  201. package/esm/components/tabs.spec.js.map +1 -0
  202. package/esm/components/wizard/index.d.ts.map +1 -1
  203. package/esm/components/wizard/index.js +10 -7
  204. package/esm/components/wizard/index.js.map +1 -1
  205. package/esm/components/wizard/index.spec.d.ts +2 -0
  206. package/esm/components/wizard/index.spec.d.ts.map +1 -0
  207. package/esm/components/wizard/index.spec.js +171 -0
  208. package/esm/components/wizard/index.spec.js.map +1 -0
  209. package/esm/services/collection-service.spec.js +391 -2
  210. package/esm/services/collection-service.spec.js.map +1 -1
  211. package/esm/services/css-variable-theme.d.ts.map +1 -1
  212. package/esm/services/css-variable-theme.js +21 -1
  213. package/esm/services/css-variable-theme.js.map +1 -1
  214. package/esm/services/css-variable-theme.spec.d.ts +2 -0
  215. package/esm/services/css-variable-theme.spec.d.ts.map +1 -0
  216. package/esm/services/css-variable-theme.spec.js +169 -0
  217. package/esm/services/css-variable-theme.spec.js.map +1 -0
  218. package/esm/services/default-palette.d.ts +4 -0
  219. package/esm/services/default-palette.d.ts.map +1 -1
  220. package/esm/services/default-palette.js +22 -0
  221. package/esm/services/default-palette.js.map +1 -1
  222. package/esm/services/theme-provider-service.d.ts +59 -1
  223. package/esm/services/theme-provider-service.d.ts.map +1 -1
  224. package/esm/services/theme-provider-service.js.map +1 -1
  225. package/esm/services/theme-provider-service.spec.d.ts +2 -0
  226. package/esm/services/theme-provider-service.spec.d.ts.map +1 -0
  227. package/esm/services/theme-provider-service.spec.js +166 -0
  228. package/esm/services/theme-provider-service.spec.js.map +1 -0
  229. package/package.json +2 -2
  230. package/src/components/animations.spec.ts +299 -0
  231. package/src/components/app-bar-link.spec.tsx +341 -0
  232. package/src/components/app-bar-link.tsx +21 -21
  233. package/src/components/app-bar.spec.tsx +142 -0
  234. package/src/components/app-bar.tsx +22 -22
  235. package/src/components/avatar.spec.tsx +146 -0
  236. package/src/components/avatar.tsx +17 -20
  237. package/src/components/button.spec.tsx +193 -0
  238. package/src/components/button.tsx +162 -197
  239. package/src/components/command-palette/command-palette-input.spec.tsx +320 -0
  240. package/src/components/command-palette/command-palette-input.tsx +19 -22
  241. package/src/components/command-palette/command-palette-manager.spec.ts +470 -0
  242. package/src/components/command-palette/command-palette-suggestion-list.spec.tsx +499 -0
  243. package/src/components/command-palette/command-palette-suggestion-list.tsx +42 -46
  244. package/src/components/command-palette/index.spec.tsx +684 -0
  245. package/src/components/command-palette/index.tsx +107 -136
  246. package/src/components/data-grid/body.spec.tsx +340 -0
  247. package/src/components/data-grid/body.tsx +1 -1
  248. package/src/components/data-grid/data-grid-row.spec.tsx +382 -0
  249. package/src/components/data-grid/data-grid-row.tsx +50 -82
  250. package/src/components/data-grid/data-grid.spec.tsx +939 -0
  251. package/src/components/data-grid/data-grid.tsx +38 -35
  252. package/src/components/data-grid/footer.spec.tsx +344 -0
  253. package/src/components/data-grid/footer.tsx +19 -19
  254. package/src/components/data-grid/header.spec.tsx +563 -0
  255. package/src/components/data-grid/header.tsx +53 -44
  256. package/src/components/data-grid/selection-cell.spec.tsx +150 -0
  257. package/src/components/data-grid/selection-cell.tsx +12 -6
  258. package/src/components/fab.spec.tsx +108 -0
  259. package/src/components/fab.tsx +10 -1
  260. package/src/components/form.spec.tsx +481 -0
  261. package/src/components/grid.spec.tsx +334 -0
  262. package/src/components/grid.tsx +57 -63
  263. package/src/components/inputs/autocomplete.spec.tsx +258 -0
  264. package/src/components/inputs/input.spec.tsx +808 -0
  265. package/src/components/inputs/input.tsx +153 -139
  266. package/src/components/inputs/text-area.spec.tsx +285 -0
  267. package/src/components/inputs/text-area.tsx +53 -79
  268. package/src/components/loader.spec.tsx +346 -0
  269. package/src/components/loader.tsx +1 -1
  270. package/src/components/modal.spec.tsx +304 -0
  271. package/src/components/modal.tsx +11 -9
  272. package/src/components/noty-list.spec.tsx +631 -0
  273. package/src/components/noty-list.tsx +39 -50
  274. package/src/components/paper.spec.tsx +72 -0
  275. package/src/components/paper.tsx +15 -13
  276. package/src/components/skeleton.spec.tsx +219 -0
  277. package/src/components/skeleton.tsx +1 -1
  278. package/src/components/styles.spec.ts +70 -0
  279. package/src/components/suggest/index.spec.tsx +861 -0
  280. package/src/components/suggest/index.tsx +74 -101
  281. package/src/components/suggest/suggest-input.spec.tsx +181 -0
  282. package/src/components/suggest/suggest-input.tsx +16 -24
  283. package/src/components/suggest/suggest-manager.spec.ts +409 -0
  284. package/src/components/suggest/suggestion-list.spec.tsx +334 -0
  285. package/src/components/suggest/suggestion-list.tsx +43 -48
  286. package/src/components/tabs.spec.tsx +236 -0
  287. package/src/components/tabs.tsx +33 -21
  288. package/src/components/wizard/index.spec.tsx +224 -0
  289. package/src/components/wizard/index.tsx +10 -9
  290. package/src/services/collection-service.spec.ts +492 -3
  291. package/src/services/css-variable-theme.spec.ts +204 -0
  292. package/src/services/css-variable-theme.ts +21 -1
  293. package/src/services/default-palette.ts +22 -0
  294. package/src/services/theme-provider-service.spec.ts +195 -0
  295. package/src/services/theme-provider-service.ts +60 -2
@@ -0,0 +1,563 @@
1
+ import type { FindOptions } from '@furystack/core'
2
+ import { Injector } from '@furystack/inject'
3
+ import { createComponent, initializeShadeRoot } from '@furystack/shades'
4
+ import { ObservableValue, sleepAsync } from '@furystack/utils'
5
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
6
+ import { DataGridHeader, OrderButton } from './header.js'
7
+
8
+ type TestItem = { id: number; name: string; email: string }
9
+
10
+ describe('DataGridHeader', () => {
11
+ let originalAnimate: typeof Element.prototype.animate
12
+
13
+ beforeEach(() => {
14
+ document.body.innerHTML = '<div id="root"></div>'
15
+
16
+ originalAnimate = Element.prototype.animate
17
+
18
+ Element.prototype.animate = vi.fn(() => {
19
+ const mockAnimation = {
20
+ onfinish: null as ((event: AnimationPlaybackEvent) => void) | null,
21
+ oncancel: null as ((event: AnimationPlaybackEvent) => void) | null,
22
+ cancel: vi.fn(),
23
+ play: vi.fn(),
24
+ pause: vi.fn(),
25
+ finish: vi.fn(),
26
+ addEventListener: vi.fn(),
27
+ removeEventListener: vi.fn(),
28
+ }
29
+
30
+ setTimeout(() => {
31
+ mockAnimation.onfinish?.({} as AnimationPlaybackEvent)
32
+ }, 0)
33
+
34
+ return mockAnimation as unknown as Animation
35
+ }) as typeof Element.prototype.animate
36
+ })
37
+
38
+ afterEach(() => {
39
+ document.body.innerHTML = ''
40
+ Element.prototype.animate = originalAnimate
41
+ })
42
+
43
+ const createFindOptions = (
44
+ options: Partial<FindOptions<TestItem, Array<keyof TestItem>>> = {},
45
+ ): ObservableValue<FindOptions<TestItem, Array<keyof TestItem>>> => {
46
+ return new ObservableValue<FindOptions<TestItem, Array<keyof TestItem>>>(options)
47
+ }
48
+
49
+ describe('rendering', () => {
50
+ it('should render with custom element', async () => {
51
+ const injector = new Injector()
52
+ const rootElement = document.getElementById('root') as HTMLDivElement
53
+ const findOptions = createFindOptions()
54
+
55
+ initializeShadeRoot({
56
+ injector,
57
+ rootElement,
58
+ jsxElement: <DataGridHeader field="name" findOptions={findOptions} />,
59
+ })
60
+
61
+ await sleepAsync(50)
62
+
63
+ const header = document.querySelector('data-grid-header')
64
+ expect(header).not.toBeNull()
65
+ })
66
+
67
+ it('should render field name', async () => {
68
+ const injector = new Injector()
69
+ const rootElement = document.getElementById('root') as HTMLDivElement
70
+ const findOptions = createFindOptions()
71
+
72
+ initializeShadeRoot({
73
+ injector,
74
+ rootElement,
75
+ jsxElement: <DataGridHeader field="name" findOptions={findOptions} />,
76
+ })
77
+
78
+ await sleepAsync(50)
79
+
80
+ const header = document.querySelector('data-grid-header')
81
+ const fieldName = header?.querySelector('.header-field-name')
82
+ expect(fieldName?.textContent).toBe('name')
83
+ })
84
+
85
+ it('should render order button', async () => {
86
+ const injector = new Injector()
87
+ const rootElement = document.getElementById('root') as HTMLDivElement
88
+ const findOptions = createFindOptions()
89
+
90
+ initializeShadeRoot({
91
+ injector,
92
+ rootElement,
93
+ jsxElement: <DataGridHeader field="name" findOptions={findOptions} />,
94
+ })
95
+
96
+ await sleepAsync(50)
97
+
98
+ const orderButton = document.querySelector('data-grid-order-button')
99
+ expect(orderButton).not.toBeNull()
100
+ })
101
+
102
+ it('should render search button', async () => {
103
+ const injector = new Injector()
104
+ const rootElement = document.getElementById('root') as HTMLDivElement
105
+ const findOptions = createFindOptions()
106
+
107
+ initializeShadeRoot({
108
+ injector,
109
+ rootElement,
110
+ jsxElement: <DataGridHeader field="name" findOptions={findOptions} />,
111
+ })
112
+
113
+ await sleepAsync(50)
114
+
115
+ const searchButton = document.querySelector('data-grid-search-button')
116
+ expect(searchButton).not.toBeNull()
117
+ })
118
+
119
+ it('should render search form', async () => {
120
+ const injector = new Injector()
121
+ const rootElement = document.getElementById('root') as HTMLDivElement
122
+ const findOptions = createFindOptions()
123
+
124
+ initializeShadeRoot({
125
+ injector,
126
+ rootElement,
127
+ jsxElement: <DataGridHeader field="name" findOptions={findOptions} />,
128
+ })
129
+
130
+ await sleepAsync(50)
131
+
132
+ const searchForm = document.querySelector('data-grid-search-form')
133
+ expect(searchForm).not.toBeNull()
134
+ })
135
+ })
136
+
137
+ describe('OrderButton', () => {
138
+ it('should show neutral icon when no order is set', async () => {
139
+ const injector = new Injector()
140
+ const rootElement = document.getElementById('root') as HTMLDivElement
141
+ const findOptions = createFindOptions()
142
+
143
+ initializeShadeRoot({
144
+ injector,
145
+ rootElement,
146
+ jsxElement: <OrderButton field="name" findOptions={findOptions} />,
147
+ })
148
+
149
+ await sleepAsync(50)
150
+
151
+ const button = document.querySelector('data-grid-order-button')
152
+ expect(button?.textContent).toContain('↕')
153
+ })
154
+
155
+ it('should show descending icon when ASC order is set for field', async () => {
156
+ const injector = new Injector()
157
+ const rootElement = document.getElementById('root') as HTMLDivElement
158
+ const findOptions = createFindOptions({ order: { name: 'ASC' } })
159
+
160
+ initializeShadeRoot({
161
+ injector,
162
+ rootElement,
163
+ jsxElement: <OrderButton field="name" findOptions={findOptions} />,
164
+ })
165
+
166
+ await sleepAsync(50)
167
+
168
+ const button = document.querySelector('data-grid-order-button')
169
+ expect(button?.textContent).toContain('⬇')
170
+ })
171
+
172
+ it('should show ascending icon when DESC order is set for field', async () => {
173
+ const injector = new Injector()
174
+ const rootElement = document.getElementById('root') as HTMLDivElement
175
+ const findOptions = createFindOptions({ order: { name: 'DESC' } })
176
+
177
+ initializeShadeRoot({
178
+ injector,
179
+ rootElement,
180
+ jsxElement: <OrderButton field="name" findOptions={findOptions} />,
181
+ })
182
+
183
+ await sleepAsync(50)
184
+
185
+ const button = document.querySelector('data-grid-order-button')
186
+ expect(button?.textContent).toContain('⬆')
187
+ })
188
+
189
+ it('should show neutral icon when order is set for different field', async () => {
190
+ const injector = new Injector()
191
+ const rootElement = document.getElementById('root') as HTMLDivElement
192
+ const findOptions = createFindOptions({ order: { id: 'ASC' } })
193
+
194
+ initializeShadeRoot({
195
+ injector,
196
+ rootElement,
197
+ jsxElement: <OrderButton field="name" findOptions={findOptions} />,
198
+ })
199
+
200
+ await sleepAsync(50)
201
+
202
+ const button = document.querySelector('data-grid-order-button')
203
+ expect(button?.textContent).toContain('↕')
204
+ })
205
+
206
+ it('should toggle order to ASC when clicking on unsorted field', async () => {
207
+ const injector = new Injector()
208
+ const rootElement = document.getElementById('root') as HTMLDivElement
209
+ const findOptions = createFindOptions()
210
+
211
+ initializeShadeRoot({
212
+ injector,
213
+ rootElement,
214
+ jsxElement: <OrderButton field="name" findOptions={findOptions} />,
215
+ })
216
+
217
+ await sleepAsync(50)
218
+
219
+ const button = document.querySelector('data-grid-order-button')?.querySelector('button')
220
+ button?.click()
221
+
222
+ await sleepAsync(50)
223
+
224
+ const updatedOptions = findOptions.getValue()
225
+ expect(updatedOptions.order).toEqual({ name: 'ASC' })
226
+ })
227
+
228
+ it('should toggle order from ASC to DESC when clicking', async () => {
229
+ const injector = new Injector()
230
+ const rootElement = document.getElementById('root') as HTMLDivElement
231
+ const findOptions = createFindOptions({ order: { name: 'ASC' } })
232
+
233
+ initializeShadeRoot({
234
+ injector,
235
+ rootElement,
236
+ jsxElement: <OrderButton field="name" findOptions={findOptions} />,
237
+ })
238
+
239
+ await sleepAsync(50)
240
+
241
+ const button = document.querySelector('data-grid-order-button')?.querySelector('button')
242
+ button?.click()
243
+
244
+ await sleepAsync(50)
245
+
246
+ const updatedOptions = findOptions.getValue()
247
+ expect(updatedOptions.order).toEqual({ name: 'DESC' })
248
+ })
249
+
250
+ it('should toggle order from DESC to ASC when clicking', async () => {
251
+ const injector = new Injector()
252
+ const rootElement = document.getElementById('root') as HTMLDivElement
253
+ const findOptions = createFindOptions({ order: { name: 'DESC' } })
254
+
255
+ initializeShadeRoot({
256
+ injector,
257
+ rootElement,
258
+ jsxElement: <OrderButton field="name" findOptions={findOptions} />,
259
+ })
260
+
261
+ await sleepAsync(50)
262
+
263
+ const button = document.querySelector('data-grid-order-button')?.querySelector('button')
264
+ button?.click()
265
+
266
+ await sleepAsync(50)
267
+
268
+ const updatedOptions = findOptions.getValue()
269
+ expect(updatedOptions.order).toEqual({ name: 'ASC' })
270
+ })
271
+
272
+ it('should react to external findOptions changes', async () => {
273
+ const injector = new Injector()
274
+ const rootElement = document.getElementById('root') as HTMLDivElement
275
+ const findOptions = createFindOptions()
276
+
277
+ initializeShadeRoot({
278
+ injector,
279
+ rootElement,
280
+ jsxElement: <OrderButton field="name" findOptions={findOptions} />,
281
+ })
282
+
283
+ await sleepAsync(50)
284
+
285
+ let button = document.querySelector('data-grid-order-button')
286
+ expect(button?.textContent).toContain('↕')
287
+
288
+ findOptions.setValue({ order: { name: 'ASC' } })
289
+ await sleepAsync(50)
290
+
291
+ button = document.querySelector('data-grid-order-button')
292
+ expect(button?.textContent).toContain('⬇')
293
+ })
294
+ })
295
+
296
+ describe('SearchButton', () => {
297
+ it('should show inactive icon when no filter is set', async () => {
298
+ const injector = new Injector()
299
+ const rootElement = document.getElementById('root') as HTMLDivElement
300
+ const findOptions = createFindOptions()
301
+
302
+ initializeShadeRoot({
303
+ injector,
304
+ rootElement,
305
+ jsxElement: <DataGridHeader field="name" findOptions={findOptions} />,
306
+ })
307
+
308
+ await sleepAsync(50)
309
+
310
+ const searchButton = document.querySelector('data-grid-search-button')
311
+ expect(searchButton?.textContent).toContain('🔎')
312
+ })
313
+
314
+ it('should show active icon when filter is set for field', async () => {
315
+ const injector = new Injector()
316
+ const rootElement = document.getElementById('root') as HTMLDivElement
317
+ const findOptions = createFindOptions({ filter: { name: { $regex: 'test' } } })
318
+
319
+ initializeShadeRoot({
320
+ injector,
321
+ rootElement,
322
+ jsxElement: <DataGridHeader field="name" findOptions={findOptions} />,
323
+ })
324
+
325
+ await sleepAsync(50)
326
+
327
+ const searchButton = document.querySelector('data-grid-search-button')
328
+ expect(searchButton?.textContent).toContain('🔍')
329
+ })
330
+
331
+ it('should show inactive icon when filter is set for different field', async () => {
332
+ const injector = new Injector()
333
+ const rootElement = document.getElementById('root') as HTMLDivElement
334
+ const findOptions = createFindOptions({ filter: { email: { $regex: 'test' } } })
335
+
336
+ initializeShadeRoot({
337
+ injector,
338
+ rootElement,
339
+ jsxElement: <DataGridHeader field="name" findOptions={findOptions} />,
340
+ })
341
+
342
+ await sleepAsync(50)
343
+
344
+ const searchButton = document.querySelector('data-grid-search-button')
345
+ expect(searchButton?.textContent).toContain('🔎')
346
+ })
347
+ })
348
+
349
+ describe('SearchForm', () => {
350
+ it('should expand search form when search button is clicked', async () => {
351
+ const injector = new Injector()
352
+ const rootElement = document.getElementById('root') as HTMLDivElement
353
+ const findOptions = createFindOptions()
354
+
355
+ initializeShadeRoot({
356
+ injector,
357
+ rootElement,
358
+ jsxElement: <DataGridHeader field="name" findOptions={findOptions} />,
359
+ })
360
+
361
+ await sleepAsync(50)
362
+
363
+ const searchButton = document.querySelector('data-grid-search-button')?.querySelector('button')
364
+ searchButton?.click()
365
+
366
+ await sleepAsync(150)
367
+
368
+ const searchForm = document.querySelector('.search-form') as HTMLElement
369
+ expect(searchForm.style.display).toBe('flex')
370
+ })
371
+
372
+ it('should update findOptions when search is submitted', async () => {
373
+ const injector = new Injector()
374
+ const rootElement = document.getElementById('root') as HTMLDivElement
375
+ const findOptions = createFindOptions()
376
+
377
+ initializeShadeRoot({
378
+ injector,
379
+ rootElement,
380
+ jsxElement: <DataGridHeader field="name" findOptions={findOptions} />,
381
+ })
382
+
383
+ await sleepAsync(50)
384
+
385
+ const searchButton = document.querySelector('data-grid-search-button')?.querySelector('button')
386
+ searchButton?.click()
387
+
388
+ await sleepAsync(150)
389
+
390
+ const input = document.querySelector('.search-form input') as HTMLInputElement
391
+ input.value = 'test-search'
392
+ input.dispatchEvent(new Event('input', { bubbles: true }))
393
+
394
+ const form = document.querySelector('.search-form') as HTMLFormElement
395
+ form.dispatchEvent(new Event('submit', { bubbles: true }))
396
+
397
+ await sleepAsync(50)
398
+
399
+ const updatedOptions = findOptions.getValue()
400
+ expect(updatedOptions.filter).toEqual({ name: { $regex: 'test-search' } })
401
+ })
402
+
403
+ it('should clear filter when clear button is clicked', async () => {
404
+ const injector = new Injector()
405
+ const rootElement = document.getElementById('root') as HTMLDivElement
406
+ const findOptions = createFindOptions({ filter: { name: { $regex: 'existing' } } })
407
+
408
+ initializeShadeRoot({
409
+ injector,
410
+ rootElement,
411
+ jsxElement: <DataGridHeader field="name" findOptions={findOptions} />,
412
+ })
413
+
414
+ await sleepAsync(50)
415
+
416
+ const searchButton = document.querySelector('data-grid-search-button')?.querySelector('button')
417
+ searchButton?.click()
418
+
419
+ await sleepAsync(150)
420
+
421
+ const clearButton = document.querySelector('.search-form button[type="reset"]') as HTMLButtonElement
422
+ clearButton?.click()
423
+
424
+ await sleepAsync(50)
425
+
426
+ const updatedOptions = findOptions.getValue()
427
+ expect(updatedOptions.filter?.name).toBeUndefined()
428
+ })
429
+
430
+ it('should preserve filters for other fields when submitting search', async () => {
431
+ const injector = new Injector()
432
+ const rootElement = document.getElementById('root') as HTMLDivElement
433
+ const findOptions = createFindOptions({ filter: { email: { $regex: 'existing' } } })
434
+
435
+ initializeShadeRoot({
436
+ injector,
437
+ rootElement,
438
+ jsxElement: <DataGridHeader field="name" findOptions={findOptions} />,
439
+ })
440
+
441
+ await sleepAsync(50)
442
+
443
+ const searchButton = document.querySelector('data-grid-search-button')?.querySelector('button')
444
+ searchButton?.click()
445
+
446
+ await sleepAsync(150)
447
+
448
+ const input = document.querySelector('.search-form input') as HTMLInputElement
449
+ input.value = 'new-search'
450
+ input.dispatchEvent(new Event('input', { bubbles: true }))
451
+
452
+ const form = document.querySelector('.search-form') as HTMLFormElement
453
+ form.dispatchEvent(new Event('submit', { bubbles: true }))
454
+
455
+ await sleepAsync(50)
456
+
457
+ const updatedOptions = findOptions.getValue()
458
+ expect(updatedOptions.filter).toEqual({
459
+ email: { $regex: 'existing' },
460
+ name: { $regex: 'new-search' },
461
+ })
462
+ })
463
+
464
+ it('should preserve other findOptions properties when updating filter', async () => {
465
+ const injector = new Injector()
466
+ const rootElement = document.getElementById('root') as HTMLDivElement
467
+ const findOptions = createFindOptions({
468
+ order: { id: 'ASC' },
469
+ top: 10,
470
+ skip: 20,
471
+ })
472
+
473
+ initializeShadeRoot({
474
+ injector,
475
+ rootElement,
476
+ jsxElement: <DataGridHeader field="name" findOptions={findOptions} />,
477
+ })
478
+
479
+ await sleepAsync(50)
480
+
481
+ const searchButton = document.querySelector('data-grid-search-button')?.querySelector('button')
482
+ searchButton?.click()
483
+
484
+ await sleepAsync(150)
485
+
486
+ const input = document.querySelector('.search-form input') as HTMLInputElement
487
+ input.value = 'search-value'
488
+ input.dispatchEvent(new Event('input', { bubbles: true }))
489
+
490
+ const form = document.querySelector('.search-form') as HTMLFormElement
491
+ form.dispatchEvent(new Event('submit', { bubbles: true }))
492
+
493
+ await sleepAsync(50)
494
+
495
+ const updatedOptions = findOptions.getValue()
496
+ expect(updatedOptions.order).toEqual({ id: 'ASC' })
497
+ expect(updatedOptions.top).toBe(10)
498
+ expect(updatedOptions.skip).toBe(20)
499
+ expect(updatedOptions.filter).toEqual({ name: { $regex: 'search-value' } })
500
+ })
501
+
502
+ it('should show current filter value in search input', async () => {
503
+ const injector = new Injector()
504
+ const rootElement = document.getElementById('root') as HTMLDivElement
505
+ const findOptions = createFindOptions({ filter: { name: { $regex: 'current-filter' } } })
506
+
507
+ initializeShadeRoot({
508
+ injector,
509
+ rootElement,
510
+ jsxElement: <DataGridHeader field="name" findOptions={findOptions} />,
511
+ })
512
+
513
+ await sleepAsync(50)
514
+
515
+ const searchButton = document.querySelector('data-grid-search-button')?.querySelector('button')
516
+ searchButton?.click()
517
+
518
+ await sleepAsync(150)
519
+
520
+ const input = document.querySelector('.search-form input') as HTMLInputElement
521
+ expect(input.value).toBe('current-filter')
522
+ })
523
+ })
524
+
525
+ describe('integration', () => {
526
+ it('should support both sorting and filtering simultaneously', async () => {
527
+ const injector = new Injector()
528
+ const rootElement = document.getElementById('root') as HTMLDivElement
529
+ const findOptions = createFindOptions()
530
+
531
+ initializeShadeRoot({
532
+ injector,
533
+ rootElement,
534
+ jsxElement: <DataGridHeader field="name" findOptions={findOptions} />,
535
+ })
536
+
537
+ await sleepAsync(50)
538
+
539
+ const orderButton = document.querySelector('data-grid-order-button')?.querySelector('button')
540
+ orderButton?.click()
541
+
542
+ await sleepAsync(50)
543
+
544
+ const searchButton = document.querySelector('data-grid-search-button')?.querySelector('button')
545
+ searchButton?.click()
546
+
547
+ await sleepAsync(150)
548
+
549
+ const input = document.querySelector('.search-form input') as HTMLInputElement
550
+ input.value = 'filter-value'
551
+ input.dispatchEvent(new Event('input', { bubbles: true }))
552
+
553
+ const form = document.querySelector('.search-form') as HTMLFormElement
554
+ form.dispatchEvent(new Event('submit', { bubbles: true }))
555
+
556
+ await sleepAsync(50)
557
+
558
+ const updatedOptions = findOptions.getValue()
559
+ expect(updatedOptions.order).toEqual({ name: 'ASC' })
560
+ expect(updatedOptions.filter).toEqual({ name: { $regex: 'filter-value' } })
561
+ })
562
+ })
563
+ })