@dashforge/tw 1.1.0 → 1.1.1

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 (390) hide show
  1. package/CHANGELOG.md +21 -1
  2. package/dist/index.esm.js +1 -1
  3. package/dist/src/index.d.ts +1 -1
  4. package/package.json +8 -3
  5. package/.swcrc +0 -29
  6. package/A11Y.md +0 -130
  7. package/CONSUMER-VALIDATION.md +0 -130
  8. package/COVERAGE.md +0 -63
  9. package/PARITY.md +0 -481
  10. package/PERFORMANCE.md +0 -227
  11. package/REFINEMENT-NOTES.md +0 -50
  12. package/THEME-AUDIT.md +0 -174
  13. package/dashforge-tw-1.0.0.tgz +0 -0
  14. package/eslint.config.mjs +0 -62
  15. package/project.json +0 -15
  16. package/rollup.config.cjs +0 -16
  17. package/src/components/Accordion/Accordion.test.tsx +0 -95
  18. package/src/components/Accordion/Accordion.tsx +0 -97
  19. package/src/components/Accordion/accordion.types.ts +0 -66
  20. package/src/components/Accordion/accordion.variants.ts +0 -32
  21. package/src/components/Alert/Alert.test.tsx +0 -302
  22. package/src/components/Alert/Alert.tsx +0 -186
  23. package/src/components/Alert/alert.types.ts +0 -144
  24. package/src/components/Alert/alert.variants.ts +0 -71
  25. package/src/components/AppShell/AppShell.test.tsx +0 -223
  26. package/src/components/AppShell/AppShell.tsx +0 -239
  27. package/src/components/AppShell/appShell.types.ts +0 -49
  28. package/src/components/AppShell/appShell.variants.ts +0 -54
  29. package/src/components/AspectRatio/AspectRatio.test.tsx +0 -173
  30. package/src/components/AspectRatio/AspectRatio.tsx +0 -73
  31. package/src/components/AspectRatio/aspectRatio.types.ts +0 -54
  32. package/src/components/Autocomplete/Autocomplete.perf.test.tsx +0 -225
  33. package/src/components/Autocomplete/Autocomplete.test.tsx +0 -556
  34. package/src/components/Autocomplete/Autocomplete.tsx +0 -1069
  35. package/src/components/Autocomplete/autocomplete.types.ts +0 -198
  36. package/src/components/Autocomplete/autocomplete.variants.ts +0 -141
  37. package/src/components/Avatar/Avatar.test.tsx +0 -287
  38. package/src/components/Avatar/Avatar.tsx +0 -304
  39. package/src/components/Avatar/avatar.types.ts +0 -205
  40. package/src/components/Avatar/avatar.variants.ts +0 -194
  41. package/src/components/Badge/Badge.test.tsx +0 -385
  42. package/src/components/Badge/Badge.tsx +0 -174
  43. package/src/components/Badge/badge.types.ts +0 -154
  44. package/src/components/Badge/badge.variants.ts +0 -161
  45. package/src/components/Box/Box.test.tsx +0 -391
  46. package/src/components/Box/Box.tsx +0 -112
  47. package/src/components/Box/box.types.ts +0 -116
  48. package/src/components/Box/box.variants.ts +0 -230
  49. package/src/components/Breadcrumbs/Breadcrumbs.perf.test.tsx +0 -128
  50. package/src/components/Breadcrumbs/Breadcrumbs.test.tsx +0 -231
  51. package/src/components/Breadcrumbs/Breadcrumbs.tsx +0 -203
  52. package/src/components/Breadcrumbs/breadcrumbs.types.ts +0 -87
  53. package/src/components/Breadcrumbs/breadcrumbs.variants.ts +0 -54
  54. package/src/components/Button/Button.test.tsx +0 -204
  55. package/src/components/Button/Button.tsx +0 -151
  56. package/src/components/Button/button.types.ts +0 -110
  57. package/src/components/Button/button.variants.ts +0 -159
  58. package/src/components/Calendar/Calendar.test.tsx +0 -75
  59. package/src/components/Calendar/Calendar.tsx +0 -264
  60. package/src/components/Calendar/calendar.types.ts +0 -65
  61. package/src/components/Calendar/calendar.variants.ts +0 -64
  62. package/src/components/Card/Card.test.tsx +0 -291
  63. package/src/components/Card/Card.tsx +0 -241
  64. package/src/components/Card/card.types.ts +0 -132
  65. package/src/components/Checkbox/Checkbox.test.tsx +0 -146
  66. package/src/components/Checkbox/Checkbox.tsx +0 -286
  67. package/src/components/Checkbox/checkbox.types.ts +0 -88
  68. package/src/components/Checkbox/checkbox.variants.ts +0 -68
  69. package/src/components/Chip/Chip.test.tsx +0 -316
  70. package/src/components/Chip/Chip.tsx +0 -224
  71. package/src/components/Chip/chip.types.ts +0 -128
  72. package/src/components/Chip/chip.variants.ts +0 -173
  73. package/src/components/ConfirmDialog/ConfirmDialog.perf.test.tsx +0 -172
  74. package/src/components/ConfirmDialog/ConfirmDialog.test.tsx +0 -311
  75. package/src/components/ConfirmDialog/ConfirmDialog.tsx +0 -252
  76. package/src/components/ConfirmDialog/confirmDialog.types.ts +0 -70
  77. package/src/components/ConfirmDialog/confirmDialog.variants.ts +0 -76
  78. package/src/components/Container/Container.test.tsx +0 -209
  79. package/src/components/Container/Container.tsx +0 -74
  80. package/src/components/Container/container.types.ts +0 -45
  81. package/src/components/Container/container.variants.ts +0 -81
  82. package/src/components/DataGrid/DataGrid.perf.test.tsx +0 -114
  83. package/src/components/DataGrid/DataGrid.test.tsx +0 -1153
  84. package/src/components/DataGrid/DataGrid.tsx +0 -1282
  85. package/src/components/DataGrid/dataGrid.types.ts +0 -238
  86. package/src/components/DataGrid/dataGrid.variants.ts +0 -152
  87. package/src/components/DataGrid/filters/ColumnFilters.test.tsx +0 -330
  88. package/src/components/DataGrid/filters/ColumnFilters.tsx +0 -456
  89. package/src/components/DataGrid/reorder/useColumnReorder.test.ts +0 -130
  90. package/src/components/DataGrid/reorder/useColumnReorder.ts +0 -144
  91. package/src/components/DataGrid/resize/useColumnResize.test.ts +0 -144
  92. package/src/components/DataGrid/resize/useColumnResize.ts +0 -135
  93. package/src/components/DataGrid/visibility/ColumnVisibilityMenu.tsx +0 -186
  94. package/src/components/DatePicker/DatePicker.test.tsx +0 -76
  95. package/src/components/DatePicker/DatePicker.tsx +0 -305
  96. package/src/components/DatePicker/datePicker.types.ts +0 -77
  97. package/src/components/DatePicker/datePicker.variants.ts +0 -50
  98. package/src/components/DateRangePicker/DateRangePicker.test.tsx +0 -92
  99. package/src/components/DateRangePicker/DateRangePicker.tsx +0 -510
  100. package/src/components/DateRangePicker/dateRangePicker.types.ts +0 -74
  101. package/src/components/DateRangePicker/dateRangePicker.variants.ts +0 -101
  102. package/src/components/DateTimePicker/DateTimePicker.test.tsx +0 -78
  103. package/src/components/DateTimePicker/DateTimePicker.tsx +0 -347
  104. package/src/components/DateTimePicker/dateTimePicker.types.ts +0 -79
  105. package/src/components/DateTimePicker/dateTimePicker.variants.ts +0 -55
  106. package/src/components/Dialog/Dialog.test.tsx +0 -96
  107. package/src/components/Dialog/Dialog.tsx +0 -102
  108. package/src/components/Dialog/dialog.types.ts +0 -62
  109. package/src/components/Dialog/dialog.variants.ts +0 -66
  110. package/src/components/Divider/Divider.test.tsx +0 -243
  111. package/src/components/Divider/Divider.tsx +0 -140
  112. package/src/components/Divider/divider.types.ts +0 -52
  113. package/src/components/Divider/divider.variants.ts +0 -116
  114. package/src/components/Grid/Grid.test.tsx +0 -321
  115. package/src/components/Grid/Grid.tsx +0 -148
  116. package/src/components/Grid/grid.types.ts +0 -112
  117. package/src/components/Grid/grid.variants.ts +0 -133
  118. package/src/components/IconButton/IconButton.test.tsx +0 -367
  119. package/src/components/IconButton/IconButton.tsx +0 -159
  120. package/src/components/IconButton/iconButton.types.ts +0 -106
  121. package/src/components/IconButton/iconButton.variants.ts +0 -31
  122. package/src/components/LeftNav/LeftNav.perf.test.tsx +0 -172
  123. package/src/components/LeftNav/LeftNav.test.tsx +0 -302
  124. package/src/components/LeftNav/LeftNav.tsx +0 -330
  125. package/src/components/LeftNav/leftNav.types.ts +0 -120
  126. package/src/components/LeftNav/leftNav.variants.ts +0 -107
  127. package/src/components/Menu/Menu.test.tsx +0 -408
  128. package/src/components/Menu/Menu.tsx +0 -335
  129. package/src/components/Menu/menu.types.ts +0 -221
  130. package/src/components/Menu/menu.variants.ts +0 -132
  131. package/src/components/NumberField/NumberField.test.tsx +0 -83
  132. package/src/components/NumberField/NumberField.tsx +0 -321
  133. package/src/components/NumberField/numberField.types.ts +0 -72
  134. package/src/components/NumberField/numberField.variants.ts +0 -94
  135. package/src/components/OTPField/OTPField.test.tsx +0 -121
  136. package/src/components/OTPField/OTPField.tsx +0 -250
  137. package/src/components/OTPField/otpField.types.ts +0 -58
  138. package/src/components/OTPField/otpField.variants.ts +0 -72
  139. package/src/components/Pagination/Pagination.test.tsx +0 -314
  140. package/src/components/Pagination/Pagination.tsx +0 -277
  141. package/src/components/Pagination/pagination.helpers.ts +0 -102
  142. package/src/components/Pagination/pagination.types.ts +0 -117
  143. package/src/components/Pagination/pagination.variants.ts +0 -117
  144. package/src/components/Popover/Popover.test.tsx +0 -72
  145. package/src/components/Popover/Popover.tsx +0 -55
  146. package/src/components/Popover/popover.types.ts +0 -42
  147. package/src/components/Popover/popover.variants.ts +0 -24
  148. package/src/components/RadioGroup/RadioGroup.test.tsx +0 -140
  149. package/src/components/RadioGroup/RadioGroup.tsx +0 -283
  150. package/src/components/RadioGroup/radioGroup.types.ts +0 -85
  151. package/src/components/RadioGroup/radioGroup.variants.ts +0 -87
  152. package/src/components/Skeleton/Skeleton.test.tsx +0 -127
  153. package/src/components/Skeleton/Skeleton.tsx +0 -49
  154. package/src/components/Skeleton/skeleton.types.ts +0 -59
  155. package/src/components/Skeleton/skeleton.variants.ts +0 -87
  156. package/src/components/Snackbar/Snackbar.perf.test.tsx +0 -144
  157. package/src/components/Snackbar/Snackbar.test.tsx +0 -327
  158. package/src/components/Snackbar/Snackbar.tsx +0 -316
  159. package/src/components/Snackbar/snackbar.types.ts +0 -127
  160. package/src/components/Snackbar/snackbar.variants.ts +0 -81
  161. package/src/components/Spinner/Spinner.test.tsx +0 -199
  162. package/src/components/Spinner/Spinner.tsx +0 -158
  163. package/src/components/Spinner/spinner.types.ts +0 -113
  164. package/src/components/Spinner/spinner.variants.ts +0 -83
  165. package/src/components/Stack/Stack.test.tsx +0 -309
  166. package/src/components/Stack/Stack.tsx +0 -115
  167. package/src/components/Stack/stack.types.ts +0 -68
  168. package/src/components/Stack/stack.variants.ts +0 -80
  169. package/src/components/Switch/Switch.test.tsx +0 -131
  170. package/src/components/Switch/Switch.tsx +0 -158
  171. package/src/components/Switch/switch.types.ts +0 -36
  172. package/src/components/Switch/switch.variants.ts +0 -70
  173. package/src/components/Table/Table.perf.test.tsx +0 -81
  174. package/src/components/Table/Table.test.tsx +0 -674
  175. package/src/components/Table/Table.tsx +0 -863
  176. package/src/components/Table/cells/RenderButton.tsx +0 -23
  177. package/src/components/Table/cells/RenderChip.tsx +0 -37
  178. package/src/components/Table/cells/RenderText.tsx +0 -37
  179. package/src/components/Table/cells/RenderTwoLine.tsx +0 -34
  180. package/src/components/Table/cells/RowActionsMenu.tsx +0 -147
  181. package/src/components/Table/cells/cells.test.tsx +0 -247
  182. package/src/components/Table/cells/index.ts +0 -14
  183. package/src/components/Table/table.types.ts +0 -438
  184. package/src/components/Table/table.variants.ts +0 -207
  185. package/src/components/Tabs/Tabs.test.tsx +0 -145
  186. package/src/components/Tabs/Tabs.tsx +0 -77
  187. package/src/components/Tabs/tabs.types.ts +0 -55
  188. package/src/components/Tabs/tabs.variants.ts +0 -69
  189. package/src/components/Tabs/useTabs.ts +0 -167
  190. package/src/components/TextField/TextField.test.tsx +0 -198
  191. package/src/components/TextField/TextField.tsx +0 -245
  192. package/src/components/TextField/textField.types.ts +0 -90
  193. package/src/components/TextField/textField.variants.ts +0 -105
  194. package/src/components/Textarea/Textarea.test.tsx +0 -75
  195. package/src/components/Textarea/Textarea.tsx +0 -190
  196. package/src/components/Textarea/textarea.types.ts +0 -60
  197. package/src/components/Textarea/textarea.variants.ts +0 -85
  198. package/src/components/TimePicker/TimePicker.test.tsx +0 -65
  199. package/src/components/TimePicker/TimePicker.tsx +0 -274
  200. package/src/components/TimePicker/timePicker.types.ts +0 -69
  201. package/src/components/TimePicker/timePicker.variants.ts +0 -53
  202. package/src/components/Tooltip/Tooltip.test.tsx +0 -73
  203. package/src/components/Tooltip/Tooltip.tsx +0 -70
  204. package/src/components/Tooltip/tooltip.types.ts +0 -44
  205. package/src/components/Tooltip/tooltip.variants.ts +0 -28
  206. package/src/components/TopBar/TopBar.test.tsx +0 -129
  207. package/src/components/TopBar/TopBar.tsx +0 -66
  208. package/src/components/TopBar/topBar.types.ts +0 -42
  209. package/src/components/TopBar/topBar.variants.ts +0 -38
  210. package/src/components/Typography/Typography.test.tsx +0 -247
  211. package/src/components/Typography/Typography.tsx +0 -112
  212. package/src/components/Typography/typography.types.ts +0 -65
  213. package/src/components/Typography/typography.variants.ts +0 -130
  214. package/src/components/VisuallyHidden/VisuallyHidden.test.tsx +0 -86
  215. package/src/components/VisuallyHidden/VisuallyHidden.tsx +0 -59
  216. package/src/components/VisuallyHidden/visuallyHidden.types.ts +0 -45
  217. package/src/components/_shared/data/getNestedValue.test.ts +0 -50
  218. package/src/components/_shared/data/getNestedValue.ts +0 -32
  219. package/src/components/_shared/data/useColumnAutoDetect.test.ts +0 -123
  220. package/src/components/_shared/data/useColumnAutoDetect.ts +0 -100
  221. package/src/components/_shared/data/useControllableState.ts +0 -48
  222. package/src/components/_shared/data/useDebouncedValue.ts +0 -24
  223. package/src/components/_shared/data/useTableFilter.test.ts +0 -166
  224. package/src/components/_shared/data/useTableFilter.ts +0 -118
  225. package/src/components/_shared/data/useTableSearch.test.ts +0 -115
  226. package/src/components/_shared/data/useTableSearch.ts +0 -66
  227. package/src/components/_shared/data/useTableSelection.test.ts +0 -100
  228. package/src/components/_shared/data/useTableSelection.ts +0 -100
  229. package/src/components/_shared/data/useTableSort.test.ts +0 -150
  230. package/src/components/_shared/data/useTableSort.ts +0 -141
  231. package/src/components/_shared/data/useVirtualizer.test.ts +0 -158
  232. package/src/components/_shared/data/useVirtualizer.ts +0 -199
  233. package/src/components/_shared/resolveValidationState.ts +0 -60
  234. package/src/components/_shared/severity/index.ts +0 -33
  235. package/src/components/_shared/severity/severity.types.ts +0 -50
  236. package/src/components/_shared/severity/severityIcons.tsx +0 -104
  237. package/src/components/_shared/severity/severityVariants.test.ts +0 -136
  238. package/src/components/_shared/severity/severityVariants.ts +0 -115
  239. package/src/components/_shared/themeIdentity.test.ts +0 -113
  240. package/src/hooks/useAccessState.spec.tsx +0 -161
  241. package/src/hooks/useAccessState.ts +0 -81
  242. package/src/index.ts +0 -550
  243. package/src/utils/cn.spec.ts +0 -16
  244. package/src/utils/cn.ts +0 -29
  245. package/test-output/vitest/coverage/base.css +0 -224
  246. package/test-output/vitest/coverage/block-navigation.js +0 -87
  247. package/test-output/vitest/coverage/clover.xml +0 -2418
  248. package/test-output/vitest/coverage/coverage-final.json +0 -89
  249. package/test-output/vitest/coverage/favicon.png +0 -0
  250. package/test-output/vitest/coverage/index.html +0 -746
  251. package/test-output/vitest/coverage/prettify.css +0 -1
  252. package/test-output/vitest/coverage/prettify.js +0 -2
  253. package/test-output/vitest/coverage/sort-arrow-sprite.png +0 -0
  254. package/test-output/vitest/coverage/sorter.js +0 -210
  255. package/test-output/vitest/coverage/src/components/Accordion/Accordion.tsx.html +0 -376
  256. package/test-output/vitest/coverage/src/components/Accordion/accordion.variants.ts.html +0 -181
  257. package/test-output/vitest/coverage/src/components/Accordion/index.html +0 -131
  258. package/test-output/vitest/coverage/src/components/AppShell/AppShell.tsx.html +0 -802
  259. package/test-output/vitest/coverage/src/components/AppShell/appShell.variants.ts.html +0 -247
  260. package/test-output/vitest/coverage/src/components/AppShell/index.html +0 -131
  261. package/test-output/vitest/coverage/src/components/AspectRatio/AspectRatio.tsx.html +0 -304
  262. package/test-output/vitest/coverage/src/components/AspectRatio/index.html +0 -116
  263. package/test-output/vitest/coverage/src/components/Autocomplete/Autocomplete.tsx.html +0 -3292
  264. package/test-output/vitest/coverage/src/components/Autocomplete/autocomplete.variants.ts.html +0 -508
  265. package/test-output/vitest/coverage/src/components/Autocomplete/index.html +0 -131
  266. package/test-output/vitest/coverage/src/components/Box/Box.tsx.html +0 -334
  267. package/test-output/vitest/coverage/src/components/Box/box.variants.ts.html +0 -775
  268. package/test-output/vitest/coverage/src/components/Box/index.html +0 -131
  269. package/test-output/vitest/coverage/src/components/Breadcrumbs/Breadcrumbs.tsx.html +0 -694
  270. package/test-output/vitest/coverage/src/components/Breadcrumbs/breadcrumbs.variants.ts.html +0 -247
  271. package/test-output/vitest/coverage/src/components/Breadcrumbs/index.html +0 -131
  272. package/test-output/vitest/coverage/src/components/Button/Button.tsx.html +0 -535
  273. package/test-output/vitest/coverage/src/components/Button/button.variants.ts.html +0 -562
  274. package/test-output/vitest/coverage/src/components/Button/index.html +0 -131
  275. package/test-output/vitest/coverage/src/components/Checkbox/Checkbox.tsx.html +0 -943
  276. package/test-output/vitest/coverage/src/components/Checkbox/checkbox.variants.ts.html +0 -289
  277. package/test-output/vitest/coverage/src/components/Checkbox/index.html +0 -131
  278. package/test-output/vitest/coverage/src/components/ConfirmDialog/ConfirmDialog.tsx.html +0 -841
  279. package/test-output/vitest/coverage/src/components/ConfirmDialog/confirmDialog.variants.ts.html +0 -313
  280. package/test-output/vitest/coverage/src/components/ConfirmDialog/index.html +0 -131
  281. package/test-output/vitest/coverage/src/components/Container/Container.tsx.html +0 -307
  282. package/test-output/vitest/coverage/src/components/Container/container.variants.ts.html +0 -328
  283. package/test-output/vitest/coverage/src/components/Container/index.html +0 -131
  284. package/test-output/vitest/coverage/src/components/DataGrid/DataGrid.tsx.html +0 -3937
  285. package/test-output/vitest/coverage/src/components/DataGrid/dataGrid.variants.ts.html +0 -541
  286. package/test-output/vitest/coverage/src/components/DataGrid/filters/ColumnFilters.tsx.html +0 -1453
  287. package/test-output/vitest/coverage/src/components/DataGrid/filters/index.html +0 -116
  288. package/test-output/vitest/coverage/src/components/DataGrid/index.html +0 -131
  289. package/test-output/vitest/coverage/src/components/DataGrid/reorder/index.html +0 -116
  290. package/test-output/vitest/coverage/src/components/DataGrid/reorder/useColumnReorder.ts.html +0 -517
  291. package/test-output/vitest/coverage/src/components/DataGrid/resize/index.html +0 -116
  292. package/test-output/vitest/coverage/src/components/DataGrid/resize/useColumnResize.ts.html +0 -490
  293. package/test-output/vitest/coverage/src/components/DataGrid/visibility/ColumnVisibilityMenu.tsx.html +0 -592
  294. package/test-output/vitest/coverage/src/components/DataGrid/visibility/index.html +0 -116
  295. package/test-output/vitest/coverage/src/components/DateTimePicker/DateTimePicker.tsx.html +0 -1042
  296. package/test-output/vitest/coverage/src/components/DateTimePicker/dateTimePicker.variants.ts.html +0 -337
  297. package/test-output/vitest/coverage/src/components/DateTimePicker/index.html +0 -131
  298. package/test-output/vitest/coverage/src/components/Dialog/Dialog.tsx.html +0 -391
  299. package/test-output/vitest/coverage/src/components/Dialog/dialog.variants.ts.html +0 -283
  300. package/test-output/vitest/coverage/src/components/Dialog/index.html +0 -131
  301. package/test-output/vitest/coverage/src/components/Divider/Divider.tsx.html +0 -505
  302. package/test-output/vitest/coverage/src/components/Divider/divider.variants.ts.html +0 -433
  303. package/test-output/vitest/coverage/src/components/Divider/index.html +0 -131
  304. package/test-output/vitest/coverage/src/components/Grid/Grid.tsx.html +0 -529
  305. package/test-output/vitest/coverage/src/components/Grid/grid.variants.ts.html +0 -484
  306. package/test-output/vitest/coverage/src/components/Grid/index.html +0 -131
  307. package/test-output/vitest/coverage/src/components/LeftNav/LeftNav.tsx.html +0 -1075
  308. package/test-output/vitest/coverage/src/components/LeftNav/index.html +0 -131
  309. package/test-output/vitest/coverage/src/components/LeftNav/leftNav.variants.ts.html +0 -406
  310. package/test-output/vitest/coverage/src/components/NumberField/NumberField.tsx.html +0 -1048
  311. package/test-output/vitest/coverage/src/components/NumberField/index.html +0 -131
  312. package/test-output/vitest/coverage/src/components/NumberField/numberField.variants.ts.html +0 -367
  313. package/test-output/vitest/coverage/src/components/OTPField/OTPField.tsx.html +0 -835
  314. package/test-output/vitest/coverage/src/components/OTPField/index.html +0 -131
  315. package/test-output/vitest/coverage/src/components/OTPField/otpField.variants.ts.html +0 -301
  316. package/test-output/vitest/coverage/src/components/Pagination/Pagination.tsx.html +0 -916
  317. package/test-output/vitest/coverage/src/components/Pagination/index.html +0 -146
  318. package/test-output/vitest/coverage/src/components/Pagination/pagination.helpers.ts.html +0 -391
  319. package/test-output/vitest/coverage/src/components/Pagination/pagination.variants.ts.html +0 -436
  320. package/test-output/vitest/coverage/src/components/Popover/Popover.tsx.html +0 -250
  321. package/test-output/vitest/coverage/src/components/Popover/index.html +0 -131
  322. package/test-output/vitest/coverage/src/components/Popover/popover.variants.ts.html +0 -157
  323. package/test-output/vitest/coverage/src/components/RadioGroup/RadioGroup.tsx.html +0 -934
  324. package/test-output/vitest/coverage/src/components/RadioGroup/index.html +0 -131
  325. package/test-output/vitest/coverage/src/components/RadioGroup/radioGroup.variants.ts.html +0 -346
  326. package/test-output/vitest/coverage/src/components/Skeleton/Skeleton.tsx.html +0 -232
  327. package/test-output/vitest/coverage/src/components/Skeleton/index.html +0 -131
  328. package/test-output/vitest/coverage/src/components/Skeleton/skeleton.variants.ts.html +0 -346
  329. package/test-output/vitest/coverage/src/components/Snackbar/Snackbar.tsx.html +0 -979
  330. package/test-output/vitest/coverage/src/components/Snackbar/index.html +0 -131
  331. package/test-output/vitest/coverage/src/components/Snackbar/snackbar.variants.ts.html +0 -307
  332. package/test-output/vitest/coverage/src/components/Stack/Stack.tsx.html +0 -430
  333. package/test-output/vitest/coverage/src/components/Stack/index.html +0 -131
  334. package/test-output/vitest/coverage/src/components/Stack/stack.variants.ts.html +0 -325
  335. package/test-output/vitest/coverage/src/components/Switch/Switch.tsx.html +0 -559
  336. package/test-output/vitest/coverage/src/components/Switch/index.html +0 -131
  337. package/test-output/vitest/coverage/src/components/Switch/switch.variants.ts.html +0 -295
  338. package/test-output/vitest/coverage/src/components/Table/Table.tsx.html +0 -2677
  339. package/test-output/vitest/coverage/src/components/Table/cells/RenderButton.tsx.html +0 -154
  340. package/test-output/vitest/coverage/src/components/Table/cells/RenderChip.tsx.html +0 -385
  341. package/test-output/vitest/coverage/src/components/Table/cells/RenderText.tsx.html +0 -196
  342. package/test-output/vitest/coverage/src/components/Table/cells/RenderTwoLine.tsx.html +0 -187
  343. package/test-output/vitest/coverage/src/components/Table/cells/RowActionsMenu.tsx.html +0 -526
  344. package/test-output/vitest/coverage/src/components/Table/cells/index.html +0 -191
  345. package/test-output/vitest/coverage/src/components/Table/cells/index.ts.html +0 -127
  346. package/test-output/vitest/coverage/src/components/Table/index.html +0 -131
  347. package/test-output/vitest/coverage/src/components/Table/table.variants.ts.html +0 -706
  348. package/test-output/vitest/coverage/src/components/Tabs/Tabs.tsx.html +0 -283
  349. package/test-output/vitest/coverage/src/components/Tabs/index.html +0 -131
  350. package/test-output/vitest/coverage/src/components/Tabs/tabs.variants.ts.html +0 -292
  351. package/test-output/vitest/coverage/src/components/TextField/TextField.tsx.html +0 -820
  352. package/test-output/vitest/coverage/src/components/TextField/index.html +0 -131
  353. package/test-output/vitest/coverage/src/components/TextField/textField.variants.ts.html +0 -400
  354. package/test-output/vitest/coverage/src/components/Textarea/Textarea.tsx.html +0 -655
  355. package/test-output/vitest/coverage/src/components/Textarea/index.html +0 -131
  356. package/test-output/vitest/coverage/src/components/Textarea/textarea.variants.ts.html +0 -340
  357. package/test-output/vitest/coverage/src/components/Tooltip/Tooltip.tsx.html +0 -295
  358. package/test-output/vitest/coverage/src/components/Tooltip/index.html +0 -131
  359. package/test-output/vitest/coverage/src/components/Tooltip/tooltip.variants.ts.html +0 -169
  360. package/test-output/vitest/coverage/src/components/TopBar/TopBar.tsx.html +0 -283
  361. package/test-output/vitest/coverage/src/components/TopBar/index.html +0 -131
  362. package/test-output/vitest/coverage/src/components/TopBar/topBar.variants.ts.html +0 -199
  363. package/test-output/vitest/coverage/src/components/Typography/Typography.tsx.html +0 -421
  364. package/test-output/vitest/coverage/src/components/Typography/index.html +0 -131
  365. package/test-output/vitest/coverage/src/components/Typography/typography.variants.ts.html +0 -436
  366. package/test-output/vitest/coverage/src/components/VisuallyHidden/VisuallyHidden.tsx.html +0 -262
  367. package/test-output/vitest/coverage/src/components/VisuallyHidden/index.html +0 -116
  368. package/test-output/vitest/coverage/src/components/_shared/data/getNestedValue.ts.html +0 -181
  369. package/test-output/vitest/coverage/src/components/_shared/data/index.html +0 -236
  370. package/test-output/vitest/coverage/src/components/_shared/data/useColumnAutoDetect.ts.html +0 -385
  371. package/test-output/vitest/coverage/src/components/_shared/data/useControllableState.ts.html +0 -229
  372. package/test-output/vitest/coverage/src/components/_shared/data/useDebouncedValue.ts.html +0 -157
  373. package/test-output/vitest/coverage/src/components/_shared/data/useTableFilter.ts.html +0 -439
  374. package/test-output/vitest/coverage/src/components/_shared/data/useTableSearch.ts.html +0 -283
  375. package/test-output/vitest/coverage/src/components/_shared/data/useTableSelection.ts.html +0 -385
  376. package/test-output/vitest/coverage/src/components/_shared/data/useTableSort.ts.html +0 -508
  377. package/test-output/vitest/coverage/src/components/_shared/data/useVirtualizer.ts.html +0 -682
  378. package/test-output/vitest/coverage/src/components/_shared/index.html +0 -116
  379. package/test-output/vitest/coverage/src/components/_shared/resolveValidationState.ts.html +0 -265
  380. package/test-output/vitest/coverage/src/hooks/index.html +0 -116
  381. package/test-output/vitest/coverage/src/hooks/useAccessState.ts.html +0 -328
  382. package/test-output/vitest/coverage/src/index.html +0 -116
  383. package/test-output/vitest/coverage/src/index.ts.html +0 -1153
  384. package/test-output/vitest/coverage/src/utils/cn.ts.html +0 -172
  385. package/test-output/vitest/coverage/src/utils/index.html +0 -116
  386. package/tsconfig.json +0 -13
  387. package/tsconfig.lib.json +0 -58
  388. package/tsconfig.spec.json +0 -34
  389. package/vite.config.ts +0 -33
  390. package/vitest.config.mts +0 -29
