@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,334 @@
1
+ import { Injector } from '@furystack/inject'
2
+ import { createComponent, initializeShadeRoot } from '@furystack/shades'
3
+ import { sleepAsync } from '@furystack/utils'
4
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest'
5
+ import { Grid, type GridProps, type HeaderCells, type RowCells } from './grid.js'
6
+
7
+ type TestEntry = { id: number; name: string; value: string }
8
+ type TestColumns = 'id' | 'name' | 'value'
9
+
10
+ describe('Grid', () => {
11
+ beforeEach(() => {
12
+ document.body.innerHTML = '<div id="root"></div>'
13
+ })
14
+
15
+ afterEach(() => {
16
+ document.body.innerHTML = ''
17
+ })
18
+
19
+ const renderGrid = async (props: GridProps<TestEntry, TestColumns>) => {
20
+ const injector = new Injector()
21
+ const root = document.getElementById('root')!
22
+ initializeShadeRoot({
23
+ injector,
24
+ rootElement: root,
25
+ jsxElement: <Grid {...props} />,
26
+ })
27
+ await sleepAsync(50)
28
+ const grid = document.querySelector('shade-grid') as HTMLElement
29
+ return {
30
+ injector,
31
+ grid,
32
+ table: grid?.querySelector('table') as HTMLTableElement,
33
+ }
34
+ }
35
+
36
+ describe('rendering', () => {
37
+ it('should render a grid element as custom element', async () => {
38
+ const { grid } = await renderGrid({
39
+ columns: ['id', 'name'],
40
+ entries: [],
41
+ })
42
+ expect(grid).toBeTruthy()
43
+ expect(grid.tagName.toLowerCase()).toBe('shade-grid')
44
+ })
45
+
46
+ it('should render a table inside the grid element', async () => {
47
+ const { table } = await renderGrid({
48
+ columns: ['id', 'name'],
49
+ entries: [],
50
+ })
51
+ expect(table).toBeTruthy()
52
+ expect(table.tagName.toLowerCase()).toBe('table')
53
+ })
54
+
55
+ it('should render thead and tbody', async () => {
56
+ const { table } = await renderGrid({
57
+ columns: ['id', 'name'],
58
+ entries: [],
59
+ })
60
+ expect(table.querySelector('thead')).toBeTruthy()
61
+ expect(table.querySelector('tbody')).toBeTruthy()
62
+ })
63
+ })
64
+
65
+ describe('columns', () => {
66
+ it('should render column headers', async () => {
67
+ const { table } = await renderGrid({
68
+ columns: ['id', 'name', 'value'],
69
+ entries: [],
70
+ })
71
+ const headers = table.querySelectorAll('th')
72
+ expect(headers.length).toBe(3)
73
+ })
74
+
75
+ it('should render column names as header text by default', async () => {
76
+ const { table } = await renderGrid({
77
+ columns: ['id', 'name', 'value'],
78
+ entries: [],
79
+ })
80
+ const headers = table.querySelectorAll('th')
81
+ expect(headers[0].textContent).toBe('id')
82
+ expect(headers[1].textContent).toBe('name')
83
+ expect(headers[2].textContent).toBe('value')
84
+ })
85
+
86
+ it('should use custom header components when provided', async () => {
87
+ const headerComponents: HeaderCells<TestColumns> = {
88
+ id: (name) => <span>ID Header: {name}</span>,
89
+ name: (name) => <span>Name Header: {name}</span>,
90
+ }
91
+ const { table } = await renderGrid({
92
+ columns: ['id', 'name', 'value'],
93
+ entries: [],
94
+ headerComponents,
95
+ })
96
+ const headers = table.querySelectorAll('th')
97
+ expect(headers[0].textContent).toContain('ID Header: id')
98
+ expect(headers[1].textContent).toContain('Name Header: name')
99
+ expect(headers[2].textContent).toBe('value')
100
+ })
101
+
102
+ it('should use default header component for unspecified columns', async () => {
103
+ const headerComponents: HeaderCells<TestColumns> = {
104
+ default: (name) => <strong>{name.toUpperCase()}</strong>,
105
+ }
106
+ const { table } = await renderGrid({
107
+ columns: ['id', 'name'],
108
+ entries: [],
109
+ headerComponents,
110
+ })
111
+ const headers = table.querySelectorAll('th')
112
+ expect(headers[0].textContent).toBe('ID')
113
+ expect(headers[1].textContent).toBe('NAME')
114
+ })
115
+
116
+ it('should prefer specific header component over default', async () => {
117
+ const headerComponents: HeaderCells<TestColumns> = {
118
+ id: () => <span>Custom ID</span>,
119
+ default: (name) => <span>Default: {name}</span>,
120
+ }
121
+ const { table } = await renderGrid({
122
+ columns: ['id', 'name'],
123
+ entries: [],
124
+ headerComponents,
125
+ })
126
+ const headers = table.querySelectorAll('th')
127
+ expect(headers[0].textContent).toBe('Custom ID')
128
+ expect(headers[1].textContent).toBe('Default: name')
129
+ })
130
+ })
131
+
132
+ describe('rows', () => {
133
+ const testEntries: TestEntry[] = [
134
+ { id: 1, name: 'Item 1', value: 'Value 1' },
135
+ { id: 2, name: 'Item 2', value: 'Value 2' },
136
+ { id: 3, name: 'Item 3', value: 'Value 3' },
137
+ ]
138
+
139
+ it('should render rows for each entry', async () => {
140
+ const { table } = await renderGrid({
141
+ columns: ['id', 'name'],
142
+ entries: testEntries,
143
+ rowComponents: {
144
+ default: (entry, column) => <span>{String(entry[column])}</span>,
145
+ },
146
+ })
147
+ const rows = table.querySelectorAll('tbody tr')
148
+ expect(rows.length).toBe(3)
149
+ })
150
+
151
+ it('should render cells for each column in each row', async () => {
152
+ const { table } = await renderGrid({
153
+ columns: ['id', 'name', 'value'],
154
+ entries: testEntries,
155
+ rowComponents: {
156
+ default: (entry, column) => <span>{String(entry[column])}</span>,
157
+ },
158
+ })
159
+ const firstRow = table.querySelector('tbody tr')!
160
+ const cells = firstRow.querySelectorAll('td')
161
+ expect(cells.length).toBe(3)
162
+ })
163
+
164
+ it('should use custom row components when provided', async () => {
165
+ const rowComponents: RowCells<TestEntry, TestColumns> = {
166
+ id: (entry) => <span>ID: {entry.id}</span>,
167
+ name: (entry) => <strong>{entry.name}</strong>,
168
+ }
169
+ const { table } = await renderGrid({
170
+ columns: ['id', 'name', 'value'],
171
+ entries: [testEntries[0]],
172
+ rowComponents,
173
+ })
174
+ const cells = table.querySelectorAll('tbody td')
175
+ expect(cells[0].textContent).toBe('ID: 1')
176
+ expect(cells[1].textContent).toBe('Item 1')
177
+ expect(cells[2].textContent).toBe('')
178
+ })
179
+
180
+ it('should use default row component for unspecified columns', async () => {
181
+ const rowComponents: RowCells<TestEntry, TestColumns> = {
182
+ default: (entry, column) => <span>{String(entry[column])}</span>,
183
+ }
184
+ const { table } = await renderGrid({
185
+ columns: ['id', 'name'],
186
+ entries: [testEntries[0]],
187
+ rowComponents,
188
+ })
189
+ const cells = table.querySelectorAll('tbody td')
190
+ expect(cells[0].textContent).toBe('1')
191
+ expect(cells[1].textContent).toBe('Item 1')
192
+ })
193
+
194
+ it('should prefer specific row component over default', async () => {
195
+ const rowComponents: RowCells<TestEntry, TestColumns> = {
196
+ id: (entry) => <span>Custom: {entry.id}</span>,
197
+ default: (entry, column) => <span>Default: {String(entry[column])}</span>,
198
+ }
199
+ const { table } = await renderGrid({
200
+ columns: ['id', 'name'],
201
+ entries: [testEntries[0]],
202
+ rowComponents,
203
+ })
204
+ const cells = table.querySelectorAll('tbody td')
205
+ expect(cells[0].textContent).toBe('Custom: 1')
206
+ expect(cells[1].textContent).toBe('Default: Item 1')
207
+ })
208
+
209
+ it('should render empty cells when no row component is provided', async () => {
210
+ const { table } = await renderGrid({
211
+ columns: ['id', 'name'],
212
+ entries: [testEntries[0]],
213
+ })
214
+ const cells = table.querySelectorAll('tbody td')
215
+ expect(cells[0].textContent).toBe('')
216
+ expect(cells[1].textContent).toBe('')
217
+ })
218
+ })
219
+
220
+ describe('empty state', () => {
221
+ it('should render empty tbody when no entries', async () => {
222
+ const { table } = await renderGrid({
223
+ columns: ['id', 'name'],
224
+ entries: [],
225
+ })
226
+ const rows = table.querySelectorAll('tbody tr')
227
+ expect(rows.length).toBe(0)
228
+ })
229
+
230
+ it('should still render headers when no entries', async () => {
231
+ const { table } = await renderGrid({
232
+ columns: ['id', 'name', 'value'],
233
+ entries: [],
234
+ })
235
+ const headers = table.querySelectorAll('th')
236
+ expect(headers.length).toBe(3)
237
+ })
238
+ })
239
+
240
+ describe('styles', () => {
241
+ const testEntries: TestEntry[] = [{ id: 1, name: 'Item 1', value: 'Value 1' }]
242
+
243
+ it('should apply wrapper styles to the grid element', async () => {
244
+ const { grid } = await renderGrid({
245
+ columns: ['id', 'name'],
246
+ entries: testEntries,
247
+ rowComponents: {
248
+ default: (entry, column) => <span>{String(entry[column])}</span>,
249
+ },
250
+ styles: {
251
+ wrapper: { backgroundColor: 'rgb(255, 0, 0)' },
252
+ },
253
+ })
254
+ const computedStyle = window.getComputedStyle(grid)
255
+ expect(computedStyle.backgroundColor).toBe('rgb(255, 0, 0)')
256
+ })
257
+
258
+ it('should apply header styles to th elements', async () => {
259
+ const { table } = await renderGrid({
260
+ columns: ['id', 'name'],
261
+ entries: [],
262
+ styles: {
263
+ header: { color: 'rgb(0, 255, 0)' },
264
+ },
265
+ })
266
+ const header = table.querySelector('th')!
267
+ expect(header.style.color).toBe('rgb(0, 255, 0)')
268
+ })
269
+
270
+ it('should apply cell styles to td elements', async () => {
271
+ const { table } = await renderGrid({
272
+ columns: ['id'],
273
+ entries: testEntries,
274
+ rowComponents: {
275
+ default: (entry, column) => <span>{String(entry[column])}</span>,
276
+ },
277
+ styles: {
278
+ cell: { color: 'rgb(0, 0, 255)' },
279
+ },
280
+ })
281
+ const cell = table.querySelector('td')!
282
+ expect(cell.style.color).toBe('rgb(0, 0, 255)')
283
+ })
284
+
285
+ it('should apply multiple style properties', async () => {
286
+ const { grid, table } = await renderGrid({
287
+ columns: ['id'],
288
+ entries: testEntries,
289
+ rowComponents: {
290
+ default: (entry, column) => <span>{String(entry[column])}</span>,
291
+ },
292
+ styles: {
293
+ wrapper: { margin: '10px', padding: '20px' },
294
+ header: { fontWeight: 'bold' },
295
+ cell: { textAlign: 'center' },
296
+ },
297
+ })
298
+ expect(window.getComputedStyle(grid).margin).toBe('10px')
299
+ expect(window.getComputedStyle(grid).padding).toBe('20px')
300
+ expect(table.querySelector('th')!.style.fontWeight).toBe('bold')
301
+ expect(table.querySelector('td')!.style.textAlign).toBe('center')
302
+ })
303
+ })
304
+
305
+ describe('default styles', () => {
306
+ it('should have full width and height by default', async () => {
307
+ const { grid } = await renderGrid({
308
+ columns: ['id'],
309
+ entries: [],
310
+ })
311
+ const computedStyle = window.getComputedStyle(grid)
312
+ expect(computedStyle.width).toBe('100%')
313
+ expect(computedStyle.height).toBe('100%')
314
+ })
315
+
316
+ it('should have overflow auto', async () => {
317
+ const { grid } = await renderGrid({
318
+ columns: ['id'],
319
+ entries: [],
320
+ })
321
+ const computedStyle = window.getComputedStyle(grid)
322
+ expect(computedStyle.overflow).toBe('auto')
323
+ })
324
+
325
+ it('should have display block', async () => {
326
+ const { grid } = await renderGrid({
327
+ columns: ['id'],
328
+ entries: [],
329
+ })
330
+ const computedStyle = window.getComputedStyle(grid)
331
+ expect(computedStyle.display).toBe('block')
332
+ })
333
+ })
334
+ })
@@ -1,6 +1,6 @@
1
1
  import type { ChildrenList } from '@furystack/shades'