@@ -1,49 +0,0 @@
1
- import type { ReactNode } from 'react';
2
- import type { AppShellVariants } from './appShell.variants.js';
3
-
4
- export interface AppShellSlotProps {
5
- root?: { className?: string };
6
- header?: { className?: string };
7
- nav?: { className?: string };
8
- navMobile?: { className?: string };
9
- main?: { className?: string };
10
- footer?: { className?: string };
11
- backdrop?: { className?: string };
12
- }
13
-
14
- /**
15
- * Props for `<AppShell>`.
16
- *
17
- * Top-level layout orchestrator with 4 named slots:
18
- *
19
- * - `header` (top, sticky)
20
- * - `nav` (left side, fixed-width on desktop, drawer on mobile)
21
- * - `footer` (bottom, non-sticky)
22
- * - `children` (main content, scrolls independently)
23
- *
24
- * Responsive behavior:
25
- * - **≥ md (768px)**: nav is rendered inline at the left.
26
- * - **< md**: nav slides in as an overlay drawer when
27
- * `navOpen={true}` (controlled).
28
- *
29
- * The shell is router-agnostic — pass any nav/header content. Typical
30
- * pairing is `<LeftNav>` + `<TopBar>` (with a hamburger button in
31
- * `TopBar.start` that flips `navOpen`).
32
- */
33
- export interface AppShellProps extends AppShellVariants {
34
- header?: ReactNode;
35
- nav?: ReactNode;
36
- footer?: ReactNode;
37
- children?: ReactNode;
38
- /**
39
- * Mobile drawer open state. Controlled — provide
40
- * `onNavOpenChange` to update. When `undefined`, the mobile drawer
41
- * is always closed (consumer hasn't wired up the toggle yet).
42
- */
43
- navOpen?: boolean;
44
- onNavOpenChange?: (open: boolean) => void;
45
- /** Root className shortcut. */
46
- sx?: string;
47
- /** Per-slot className overrides. */
48
- slotProps?: AppShellSlotProps;
49
- }
@@ -1,54 +0,0 @@
1
- import { tv, type VariantProps } from 'tailwind-variants';
2
-
3
- /**
4
- * Tailwind-variants recipe for `<AppShell>`.
5
- *
6
- * Slots:
7
- * - `root` — outer flex column (full viewport)
8
- * - `header` — top region
9
- * - `body` — flex row holding nav + main
10
- * - `nav` — desktop nav rail (md+)
11
- * - `navMobile` — mobile drawer (slide-in)
12
- * - `main` — scrollable content area
13
- * - `footer` — bottom region
14
- * - `backdrop` — semi-opaque overlay behind the mobile drawer
15
- */
16
- export const appShellVariants = tv({
17
- slots: {
18
- root: 'flex flex-col min-h-screen bg-neutral-100',
19
- header: 'shrink-0',
20
- body: 'flex flex-1 min-h-0',
21
- nav: 'hidden md:flex shrink-0',
22
- navMobile: [
23
- 'flex md:hidden fixed inset-y-0 left-0 z-40',
24
- // Drawer slide-in is the most prominent motion in AppShell and
25
- // a clear WCAG 2.3.3 candidate. Gate the transition on
26
- // `prefers-reduced-motion: no-preference`; the data-driven
27
- // `-translate-x-full` / `translate-x-0` still applies, just
28
- // without the smooth slide.
29
- 'transition-transform duration-200 ease-out motion-reduce:transition-none motion-reduce:duration-0',
30
- '-translate-x-full',
31
- ],
32
- main: 'flex-1 min-w-0 overflow-y-auto',
33
- footer: 'shrink-0',
34
- backdrop: [
35
- 'md:hidden fixed inset-0 z-30 bg-black/40',
36
- // Backdrop opacity fade — micro motion, gated for consistency.
37
- 'transition-opacity duration-200 motion-reduce:transition-none motion-reduce:duration-0',
38
- 'opacity-0 pointer-events-none',
39
- ],
40
- },
41
- variants: {
42
- navOpen: {
43
- true: {
44
- navMobile: 'translate-x-0',
45
- backdrop: 'opacity-100 pointer-events-auto',
46
- },
47
- },
48
- },
49
- defaultVariants: {
50
- navOpen: false,
51
- },
52
- });
53
-
54
- export type AppShellVariants = VariantProps<typeof appShellVariants>;
@@ -1,173 +0,0 @@
1
- // @vitest-environment jsdom
2
- import { describe, it, expect } from 'vitest';
3
- import { render } from '@testing-library/react';
4
- import { AspectRatio } from './AspectRatio.js';
5
-
6
- describe('<AspectRatio>', () => {
7
- // ─── Default rendering ──────────────────────────────────────────────
8
- describe('default rendering', () => {
9
- it('renders a <div> by default', () => {
10
- const { container } = render(<AspectRatio>x</AspectRatio>);
11
- expect(container.firstElementChild?.tagName).toBe('DIV');
12
- });
13
-
14
- it('default ratio is 1 (square)', () => {
15
- const { container } = render(<AspectRatio>x</AspectRatio>);
16
- const el = container.firstElementChild as HTMLElement;
17
- expect(el.style.aspectRatio).toBe('1 / 1');
18
- });
19
-
20
- it('always has w-full baseline', () => {
21
- const { container } = render(<AspectRatio>x</AspectRatio>);
22
- expect(container.firstElementChild?.className).toContain('w-full');
23
- });
24
- });
25
-
26
- // ─── ratio prop ─────────────────────────────────────────────────────
27
- describe('ratio', () => {
28
- it('numeric ratio 16/9 → "1.7777… / 1"', () => {
29
- const { container } = render(<AspectRatio ratio={16 / 9}>x</AspectRatio>);
30
- const el = container.firstElementChild as HTMLElement;
31
- // 16/9 ≈ 1.7777, formatted as "<number> / 1"
32
- expect(el.style.aspectRatio).toMatch(/^1\.\d+ \/ 1$/);
33
- });
34
-
35
- it('numeric ratio 4/3', () => {
36
- const { container } = render(<AspectRatio ratio={4 / 3}>x</AspectRatio>);
37
- const el = container.firstElementChild as HTMLElement;
38
- expect(el.style.aspectRatio).toMatch(/^1\.\d+ \/ 1$/);
39
- });
40
-
41
- it('string ratio "16 / 9" passes through', () => {
42
- const { container } = render(<AspectRatio ratio="16 / 9">x</AspectRatio>);
43
- const el = container.firstElementChild as HTMLElement;
44
- expect(el.style.aspectRatio).toBe('16 / 9');
45
- });
46
-
47
- it('string ratio "21/9" passes through (ultrawide)', () => {
48
- const { container } = render(<AspectRatio ratio="21/9">x</AspectRatio>);
49
- const el = container.firstElementChild as HTMLElement;
50
- expect(el.style.aspectRatio).toBe('21/9');
51
- });
52
- });
53
-
54
- // ─── Override ───────────────────────────────────────────────────────
55
- describe('override', () => {
56
- it('sx adds utility classes', () => {
57
- const { container } = render(
58
- <AspectRatio ratio={16 / 9} sx="rounded-xl overflow-hidden">
59
- x
60
- </AspectRatio>,
61
- );
62
- const cls = container.firstElementChild?.className ?? '';
63
- expect(cls).toContain('rounded-xl');
64
- expect(cls).toContain('overflow-hidden');
65
- });
66
-
67
- it('user style merges with aspect-ratio', () => {
68
- const { container } = render(
69
- <AspectRatio ratio={1} style={{ backgroundColor: 'red' }}>x</AspectRatio>,
70
- );
71
- const el = container.firstElementChild as HTMLElement;
72
- expect(el.style.backgroundColor).toBe('red');
73
- expect(el.style.aspectRatio).toBe('1 / 1');
74
- });
75
-
76
- it('user style aspect-ratio overrides the prop (last wins)', () => {
77
- const { container } = render(
78
- <AspectRatio ratio={1} style={{ aspectRatio: '2 / 1' }}>x</AspectRatio>,
79
- );
80
- const el = container.firstElementChild as HTMLElement;
81
- // Inline style merge — user's later spread wins
82
- expect(el.style.aspectRatio).toBe('2 / 1');
83
- });
84
- });
85
-
86
- // ─── Polymorphism ───────────────────────────────────────────────────
87
- describe('polymorphism', () => {
88
- it('as="figure" renders <figure>', () => {
89
- const { container } = render(
90
- <AspectRatio as="figure" ratio={1}>x</AspectRatio>,
91
- );
92
- expect(container.firstElementChild?.tagName).toBe('FIGURE');
93
- });
94
-
95
- it('as="picture" works for responsive image markup', () => {
96
- const { container } = render(
97
- <AspectRatio as="picture" ratio={16 / 9}>
98
- <img src="x.jpg" alt="" />
99
- </AspectRatio>,
100
- );
101
- expect(container.firstElementChild?.tagName).toBe('PICTURE');
102
- });
103
- });
104
-
105
- // ─── Child rendering ────────────────────────────────────────────────
106
- describe('child rendering', () => {
107
- it('renders an <img> child', () => {
108
- const { container } = render(
109
- <AspectRatio ratio={16 / 9}>
110
- <img src="hero.jpg" alt="hero" className="w-full h-full object-cover" />
111
- </AspectRatio>,
112
- );
113
- const img = container.querySelector('img');
114
- expect(img?.getAttribute('src')).toBe('hero.jpg');
115
- });
116
-
117
- it('renders multiple children (e.g. overlay pattern)', () => {
118
- const { container } = render(
119
- <AspectRatio ratio={16 / 9}>
120
- <img src="hero.jpg" alt="hero" />
121
- <div className="absolute inset-0 bg-black/40" />
122
- </AspectRatio>,
123
- );
124
- expect(container.firstElementChild?.children.length).toBe(2);
125
- });
126
- });
127
-
128
- // ─── Pass-through ───────────────────────────────────────────────────
129
- it('forwards data-* + aria-*', () => {
130
- const { container } = render(
131
- <AspectRatio data-testid="ar" aria-label="hero">x</AspectRatio>,
132
- );
133
- const el = container.firstElementChild;
134
- expect(el?.getAttribute('data-testid')).toBe('ar');
135
- expect(el?.getAttribute('aria-label')).toBe('hero');
136
- });
137
-
138
- // ─── F11-bis edge cases ─────────────────────────────────────────────
139
- describe('extreme ratio values', () => {
140
- it('ratio="21 / 9" (ultrawide cinema) passes through verbatim', () => {
141
- const { container } = render(<AspectRatio ratio="21 / 9">x</AspectRatio>);
142
- expect((container.firstElementChild as HTMLElement).style.aspectRatio).toBe('21 / 9');
143
- });
144
-
145
- it('ratio="9 / 16" (portrait video) passes through verbatim', () => {
146
- const { container } = render(<AspectRatio ratio="9 / 16">x</AspectRatio>);
147
- expect((container.firstElementChild as HTMLElement).style.aspectRatio).toBe('9 / 16');
148
- });
149
-
150
- it('numeric tiny ratio (0.5) formats as "0.5 / 1"', () => {
151
- const { container } = render(<AspectRatio ratio={0.5}>x</AspectRatio>);
152
- expect((container.firstElementChild as HTMLElement).style.aspectRatio).toBe('0.5 / 1');
153
- });
154
-
155
- it('numeric large ratio (3) formats as "3 / 1"', () => {
156
- const { container } = render(<AspectRatio ratio={3}>x</AspectRatio>);
157
- expect((container.firstElementChild as HTMLElement).style.aspectRatio).toBe('3 / 1');
158
- });
159
- });
160
-
161
- describe('rounded clipped media pattern (the #1 doc gotcha)', () => {
162
- it('rounded + overflow-hidden both end up on the className', () => {
163
- const { container } = render(
164
- <AspectRatio ratio={16 / 9} sx="rounded-2xl overflow-hidden">
165
- <img src="x.jpg" alt="" className="w-full h-full object-cover" />
166
- </AspectRatio>,
167
- );
168
- const cls = container.firstElementChild?.className ?? '';
169
- expect(cls).toContain('rounded-2xl');
170
- expect(cls).toContain('overflow-hidden');
171
- });
172
- });
173
- });
@@ -1,73 +0,0 @@
1
- import { forwardRef, type CSSProperties, type ElementType } from 'react';
2
- import { cn } from '../../utils/cn.js';
3
- import type { AspectRatioProps } from './aspectRatio.types.js';
4
-
5
- /**
6
- * `<AspectRatio>` — locks the aspect ratio of its child container,
7
- * regardless of width. The classic use is responsive images and
8
- * embedded media: an `<img>` that takes 100% of the available width
9
- * but always renders at 16:9 (or 1:1, or whatever the source ratio is)
10
- * — no jumping layouts during image load, no whitespace below the
11
- * media, no JS measurement.
12
- *
13
- * Implementation: native CSS `aspect-ratio` property. Supported in
14
- * every browser shipped from 2021 onward (Chrome 88, Firefox 89,
15
- * Safari 15, Edge 88). No padding-bottom hack — that workaround
16
- * predates the native property and brings ugly absolute-positioning
17
- * requirements on the child.
18
- *
19
- * Why a component if it's "just one CSS property"?
20
- * Two reasons:
21
- * 1. Discoverability — `<AspectRatio ratio={16/9}>` documents the
22
- * intent at the call site. `style={{ aspectRatio: '16/9' }}` is
23
- * the same thing functionally, but harder to spot in a 200-line
24
- * component file.
25
- * 2. Composition — pairs naturally with `sx="rounded-xl overflow-hidden"`
26
- * for the canonical "rounded clipped media" pattern. Forgetting
27
- * the `overflow-hidden` is the #1 mistake we want to prevent
28
- * through documentation (it's in this component's MDX, at the
29
- * top of the Notes).
30
- *
31
- * Child contract:
32
- * The single child is expected to fill the container — typically
33
- * `<img>` / `<video>` with `className="w-full h-full object-cover"`.
34
- * We don't force this via CSS (the consumer might want a centered
35
- * icon instead of a filling image) — but it's the 99% case, and the
36
- * docs show it first.
37
- */
38
- export const AspectRatio = forwardRef<HTMLElement, AspectRatioProps>(
39
- function AspectRatio(props, ref) {
40
- const {
41
- ratio = 1,
42
- as,
43
- sx,
44
- style,
45
- children,
46
- ...rest
47
- } = props;
48
-
49
- /*
50
- * Normalise to a CSS `aspect-ratio` string. The CSS property
51
- * accepts both `16/9` and `16 / 9` (with spaces), but for safety
52
- * we convert numbers to the canonical `N / 1` form — `aspectRatio: 1.7777`
53
- * works too, but produces an arbitrary-looking value in DevTools.
54
- */
55
- const aspectRatioValue = typeof ratio === 'number' ? `${ratio} / 1` : ratio;
56
-
57
- const mergedStyle: CSSProperties = {
58
- aspectRatio: aspectRatioValue,
59
- ...style,
60
- };
61
-
62
- const classes = cn('w-full', sx);
63
- const Tag = (as ?? 'div') as ElementType;
64
-
65
- return (
66
- <Tag ref={ref as never} className={classes} style={mergedStyle} {...rest}>
67
- {children}
68
- </Tag>
69
- );
70
- },
71
- );
72
-
73
- AspectRatio.displayName = 'AspectRatio';
@@ -1,54 +0,0 @@
1
- import type { ElementType, HTMLAttributes, ReactNode } from 'react';
2
-
3
- /**
4
- * Props for `<AspectRatio>` — content-shape primitive that locks the
5
- * aspect ratio of its child regardless of width.
6
- *
7
- * Implementation: uses the native CSS `aspect-ratio` property
8
- * (supported in all current browsers since 2021 — Chrome 88+, Firefox 89+,
9
- * Safari 15+, Edge 88+). No padding-bottom hack, no JS measurement.
10
- *
11
- * `ratio` accepts:
12
- * • A number — width / height. `16/9` is 1.7777…, `1` is square,
13
- * `4/3` is 1.333…, `21/9` is ultrawide cinema.
14
- * • A string with the CSS `aspect-ratio` syntax, e.g. `'16 / 9'`,
15
- * `'4 / 3'`. Useful when you want the source ratio to read clearly
16
- * in the JSX (`ratio="16 / 9"` is more legible than `ratio={16/9}`).
17
- *
18
- * The component renders a single element with `aspect-ratio: X` and
19
- * `width: 100%`. The child is expected to fill it — typically an
20
- * `<img>` or `<video>` with `className="w-full h-full object-cover"`.
21
- *
22
- * Why not a TV recipe?
23
- * The ratio is arbitrary (`16/9`, `1`, `2.35`, anything). Tailwind
24
- * has `aspect-square` / `aspect-video` / `aspect-[16/9]` arbitrary
25
- * values, but enumerating every conceivable ratio in TV would be
26
- * pointless. We set `style={{ aspectRatio }}` directly — pure CSS
27
- * property, no class purge concerns.
28
- */
29
- export interface AspectRatioProps
30
- extends Omit<HTMLAttributes<HTMLDivElement>, 'className'> {
31
- /**
32
- * Aspect ratio as width/height. Number (`16/9`) or CSS string
33
- * (`'16 / 9'`). Default `1` (square).
34
- */
35
- ratio?: number | string;
36
-
37
- /**
38
- * The child that fills the locked-ratio container. Typically an
39
- * `<img>` or `<video>` with `className="w-full h-full object-cover"`.
40
- */
41
- children?: ReactNode;
42
-
43
- /**
44
- * Override the rendered HTML tag. Defaults to `'div'`.
45
- */
46
- as?: ElementType;
47
-
48
- /**
49
- * Utility classes appended to the base. Resolved via `tailwind-merge`.
50
- * Common overrides: `sx="rounded-xl overflow-hidden"` to clip the
51
- * child's overflow (image bleeds otherwise).
52
- */
53
- sx?: string;
54
- }
@@ -1,225 +0,0 @@
1
- // @vitest-environment jsdom
2
- /**
3
- * Performance + re-render budget tests for `<Autocomplete>`.
4
- *
5
- * These are NOT "is X fast" benchmarks — they're guardrails against
6
- * regressions in the things that matter for a 1000+ option combobox:
7
- *
8
- * 1. **Filter memoization**: `filteredOptions` must be reference-stable
9
- * when its inputs (options + filter text + selection state) don't
10
- * change. A stable reference means downstream lists don't re-mount.
11
- *
12
- * 2. **Render budget**: typing N characters must cause O(N) renders
13
- * of the component, not O(N²) or worse. With our internals there
14
- * are 2 renders per keystroke (one for `inputValue`, one for the
15
- * popover opening on first keystroke), so the budget is `2N + k`
16
- * for a small constant `k`.
17
- *
18
- * 3. **Initial render time** with a large option list must finish
19
- * within a generous wall-clock budget. This catches a future
20
- * refactor that, say, accidentally calls `getOptionLabel` 1000²
21
- * times during mount.
22
- */
23
- import { describe, it, expect } from 'vitest';
24
- import { useEffect, useRef } from 'react';
25
- import { render, fireEvent, screen } from '@testing-library/react';
26
- import { Autocomplete } from './Autocomplete.js';
27
- import type { AutocompleteOption } from './autocomplete.types.js';
28
-
29
- // Generate `n` deterministic options.
30
- function makeOptions(n: number): AutocompleteOption[] {
31
- return Array.from({ length: n }, (_, i) => ({
32
- value: `o-${i}`,
33
- label: `Option ${i}`,
34
- }));
35
- }
36
-
37
- /**
38
- * Wrap a component and expose a render-counter ref so tests can assert
39
- * how many times React re-rendered the wrapped subtree.
40
- */
41
- function withRenderCounter<P>(
42
- Component: React.ComponentType<P>,
43
- counterRef: { count: number }
44
- ): React.FC<P> {
45
- return function Counted(props: P) {
46
- counterRef.count++;
47
- return <Component {...(props as P & object)} />;
48
- };
49
- }
50
-
51
- describe('<Autocomplete> performance', () => {
52
- it('mounts a 1000-option list well under 500ms', () => {
53
- const options = makeOptions(1000);
54
- const t0 = performance.now();
55
- render(<Autocomplete name="x" options={options} label="X" />);
56
- const t1 = performance.now();
57
- expect(t1 - t0).toBeLessThan(500);
58
- });
59
-
60
- it('filters a 1000-option list well under 100ms per keystroke', () => {
61
- const options = makeOptions(1000);
62
- render(<Autocomplete name="x" options={options} label="X" />);
63
- const input = screen.getByRole('combobox') as HTMLInputElement;
64
- const t0 = performance.now();
65
- fireEvent.change(input, { target: { value: '9' } });
66
- const t1 = performance.now();
67
- expect(t1 - t0).toBeLessThan(100);
68
- });
69
- });
70
-
71
- describe('<Autocomplete> re-render budget', () => {
72
- it('renders at most 2 times on mount (StrictMode-free path)', () => {
73
- const counter = { count: 0 };
74
- const Counted = withRenderCounter(Autocomplete, counter);
75
- render(<Counted name="x" options={makeOptions(20)} label="X" />);
76
- // 1 initial render — and we tolerate up to 2 to account for the
77
- // synchronous useState callbacks that schedule a follow-up commit.
78
- expect(counter.count).toBeLessThanOrEqual(2);
79
- });
80
-
81
- it('keeps renders linear in keystrokes (≤ 3 renders per char)', () => {
82
- const counter = { count: 0 };
83
- const Counted = withRenderCounter(Autocomplete, counter);
84
- const { container } = render(
85
- <Counted name="x" options={makeOptions(50)} label="X" />
86
- );
87
- const baseline = counter.count;
88
- const input = container.querySelector(
89
- 'input[role="combobox"]'
90
- ) as HTMLInputElement;
91
- const N = 10;
92
- for (let i = 0; i < N; i++) {
93
- fireEvent.change(input, { target: { value: 'a'.repeat(i + 1) } });
94
- }
95
- const perChar = (counter.count - baseline) / N;
96
- // 1-2 renders per keystroke is the design budget; allow some slack
97
- // for popover-open transitions on the first keystroke and the
98
- // highlight reset useState call.
99
- expect(perChar).toBeLessThanOrEqual(3);
100
- });
101
-
102
- it('does NOT re-render an unrelated sibling on each keystroke', () => {
103
- // Critical perf property: typing into one Autocomplete must not
104
- // cause sibling components in the same tree to re-render. We check
105
- // this by mounting a sibling whose render count we count directly.
106
- const sibCount = { count: 0 };
107
- const Sibling = () => {
108
- sibCount.count++;
109
- return <div data-testid="sib" />;
110
- };
111
- render(
112
- <div>
113
- <Autocomplete name="x" options={makeOptions(50)} label="X" />
114
- <Sibling />
115
- </div>
116
- );
117
- const before = sibCount.count;
118
- const input = screen.getByRole('combobox') as HTMLInputElement;
119
- for (let i = 0; i < 5; i++) {
120
- fireEvent.change(input, { target: { value: 'a'.repeat(i + 1) } });
121
- }
122
- // No state in the parent changes ⇒ sibling never re-renders.
123
- expect(sibCount.count).toBe(before);
124
- });
125
-
126
- it('filteredOptions reference is stable across unrelated re-renders', () => {
127
- // Functional contract: if the deps (options, filter text, selection)
128
- // don't change, `useMemo` should return the same array reference.
129
- // We verify by triggering a parent re-render with no relevant input
130
- // change and asserting the `<li>` DOM elements are reused (same id
131
- // identities ⇒ React reused the keys).
132
- const options = makeOptions(20);
133
- const seenIds = new Set<string>();
134
- const { rerender } = render(
135
- <Autocomplete name="x" options={options} label="X" />
136
- );
137
- const input = screen.getByRole('combobox') as HTMLInputElement;
138
- fireEvent.focus(input); // open popover so the list renders
139
- const lis1 = Array.from(
140
- document.querySelectorAll('[role="option"]')
141
- ).map((el) => el.id);
142
- lis1.forEach((id) => seenIds.add(id));
143
-
144
- // Trigger an unrelated re-render: same options ref, same input
145
- // value, same selection.
146
- rerender(<Autocomplete name="x" options={options} label="X" />);
147
- const lis2 = Array.from(
148
- document.querySelectorAll('[role="option"]')
149
- ).map((el) => el.id);
150
-
151
- // Same ids ⇒ React keyed reuse held; list DOM was not torn down.
152
- expect(lis2).toEqual(lis1);
153
- });
154
-
155
- it('captures number of options accessor calls during one filter cycle', () => {
156
- // Implementation detail with strong perf relevance: filtering 1000
157
- // options must call `getOptionLabel` at most O(N) times per filter
158
- // (NOT O(N²) or anything weirdly memoized that re-computes per
159
- // child).
160
- let labelCalls = 0;
161
- let valueCalls = 0;
162
- const N = 200;
163
- const options: AutocompleteOption[] = makeOptions(N);
164
- render(
165
- <Autocomplete
166
- name="x"
167
- options={options}
168
- label="X"
169
- getOptionLabel={(o) => {
170
- labelCalls++;
171
- return o.label;
172
- }}
173
- getOptionValue={(o) => {
174
- valueCalls++;
175
- return o.value;
176
- }}
177
- />
178
- );
179
- const input = screen.getByRole('combobox') as HTMLInputElement;
180
- labelCalls = 0;
181
- valueCalls = 0;
182
- fireEvent.change(input, { target: { value: '5' } });
183
- // Budget: O(N) on both. Allow 4x N to cover the filter pass plus
184
- // the popover-open render's per-option `getOptionValue` for ids
185
- // and `getOptionLabel` for the rendered <li> body.
186
- expect(labelCalls).toBeLessThanOrEqual(4 * N);
187
- expect(valueCalls).toBeLessThanOrEqual(4 * N);
188
- });
189
- });
190
-
191
- describe('<Autocomplete> async + perf interaction', () => {
192
- it('debounces loadOptions so 5 rapid keystrokes ⇒ 1 fetch', async () => {
193
- const calls: string[] = [];
194
- const load = (q: string) => {
195
- calls.push(q);
196
- return Promise.resolve<AutocompleteOption[]>([]);
197
- };
198
- render(
199
- <Autocomplete
200
- name="x"
201
- options={[]}
202
- loadOptions={load}
203
- loadDebounceMs={50}
204
- />
205
- );
206
- const input = screen.getByRole('combobox') as HTMLInputElement;
207
- // Type 5 chars in quick succession (synchronous).
208
- for (let i = 0; i < 5; i++) {
209
- fireEvent.change(input, { target: { value: 'abcde'.slice(0, i + 1) } });
210
- }
211
- // Before debounce expires, no call yet.
212
- expect(calls.length).toBe(0);
213
- // Wait past debounce window.
214
- await new Promise((r) => setTimeout(r, 80));
215
- // Exactly ONE call lands, with the LAST typed value.
216
- expect(calls).toEqual(['abcde']);
217
- });
218
- });
219
-
220
- /**
221
- * Module-level useEffect import sanity (suppresses "useRef declared but
222
- * not used" if a test runner tree-shake removes some perf tests in CI).
223
- */
224
- void useRef;
225
- void useEffect;