2
- import { Shade, createComponent } from '@furystack/shades'
3
- import { ThemeProviderService } from '../services/theme-provider-service.js'
2
+ import { attachStyles, createComponent, Shade } from '@furystack/shades'
3
+ import { cssVariableTheme } from '../services/css-variable-theme.js'
4
4
 
5
5
  // ToDo: https://stackoverflow.com/questions/51459971/type-of-generic-stateless-component-react-or-extending-generic-function-interfa
6
6
 
@@ -26,12 +26,20 @@ export type RowCells<T, Columns extends string> = {
26
26
  export const Grid: <T, Column extends string>(props: GridProps<T, Column>, children: ChildrenList) => JSX.Element<any> =
27
27
  Shade({
28
28
  shadowDomName: 'shade-grid',
29
- render: ({ props, injector }) => {
30
- const { theme } = injector.getInstance(ThemeProviderService)
31
- const headerStyle: Partial<CSSStyleDeclaration> = {
29
+ css: {
30
+ display: 'block',
31
+ width: '100%',
32
+ height: '100%',
33
+ overflow: 'auto',
34
+ '& table': {
35
+ width: '100%',
36
+ position: 'relative',
37
+ borderCollapse: 'collapse',
38
+ },
39
+ '& th': {
32
40
  padding: '1em 1.2em',
33
- backgroundColor: theme.background.paper,
34
- color: theme.text.secondary,
41
+ backgroundColor: cssVariableTheme.background.paper,
42
+ color: cssVariableTheme.text.secondary,
35
43
  borderRadius: '2px',
36
44
  top: '0',
37
45
  position: 'sticky',
@@ -41,71 +49,57 @@ export const Grid: <T, Column extends string>(props: GridProps<T, Column>, child
41
49
  fontWeight: '600',
42
50
  letterSpacing: '0.05em',
43
51
  textAlign: 'left',
44
- borderBottom: `2px solid ${theme.background.default}`,
45
- borderRight: `1px solid rgba(128, 128, 128, 0.2)`,
46
- ...props.styles?.header,
47
- }
48
-
49
- const cellStyle: Partial<CSSStyleDeclaration> = {
52
+ borderBottom: `2px solid ${cssVariableTheme.background.default}`,
53
+ borderRight: '1px solid rgba(128, 128, 128, 0.2)',
54
+ },
55
+ '& td': {
50
56
  padding: '0.75em 1.2em',
51
- borderBottom: `1px solid rgba(128, 128, 128, 0.15)`,
57
+ borderBottom: '1px solid rgba(128, 128, 128, 0.15)',
52
58
  transition: 'background-color 0.2s ease',
53
59
  fontSize: '0.875rem',
54
60
  lineHeight: '1.5',
55
- ...props.styles?.cell,
61
+ },
62
+ '& tbody tr': {
63
+ transition: 'background-color 0.2s ease',
64
+ },
65
+ '& tbody tr:hover': {
66
+ backgroundColor: 'rgba(128, 128, 128, 0.08)',
67
+ },
68
+ },
69
+ render: ({ props, element }) => {
70
+ if (props.styles?.wrapper) {
71
+ attachStyles(element, { style: props.styles.wrapper })
56
72
  }
57
73
 
58
74
  return (
59
- <div
60
- className="shade-grid-wrapper"
61
- style={{
62
- ...props.styles?.wrapper,
63
- width: '100%',
64
- height: '100%',
65
- overflow: 'auto',
66
- }}
67
- >
68
- <table style={{ width: '100%', position: 'relative', borderCollapse: 'collapse' }}>
69
- <thead>
75
+ <table>
76
+ <thead>
77
+ <tr>
78
+ {props.columns.map((column) => {
79
+ return (
80
+ <th style={props.styles?.header}>
81
+ {props.headerComponents?.[column]?.(column) || props.headerComponents?.default?.(column) || (
82
+ <>{column}</>
83
+ )}
84
+ </th>
85
+ )
86
+ })}
87
+ </tr>
88
+ </thead>
89
+ <tbody>
90
+ {props.entries.map((entry) => (
70
91
  <tr>
71
- {props.columns.map((column) => {
72
- return (
73
- <th style={headerStyle}>
74
- {props.headerComponents?.[column]?.(column) || props.headerComponents?.default?.(column) || (
75
- <>{column}</>
76
- )}
77
- </th>
78
- )
79
- })}
92
+ {props.columns.map((column) => (
93
+ <td style={props.styles?.cell}>
94
+ {props.rowComponents?.[column]?.(entry, column) ||
95
+ props.rowComponents?.default?.(entry, column) ||
96
+ null}
97
+ </td>
98
+ ))}
80
99
  </tr>
81
- </thead>
82
- <tbody>
83
- {props.entries.map((entry) => (
84
- <tr
85
- style={{
86
- transition: 'background-color 0.2s ease',
87
- }}
88
- onmouseenter={(e) => {
89
- const target = e.currentTarget as HTMLElement
90
- target.style.backgroundColor = `rgba(128, 128, 128, 0.08)`
91
- }}
92
- onmouseleave={(e) => {
93
- const target = e.currentTarget as HTMLElement
94
- target.style.backgroundColor = 'transparent'
95
- }}
96
- >
97
- {props.columns.map((column) => (
98
- <td style={cellStyle}>
99
- {props.rowComponents?.[column]?.(entry, column) ||
100
- props.rowComponents?.default?.(entry, column) ||
101
- null}
102
- </td>
103
- ))}
104
- </tr>
105
- ))}
106
- </tbody>
107
- </table>
108
- </div>
100
+ ))}
101
+ </tbody>
102
+ </table>
109
103
  )
110
104
  },
111
105
  })