@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.
- package/CHANGELOG.md +21 -1
- package/dist/index.esm.js +1 -1
- package/dist/src/index.d.ts +1 -1
- package/package.json +8 -3
- package/.swcrc +0 -29
- package/A11Y.md +0 -130
- package/CONSUMER-VALIDATION.md +0 -130
- package/COVERAGE.md +0 -63
- package/PARITY.md +0 -481
- package/PERFORMANCE.md +0 -227
- package/REFINEMENT-NOTES.md +0 -50
- package/THEME-AUDIT.md +0 -174
- package/dashforge-tw-1.0.0.tgz +0 -0
- package/eslint.config.mjs +0 -62
- package/project.json +0 -15
- package/rollup.config.cjs +0 -16
- package/src/components/Accordion/Accordion.test.tsx +0 -95
- package/src/components/Accordion/Accordion.tsx +0 -97
- package/src/components/Accordion/accordion.types.ts +0 -66
- package/src/components/Accordion/accordion.variants.ts +0 -32
- package/src/components/Alert/Alert.test.tsx +0 -302
- package/src/components/Alert/Alert.tsx +0 -186
- package/src/components/Alert/alert.types.ts +0 -144
- package/src/components/Alert/alert.variants.ts +0 -71
- package/src/components/AppShell/AppShell.test.tsx +0 -223
- package/src/components/AppShell/AppShell.tsx +0 -239
- package/src/components/AppShell/appShell.types.ts +0 -49
- package/src/components/AppShell/appShell.variants.ts +0 -54
- package/src/components/AspectRatio/AspectRatio.test.tsx +0 -173
- package/src/components/AspectRatio/AspectRatio.tsx +0 -73
- package/src/components/AspectRatio/aspectRatio.types.ts +0 -54
- package/src/components/Autocomplete/Autocomplete.perf.test.tsx +0 -225
- package/src/components/Autocomplete/Autocomplete.test.tsx +0 -556
- package/src/components/Autocomplete/Autocomplete.tsx +0 -1069
- package/src/components/Autocomplete/autocomplete.types.ts +0 -198
- package/src/components/Autocomplete/autocomplete.variants.ts +0 -141
- package/src/components/Avatar/Avatar.test.tsx +0 -287
- package/src/components/Avatar/Avatar.tsx +0 -304
- package/src/components/Avatar/avatar.types.ts +0 -205
- package/src/components/Avatar/avatar.variants.ts +0 -194
- package/src/components/Badge/Badge.test.tsx +0 -385
- package/src/components/Badge/Badge.tsx +0 -174
- package/src/components/Badge/badge.types.ts +0 -154
- package/src/components/Badge/badge.variants.ts +0 -161
- package/src/components/Box/Box.test.tsx +0 -391
- package/src/components/Box/Box.tsx +0 -112
- package/src/components/Box/box.types.ts +0 -116
- package/src/components/Box/box.variants.ts +0 -230
- package/src/components/Breadcrumbs/Breadcrumbs.perf.test.tsx +0 -128
- package/src/components/Breadcrumbs/Breadcrumbs.test.tsx +0 -231
- package/src/components/Breadcrumbs/Breadcrumbs.tsx +0 -203
- package/src/components/Breadcrumbs/breadcrumbs.types.ts +0 -87
- package/src/components/Breadcrumbs/breadcrumbs.variants.ts +0 -54
- package/src/components/Button/Button.test.tsx +0 -204
- package/src/components/Button/Button.tsx +0 -151
- package/src/components/Button/button.types.ts +0 -110
- package/src/components/Button/button.variants.ts +0 -159
- package/src/components/Calendar/Calendar.test.tsx +0 -75
- package/src/components/Calendar/Calendar.tsx +0 -264
- package/src/components/Calendar/calendar.types.ts +0 -65
- package/src/components/Calendar/calendar.variants.ts +0 -64
- package/src/components/Card/Card.test.tsx +0 -291
- package/src/components/Card/Card.tsx +0 -241
- package/src/components/Card/card.types.ts +0 -132
- package/src/components/Checkbox/Checkbox.test.tsx +0 -146
- package/src/components/Checkbox/Checkbox.tsx +0 -286
- package/src/components/Checkbox/checkbox.types.ts +0 -88
- package/src/components/Checkbox/checkbox.variants.ts +0 -68
- package/src/components/Chip/Chip.test.tsx +0 -316
- package/src/components/Chip/Chip.tsx +0 -224
- package/src/components/Chip/chip.types.ts +0 -128
- package/src/components/Chip/chip.variants.ts +0 -173
- package/src/components/ConfirmDialog/ConfirmDialog.perf.test.tsx +0 -172
- package/src/components/ConfirmDialog/ConfirmDialog.test.tsx +0 -311
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +0 -252
- package/src/components/ConfirmDialog/confirmDialog.types.ts +0 -70
- package/src/components/ConfirmDialog/confirmDialog.variants.ts +0 -76
- package/src/components/Container/Container.test.tsx +0 -209
- package/src/components/Container/Container.tsx +0 -74
- package/src/components/Container/container.types.ts +0 -45
- package/src/components/Container/container.variants.ts +0 -81
- package/src/components/DataGrid/DataGrid.perf.test.tsx +0 -114
- package/src/components/DataGrid/DataGrid.test.tsx +0 -1153
- package/src/components/DataGrid/DataGrid.tsx +0 -1282
- package/src/components/DataGrid/dataGrid.types.ts +0 -238
- package/src/components/DataGrid/dataGrid.variants.ts +0 -152
- package/src/components/DataGrid/filters/ColumnFilters.test.tsx +0 -330
- package/src/components/DataGrid/filters/ColumnFilters.tsx +0 -456
- package/src/components/DataGrid/reorder/useColumnReorder.test.ts +0 -130
- package/src/components/DataGrid/reorder/useColumnReorder.ts +0 -144
- package/src/components/DataGrid/resize/useColumnResize.test.ts +0 -144
- package/src/components/DataGrid/resize/useColumnResize.ts +0 -135
- package/src/components/DataGrid/visibility/ColumnVisibilityMenu.tsx +0 -186
- package/src/components/DatePicker/DatePicker.test.tsx +0 -76
- package/src/components/DatePicker/DatePicker.tsx +0 -305
- package/src/components/DatePicker/datePicker.types.ts +0 -77
- package/src/components/DatePicker/datePicker.variants.ts +0 -50
- package/src/components/DateRangePicker/DateRangePicker.test.tsx +0 -92
- package/src/components/DateRangePicker/DateRangePicker.tsx +0 -510
- package/src/components/DateRangePicker/dateRangePicker.types.ts +0 -74
- package/src/components/DateRangePicker/dateRangePicker.variants.ts +0 -101
- package/src/components/DateTimePicker/DateTimePicker.test.tsx +0 -78
- package/src/components/DateTimePicker/DateTimePicker.tsx +0 -347
- package/src/components/DateTimePicker/dateTimePicker.types.ts +0 -79
- package/src/components/DateTimePicker/dateTimePicker.variants.ts +0 -55
- package/src/components/Dialog/Dialog.test.tsx +0 -96
- package/src/components/Dialog/Dialog.tsx +0 -102
- package/src/components/Dialog/dialog.types.ts +0 -62
- package/src/components/Dialog/dialog.variants.ts +0 -66
- package/src/components/Divider/Divider.test.tsx +0 -243
- package/src/components/Divider/Divider.tsx +0 -140
- package/src/components/Divider/divider.types.ts +0 -52
- package/src/components/Divider/divider.variants.ts +0 -116
- package/src/components/Grid/Grid.test.tsx +0 -321
- package/src/components/Grid/Grid.tsx +0 -148
- package/src/components/Grid/grid.types.ts +0 -112
- package/src/components/Grid/grid.variants.ts +0 -133
- package/src/components/IconButton/IconButton.test.tsx +0 -367
- package/src/components/IconButton/IconButton.tsx +0 -159
- package/src/components/IconButton/iconButton.types.ts +0 -106
- package/src/components/IconButton/iconButton.variants.ts +0 -31
- package/src/components/LeftNav/LeftNav.perf.test.tsx +0 -172
- package/src/components/LeftNav/LeftNav.test.tsx +0 -302
- package/src/components/LeftNav/LeftNav.tsx +0 -330
- package/src/components/LeftNav/leftNav.types.ts +0 -120
- package/src/components/LeftNav/leftNav.variants.ts +0 -107
- package/src/components/Menu/Menu.test.tsx +0 -408
- package/src/components/Menu/Menu.tsx +0 -335
- package/src/components/Menu/menu.types.ts +0 -221
- package/src/components/Menu/menu.variants.ts +0 -132
- package/src/components/NumberField/NumberField.test.tsx +0 -83
- package/src/components/NumberField/NumberField.tsx +0 -321
- package/src/components/NumberField/numberField.types.ts +0 -72
- package/src/components/NumberField/numberField.variants.ts +0 -94
- package/src/components/OTPField/OTPField.test.tsx +0 -121
- package/src/components/OTPField/OTPField.tsx +0 -250
- package/src/components/OTPField/otpField.types.ts +0 -58
- package/src/components/OTPField/otpField.variants.ts +0 -72
- package/src/components/Pagination/Pagination.test.tsx +0 -314
- package/src/components/Pagination/Pagination.tsx +0 -277
- package/src/components/Pagination/pagination.helpers.ts +0 -102
- package/src/components/Pagination/pagination.types.ts +0 -117
- package/src/components/Pagination/pagination.variants.ts +0 -117
- package/src/components/Popover/Popover.test.tsx +0 -72
- package/src/components/Popover/Popover.tsx +0 -55
- package/src/components/Popover/popover.types.ts +0 -42
- package/src/components/Popover/popover.variants.ts +0 -24
- package/src/components/RadioGroup/RadioGroup.test.tsx +0 -140
- package/src/components/RadioGroup/RadioGroup.tsx +0 -283
- package/src/components/RadioGroup/radioGroup.types.ts +0 -85
- package/src/components/RadioGroup/radioGroup.variants.ts +0 -87
- package/src/components/Skeleton/Skeleton.test.tsx +0 -127
- package/src/components/Skeleton/Skeleton.tsx +0 -49
- package/src/components/Skeleton/skeleton.types.ts +0 -59
- package/src/components/Skeleton/skeleton.variants.ts +0 -87
- package/src/components/Snackbar/Snackbar.perf.test.tsx +0 -144
- package/src/components/Snackbar/Snackbar.test.tsx +0 -327
- package/src/components/Snackbar/Snackbar.tsx +0 -316
- package/src/components/Snackbar/snackbar.types.ts +0 -127
- package/src/components/Snackbar/snackbar.variants.ts +0 -81
- package/src/components/Spinner/Spinner.test.tsx +0 -199
- package/src/components/Spinner/Spinner.tsx +0 -158
- package/src/components/Spinner/spinner.types.ts +0 -113
- package/src/components/Spinner/spinner.variants.ts +0 -83
- package/src/components/Stack/Stack.test.tsx +0 -309
- package/src/components/Stack/Stack.tsx +0 -115
- package/src/components/Stack/stack.types.ts +0 -68
- package/src/components/Stack/stack.variants.ts +0 -80
- package/src/components/Switch/Switch.test.tsx +0 -131
- package/src/components/Switch/Switch.tsx +0 -158
- package/src/components/Switch/switch.types.ts +0 -36
- package/src/components/Switch/switch.variants.ts +0 -70
- package/src/components/Table/Table.perf.test.tsx +0 -81
- package/src/components/Table/Table.test.tsx +0 -674
- package/src/components/Table/Table.tsx +0 -863
- package/src/components/Table/cells/RenderButton.tsx +0 -23
- package/src/components/Table/cells/RenderChip.tsx +0 -37
- package/src/components/Table/cells/RenderText.tsx +0 -37
- package/src/components/Table/cells/RenderTwoLine.tsx +0 -34
- package/src/components/Table/cells/RowActionsMenu.tsx +0 -147
- package/src/components/Table/cells/cells.test.tsx +0 -247
- package/src/components/Table/cells/index.ts +0 -14
- package/src/components/Table/table.types.ts +0 -438
- package/src/components/Table/table.variants.ts +0 -207
- package/src/components/Tabs/Tabs.test.tsx +0 -145
- package/src/components/Tabs/Tabs.tsx +0 -77
- package/src/components/Tabs/tabs.types.ts +0 -55
- package/src/components/Tabs/tabs.variants.ts +0 -69
- package/src/components/Tabs/useTabs.ts +0 -167
- package/src/components/TextField/TextField.test.tsx +0 -198
- package/src/components/TextField/TextField.tsx +0 -245
- package/src/components/TextField/textField.types.ts +0 -90
- package/src/components/TextField/textField.variants.ts +0 -105
- package/src/components/Textarea/Textarea.test.tsx +0 -75
- package/src/components/Textarea/Textarea.tsx +0 -190
- package/src/components/Textarea/textarea.types.ts +0 -60
- package/src/components/Textarea/textarea.variants.ts +0 -85
- package/src/components/TimePicker/TimePicker.test.tsx +0 -65
- package/src/components/TimePicker/TimePicker.tsx +0 -274
- package/src/components/TimePicker/timePicker.types.ts +0 -69
- package/src/components/TimePicker/timePicker.variants.ts +0 -53
- package/src/components/Tooltip/Tooltip.test.tsx +0 -73
- package/src/components/Tooltip/Tooltip.tsx +0 -70
- package/src/components/Tooltip/tooltip.types.ts +0 -44
- package/src/components/Tooltip/tooltip.variants.ts +0 -28
- package/src/components/TopBar/TopBar.test.tsx +0 -129
- package/src/components/TopBar/TopBar.tsx +0 -66
- package/src/components/TopBar/topBar.types.ts +0 -42
- package/src/components/TopBar/topBar.variants.ts +0 -38
- package/src/components/Typography/Typography.test.tsx +0 -247
- package/src/components/Typography/Typography.tsx +0 -112
- package/src/components/Typography/typography.types.ts +0 -65
- package/src/components/Typography/typography.variants.ts +0 -130
- package/src/components/VisuallyHidden/VisuallyHidden.test.tsx +0 -86
- package/src/components/VisuallyHidden/VisuallyHidden.tsx +0 -59
- package/src/components/VisuallyHidden/visuallyHidden.types.ts +0 -45
- package/src/components/_shared/data/getNestedValue.test.ts +0 -50
- package/src/components/_shared/data/getNestedValue.ts +0 -32
- package/src/components/_shared/data/useColumnAutoDetect.test.ts +0 -123
- package/src/components/_shared/data/useColumnAutoDetect.ts +0 -100
- package/src/components/_shared/data/useControllableState.ts +0 -48
- package/src/components/_shared/data/useDebouncedValue.ts +0 -24
- package/src/components/_shared/data/useTableFilter.test.ts +0 -166
- package/src/components/_shared/data/useTableFilter.ts +0 -118
- package/src/components/_shared/data/useTableSearch.test.ts +0 -115
- package/src/components/_shared/data/useTableSearch.ts +0 -66
- package/src/components/_shared/data/useTableSelection.test.ts +0 -100
- package/src/components/_shared/data/useTableSelection.ts +0 -100
- package/src/components/_shared/data/useTableSort.test.ts +0 -150
- package/src/components/_shared/data/useTableSort.ts +0 -141
- package/src/components/_shared/data/useVirtualizer.test.ts +0 -158
- package/src/components/_shared/data/useVirtualizer.ts +0 -199
- package/src/components/_shared/resolveValidationState.ts +0 -60
- package/src/components/_shared/severity/index.ts +0 -33
- package/src/components/_shared/severity/severity.types.ts +0 -50
- package/src/components/_shared/severity/severityIcons.tsx +0 -104
- package/src/components/_shared/severity/severityVariants.test.ts +0 -136
- package/src/components/_shared/severity/severityVariants.ts +0 -115
- package/src/components/_shared/themeIdentity.test.ts +0 -113
- package/src/hooks/useAccessState.spec.tsx +0 -161
- package/src/hooks/useAccessState.ts +0 -81
- package/src/index.ts +0 -550
- package/src/utils/cn.spec.ts +0 -16
- package/src/utils/cn.ts +0 -29
- package/test-output/vitest/coverage/base.css +0 -224
- package/test-output/vitest/coverage/block-navigation.js +0 -87
- package/test-output/vitest/coverage/clover.xml +0 -2418
- package/test-output/vitest/coverage/coverage-final.json +0 -89
- package/test-output/vitest/coverage/favicon.png +0 -0
- package/test-output/vitest/coverage/index.html +0 -746
- package/test-output/vitest/coverage/prettify.css +0 -1
- package/test-output/vitest/coverage/prettify.js +0 -2
- package/test-output/vitest/coverage/sort-arrow-sprite.png +0 -0
- package/test-output/vitest/coverage/sorter.js +0 -210
- package/test-output/vitest/coverage/src/components/Accordion/Accordion.tsx.html +0 -376
- package/test-output/vitest/coverage/src/components/Accordion/accordion.variants.ts.html +0 -181
- package/test-output/vitest/coverage/src/components/Accordion/index.html +0 -131
- package/test-output/vitest/coverage/src/components/AppShell/AppShell.tsx.html +0 -802
- package/test-output/vitest/coverage/src/components/AppShell/appShell.variants.ts.html +0 -247
- package/test-output/vitest/coverage/src/components/AppShell/index.html +0 -131
- package/test-output/vitest/coverage/src/components/AspectRatio/AspectRatio.tsx.html +0 -304
- package/test-output/vitest/coverage/src/components/AspectRatio/index.html +0 -116
- package/test-output/vitest/coverage/src/components/Autocomplete/Autocomplete.tsx.html +0 -3292
- package/test-output/vitest/coverage/src/components/Autocomplete/autocomplete.variants.ts.html +0 -508
- package/test-output/vitest/coverage/src/components/Autocomplete/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Box/Box.tsx.html +0 -334
- package/test-output/vitest/coverage/src/components/Box/box.variants.ts.html +0 -775
- package/test-output/vitest/coverage/src/components/Box/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Breadcrumbs/Breadcrumbs.tsx.html +0 -694
- package/test-output/vitest/coverage/src/components/Breadcrumbs/breadcrumbs.variants.ts.html +0 -247
- package/test-output/vitest/coverage/src/components/Breadcrumbs/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Button/Button.tsx.html +0 -535
- package/test-output/vitest/coverage/src/components/Button/button.variants.ts.html +0 -562
- package/test-output/vitest/coverage/src/components/Button/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Checkbox/Checkbox.tsx.html +0 -943
- package/test-output/vitest/coverage/src/components/Checkbox/checkbox.variants.ts.html +0 -289
- package/test-output/vitest/coverage/src/components/Checkbox/index.html +0 -131
- package/test-output/vitest/coverage/src/components/ConfirmDialog/ConfirmDialog.tsx.html +0 -841
- package/test-output/vitest/coverage/src/components/ConfirmDialog/confirmDialog.variants.ts.html +0 -313
- package/test-output/vitest/coverage/src/components/ConfirmDialog/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Container/Container.tsx.html +0 -307
- package/test-output/vitest/coverage/src/components/Container/container.variants.ts.html +0 -328
- package/test-output/vitest/coverage/src/components/Container/index.html +0 -131
- package/test-output/vitest/coverage/src/components/DataGrid/DataGrid.tsx.html +0 -3937
- package/test-output/vitest/coverage/src/components/DataGrid/dataGrid.variants.ts.html +0 -541
- package/test-output/vitest/coverage/src/components/DataGrid/filters/ColumnFilters.tsx.html +0 -1453
- package/test-output/vitest/coverage/src/components/DataGrid/filters/index.html +0 -116
- package/test-output/vitest/coverage/src/components/DataGrid/index.html +0 -131
- package/test-output/vitest/coverage/src/components/DataGrid/reorder/index.html +0 -116
- package/test-output/vitest/coverage/src/components/DataGrid/reorder/useColumnReorder.ts.html +0 -517
- package/test-output/vitest/coverage/src/components/DataGrid/resize/index.html +0 -116
- package/test-output/vitest/coverage/src/components/DataGrid/resize/useColumnResize.ts.html +0 -490
- package/test-output/vitest/coverage/src/components/DataGrid/visibility/ColumnVisibilityMenu.tsx.html +0 -592
- package/test-output/vitest/coverage/src/components/DataGrid/visibility/index.html +0 -116
- package/test-output/vitest/coverage/src/components/DateTimePicker/DateTimePicker.tsx.html +0 -1042
- package/test-output/vitest/coverage/src/components/DateTimePicker/dateTimePicker.variants.ts.html +0 -337
- package/test-output/vitest/coverage/src/components/DateTimePicker/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Dialog/Dialog.tsx.html +0 -391
- package/test-output/vitest/coverage/src/components/Dialog/dialog.variants.ts.html +0 -283
- package/test-output/vitest/coverage/src/components/Dialog/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Divider/Divider.tsx.html +0 -505
- package/test-output/vitest/coverage/src/components/Divider/divider.variants.ts.html +0 -433
- package/test-output/vitest/coverage/src/components/Divider/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Grid/Grid.tsx.html +0 -529
- package/test-output/vitest/coverage/src/components/Grid/grid.variants.ts.html +0 -484
- package/test-output/vitest/coverage/src/components/Grid/index.html +0 -131
- package/test-output/vitest/coverage/src/components/LeftNav/LeftNav.tsx.html +0 -1075
- package/test-output/vitest/coverage/src/components/LeftNav/index.html +0 -131
- package/test-output/vitest/coverage/src/components/LeftNav/leftNav.variants.ts.html +0 -406
- package/test-output/vitest/coverage/src/components/NumberField/NumberField.tsx.html +0 -1048
- package/test-output/vitest/coverage/src/components/NumberField/index.html +0 -131
- package/test-output/vitest/coverage/src/components/NumberField/numberField.variants.ts.html +0 -367
- package/test-output/vitest/coverage/src/components/OTPField/OTPField.tsx.html +0 -835
- package/test-output/vitest/coverage/src/components/OTPField/index.html +0 -131
- package/test-output/vitest/coverage/src/components/OTPField/otpField.variants.ts.html +0 -301
- package/test-output/vitest/coverage/src/components/Pagination/Pagination.tsx.html +0 -916
- package/test-output/vitest/coverage/src/components/Pagination/index.html +0 -146
- package/test-output/vitest/coverage/src/components/Pagination/pagination.helpers.ts.html +0 -391
- package/test-output/vitest/coverage/src/components/Pagination/pagination.variants.ts.html +0 -436
- package/test-output/vitest/coverage/src/components/Popover/Popover.tsx.html +0 -250
- package/test-output/vitest/coverage/src/components/Popover/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Popover/popover.variants.ts.html +0 -157
- package/test-output/vitest/coverage/src/components/RadioGroup/RadioGroup.tsx.html +0 -934
- package/test-output/vitest/coverage/src/components/RadioGroup/index.html +0 -131
- package/test-output/vitest/coverage/src/components/RadioGroup/radioGroup.variants.ts.html +0 -346
- package/test-output/vitest/coverage/src/components/Skeleton/Skeleton.tsx.html +0 -232
- package/test-output/vitest/coverage/src/components/Skeleton/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Skeleton/skeleton.variants.ts.html +0 -346
- package/test-output/vitest/coverage/src/components/Snackbar/Snackbar.tsx.html +0 -979
- package/test-output/vitest/coverage/src/components/Snackbar/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Snackbar/snackbar.variants.ts.html +0 -307
- package/test-output/vitest/coverage/src/components/Stack/Stack.tsx.html +0 -430
- package/test-output/vitest/coverage/src/components/Stack/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Stack/stack.variants.ts.html +0 -325
- package/test-output/vitest/coverage/src/components/Switch/Switch.tsx.html +0 -559
- package/test-output/vitest/coverage/src/components/Switch/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Switch/switch.variants.ts.html +0 -295
- package/test-output/vitest/coverage/src/components/Table/Table.tsx.html +0 -2677
- package/test-output/vitest/coverage/src/components/Table/cells/RenderButton.tsx.html +0 -154
- package/test-output/vitest/coverage/src/components/Table/cells/RenderChip.tsx.html +0 -385
- package/test-output/vitest/coverage/src/components/Table/cells/RenderText.tsx.html +0 -196
- package/test-output/vitest/coverage/src/components/Table/cells/RenderTwoLine.tsx.html +0 -187
- package/test-output/vitest/coverage/src/components/Table/cells/RowActionsMenu.tsx.html +0 -526
- package/test-output/vitest/coverage/src/components/Table/cells/index.html +0 -191
- package/test-output/vitest/coverage/src/components/Table/cells/index.ts.html +0 -127
- package/test-output/vitest/coverage/src/components/Table/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Table/table.variants.ts.html +0 -706
- package/test-output/vitest/coverage/src/components/Tabs/Tabs.tsx.html +0 -283
- package/test-output/vitest/coverage/src/components/Tabs/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Tabs/tabs.variants.ts.html +0 -292
- package/test-output/vitest/coverage/src/components/TextField/TextField.tsx.html +0 -820
- package/test-output/vitest/coverage/src/components/TextField/index.html +0 -131
- package/test-output/vitest/coverage/src/components/TextField/textField.variants.ts.html +0 -400
- package/test-output/vitest/coverage/src/components/Textarea/Textarea.tsx.html +0 -655
- package/test-output/vitest/coverage/src/components/Textarea/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Textarea/textarea.variants.ts.html +0 -340
- package/test-output/vitest/coverage/src/components/Tooltip/Tooltip.tsx.html +0 -295
- package/test-output/vitest/coverage/src/components/Tooltip/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Tooltip/tooltip.variants.ts.html +0 -169
- package/test-output/vitest/coverage/src/components/TopBar/TopBar.tsx.html +0 -283
- package/test-output/vitest/coverage/src/components/TopBar/index.html +0 -131
- package/test-output/vitest/coverage/src/components/TopBar/topBar.variants.ts.html +0 -199
- package/test-output/vitest/coverage/src/components/Typography/Typography.tsx.html +0 -421
- package/test-output/vitest/coverage/src/components/Typography/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Typography/typography.variants.ts.html +0 -436
- package/test-output/vitest/coverage/src/components/VisuallyHidden/VisuallyHidden.tsx.html +0 -262
- package/test-output/vitest/coverage/src/components/VisuallyHidden/index.html +0 -116
- package/test-output/vitest/coverage/src/components/_shared/data/getNestedValue.ts.html +0 -181
- package/test-output/vitest/coverage/src/components/_shared/data/index.html +0 -236
- package/test-output/vitest/coverage/src/components/_shared/data/useColumnAutoDetect.ts.html +0 -385
- package/test-output/vitest/coverage/src/components/_shared/data/useControllableState.ts.html +0 -229
- package/test-output/vitest/coverage/src/components/_shared/data/useDebouncedValue.ts.html +0 -157
- package/test-output/vitest/coverage/src/components/_shared/data/useTableFilter.ts.html +0 -439
- package/test-output/vitest/coverage/src/components/_shared/data/useTableSearch.ts.html +0 -283
- package/test-output/vitest/coverage/src/components/_shared/data/useTableSelection.ts.html +0 -385
- package/test-output/vitest/coverage/src/components/_shared/data/useTableSort.ts.html +0 -508
- package/test-output/vitest/coverage/src/components/_shared/data/useVirtualizer.ts.html +0 -682
- package/test-output/vitest/coverage/src/components/_shared/index.html +0 -116
- package/test-output/vitest/coverage/src/components/_shared/resolveValidationState.ts.html +0 -265
- package/test-output/vitest/coverage/src/hooks/index.html +0 -116
- package/test-output/vitest/coverage/src/hooks/useAccessState.ts.html +0 -328
- package/test-output/vitest/coverage/src/index.html +0 -116
- package/test-output/vitest/coverage/src/index.ts.html +0 -1153
- package/test-output/vitest/coverage/src/utils/cn.ts.html +0 -172
- package/test-output/vitest/coverage/src/utils/index.html +0 -116
- package/tsconfig.json +0 -13
- package/tsconfig.lib.json +0 -58
- package/tsconfig.spec.json +0 -34
- package/vite.config.ts +0 -33
- package/vitest.config.mts +0 -29
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
3
|
-
import { render, fireEvent } from '@testing-library/react';
|
|
4
|
-
import { RbacProvider } from '@dashforge/rbac';
|
|
5
|
-
import type { RbacPolicy, Subject } from '@dashforge/rbac';
|
|
6
|
-
import { DashFormProvider } from '@dashforge/forms';
|
|
7
|
-
import { TextField } from './TextField.js';
|
|
8
|
-
|
|
9
|
-
const READ_ONLY: RbacPolicy = {
|
|
10
|
-
roles: [
|
|
11
|
-
{
|
|
12
|
-
name: 'viewer',
|
|
13
|
-
permissions: [{ resource: 'doc', action: 'update', effect: 'deny' }],
|
|
14
|
-
},
|
|
15
|
-
],
|
|
16
|
-
};
|
|
17
|
-
const viewer: Subject = { id: 'v', roles: ['viewer'] };
|
|
18
|
-
|
|
19
|
-
function withForm(children: React.ReactNode, defaultValues: Record<string, unknown> = {}) {
|
|
20
|
-
return <DashFormProvider defaultValues={defaultValues}>{children}</DashFormProvider>;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
describe('TextField — standalone', () => {
|
|
24
|
-
it('renders an <input type="text"> by default', () => {
|
|
25
|
-
const { getByRole } = render(<TextField name="email" label="Email" />);
|
|
26
|
-
const input = getByRole('textbox') as HTMLInputElement;
|
|
27
|
-
expect(input.type).toBe('text');
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('label is wired via htmlFor to the input id', () => {
|
|
31
|
-
const { getByText, getByRole } = render(<TextField name="x" label="My label" />);
|
|
32
|
-
const label = getByText('My label').closest('label') as HTMLLabelElement;
|
|
33
|
-
expect(label.htmlFor).toBe((getByRole('textbox') as HTMLInputElement).id);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('renders the required asterisk and sets the native required attr', () => {
|
|
37
|
-
const { getByText, getByRole } = render(
|
|
38
|
-
<TextField name="x" label="X" required />
|
|
39
|
-
);
|
|
40
|
-
expect(getByText('*')).toBeTruthy();
|
|
41
|
-
expect((getByRole('textbox') as HTMLInputElement).required).toBe(true);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('forwards type="password" / "email" / etc.', () => {
|
|
45
|
-
const { getByLabelText } = render(<TextField name="p" label="P" type="password" />);
|
|
46
|
-
expect((getByLabelText('P') as HTMLInputElement).type).toBe('password');
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('renders helperText below the input', () => {
|
|
50
|
-
const { getByText } = render(<TextField name="x" label="X" helperText="Hint" />);
|
|
51
|
-
expect(getByText('Hint')).toBeTruthy();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('aria-describedby links to the helper text id when present', () => {
|
|
55
|
-
const { getByRole, getByText } = render(
|
|
56
|
-
<TextField name="x" label="X" helperText="Hint" />
|
|
57
|
-
);
|
|
58
|
-
const input = getByRole('textbox') as HTMLInputElement;
|
|
59
|
-
const helperId = input.getAttribute('aria-describedby');
|
|
60
|
-
expect(helperId).toBeTruthy();
|
|
61
|
-
expect(getByText('Hint').id).toBe(helperId);
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
describe('TextField — variants', () => {
|
|
66
|
-
it('size lg uses h-12 + text-lg', () => {
|
|
67
|
-
const { container } = render(<TextField name="x" label="X" size="lg" />);
|
|
68
|
-
const wrap = container.querySelector('div[class*="rounded-md"]');
|
|
69
|
-
expect(wrap?.className).toContain('h-12');
|
|
70
|
-
expect(container.querySelector('input')?.className).toContain('text-lg');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('error=true applies danger ring + uses errorText slot for helperText', () => {
|
|
74
|
-
const { container, getByText } = render(
|
|
75
|
-
<TextField name="x" label="X" error helperText="Bad" />
|
|
76
|
-
);
|
|
77
|
-
expect(container.querySelector('div[class*="rounded-md"]')?.className).toContain(
|
|
78
|
-
'border-danger-500'
|
|
79
|
-
);
|
|
80
|
-
expect(getByText('Bad').className).toContain('text-danger-600');
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('aria-invalid is set when error=true', () => {
|
|
84
|
-
const { getByRole } = render(<TextField name="x" label="X" error />);
|
|
85
|
-
expect(getByRole('textbox').getAttribute('aria-invalid')).toBe('true');
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('fullWidth adds w-full to root + wrapper', () => {
|
|
89
|
-
const { container } = render(<TextField name="x" label="X" fullWidth />);
|
|
90
|
-
expect((container.firstChild as HTMLElement).className).toContain('w-full');
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('layout=inline puts label and input side by side', () => {
|
|
94
|
-
const { container } = render(<TextField name="x" label="X" layout="inline" />);
|
|
95
|
-
expect((container.firstChild as HTMLElement).className).toContain('flex-row');
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
describe('TextField — override (sx + slotProps)', () => {
|
|
100
|
-
it('sx is applied to root', () => {
|
|
101
|
-
const { container } = render(<TextField name="x" label="X" sx="md:w-1/2" />);
|
|
102
|
-
expect((container.firstChild as HTMLElement).className).toContain('md:w-1/2');
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('slotProps.input applies to the <input>', () => {
|
|
106
|
-
const { getByRole } = render(
|
|
107
|
-
<TextField name="x" label="X" slotProps={{ input: { className: 'font-mono' } }} />
|
|
108
|
-
);
|
|
109
|
-
expect(getByRole('textbox').className).toContain('font-mono');
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('slotProps.label applies to the <label>', () => {
|
|
113
|
-
const { getByText } = render(
|
|
114
|
-
<TextField name="x" label="My" slotProps={{ label: { className: 'uppercase' } }} />
|
|
115
|
-
);
|
|
116
|
-
const label = getByText('My').closest('label');
|
|
117
|
-
expect(label?.className).toContain('uppercase');
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
describe('TextField — RBAC', () => {
|
|
122
|
-
it('hides with onUnauthorized=hide', () => {
|
|
123
|
-
const { queryByRole } = render(
|
|
124
|
-
<RbacProvider policy={READ_ONLY} subject={viewer}>
|
|
125
|
-
<TextField
|
|
126
|
-
name="x"
|
|
127
|
-
label="X"
|
|
128
|
-
access={{ resource: 'doc', action: 'update', onUnauthorized: 'hide' }}
|
|
129
|
-
/>
|
|
130
|
-
</RbacProvider>
|
|
131
|
-
);
|
|
132
|
-
expect(queryByRole('textbox')).toBeNull();
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it('disables with onUnauthorized=disable', () => {
|
|
136
|
-
const { getByRole } = render(
|
|
137
|
-
<RbacProvider policy={READ_ONLY} subject={viewer}>
|
|
138
|
-
<TextField
|
|
139
|
-
name="x"
|
|
140
|
-
label="X"
|
|
141
|
-
access={{ resource: 'doc', action: 'update', onUnauthorized: 'disable' }}
|
|
142
|
-
/>
|
|
143
|
-
</RbacProvider>
|
|
144
|
-
);
|
|
145
|
-
expect((getByRole('textbox') as HTMLInputElement).disabled).toBe(true);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it('becomes readonly with onUnauthorized=readonly', () => {
|
|
149
|
-
const { getByRole } = render(
|
|
150
|
-
<RbacProvider policy={READ_ONLY} subject={viewer}>
|
|
151
|
-
<TextField
|
|
152
|
-
name="x"
|
|
153
|
-
label="X"
|
|
154
|
-
access={{ resource: 'doc', action: 'update', onUnauthorized: 'readonly' }}
|
|
155
|
-
/>
|
|
156
|
-
</RbacProvider>
|
|
157
|
-
);
|
|
158
|
-
expect((getByRole('textbox') as HTMLInputElement).readOnly).toBe(true);
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
describe('TextField — DashFormProvider integration', () => {
|
|
163
|
-
it('renders inside DashFormProvider', () => {
|
|
164
|
-
const { getByRole } = render(withForm(<TextField name="email" label="Email" />));
|
|
165
|
-
expect(getByRole('textbox')).toBeTruthy();
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it('updates the input value as the user types', () => {
|
|
169
|
-
const { getByRole } = render(withForm(<TextField name="email" label="Email" />));
|
|
170
|
-
const input = getByRole('textbox') as HTMLInputElement;
|
|
171
|
-
fireEvent.change(input, { target: { value: 'hi@example.com' } });
|
|
172
|
-
expect(input.value).toBe('hi@example.com');
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
// Skipped pending decision on the bridge ↔ controlled-value contract
|
|
176
|
-
// (the current implementation lets the bridge value win when both are
|
|
177
|
-
// present; the test as written asserts the opposite). Behavior to
|
|
178
|
-
// resolve in 1.0.1 — see TextField.tsx line ~203.
|
|
179
|
-
it.skip('respects explicit `value` (controlled mode wins over bridge)', () => {
|
|
180
|
-
const onChange = vi.fn();
|
|
181
|
-
const { getByRole } = render(
|
|
182
|
-
withForm(
|
|
183
|
-
<TextField name="email" label="Email" value="forced" onChange={onChange} />,
|
|
184
|
-
{ email: '' }
|
|
185
|
-
)
|
|
186
|
-
);
|
|
187
|
-
expect((getByRole('textbox') as HTMLInputElement).value).toBe('forced');
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('fires user onChange in addition to the bridge update', () => {
|
|
191
|
-
const onChange = vi.fn();
|
|
192
|
-
const { getByRole } = render(
|
|
193
|
-
withForm(<TextField name="email" label="Email" onChange={onChange} />)
|
|
194
|
-
);
|
|
195
|
-
fireEvent.change(getByRole('textbox'), { target: { value: 'x' } });
|
|
196
|
-
expect(onChange).toHaveBeenCalled();
|
|
197
|
-
});
|
|
198
|
-
});
|
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
import { useCallback, useContext, useEffect, useId, useRef } from 'react';
|
|
2
|
-
import { DashFormContext, useEngineVisibility } from '@dashforge/ui-core';
|
|
3
|
-
import type { DashFormBridge, FieldRegistration } from '@dashforge/ui-core';
|
|
4
|
-
import { useDashFieldMeta } from '@dashforge/forms';
|
|
5
|
-
import { cn } from '../../utils/cn.js';
|
|
6
|
-
import { useAccessState } from '../../hooks/useAccessState.js';
|
|
7
|
-
import { resolveValidationState } from '../_shared/resolveValidationState.js';
|
|
8
|
-
import { textFieldVariants } from './textField.variants.js';
|
|
9
|
-
import type { TextFieldProps } from './textField.types.js';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Dashforge TW TextField — bridge-integrated text input.
|
|
13
|
-
*
|
|
14
|
-
* Implementation mirrors `@dashforge/ui/TextField` (MUI) at the bridge
|
|
15
|
-
* level — same `name` / `rules` / `visibleWhen` / `access` semantics,
|
|
16
|
-
* same validation gating, same StrictMode-safe unregister pattern.
|
|
17
|
-
* Renderer differences:
|
|
18
|
-
*
|
|
19
|
-
* - Native `<input>` element wrapped in a TV-styled
|
|
20
|
-
* `<div role-less wrapper>` so the focus ring extends to the entire
|
|
21
|
-
* field (consistent with shadcn-style inputs).
|
|
22
|
-
* - Layout modes: `stacked` (default — label above) and `inline`
|
|
23
|
-
* (label left of input). MUI's `floating` mode is deferred to a
|
|
24
|
-
* follow-up sprint; consumers needing the visual can layer it via
|
|
25
|
-
* `slotProps`.
|
|
26
|
-
* - No Select-integration hook (Select lands in F4 as its own component).
|
|
27
|
-
*
|
|
28
|
-
* **A11y**:
|
|
29
|
-
* - `aria-invalid` is set from the resolved validation state.
|
|
30
|
-
* - `aria-describedby` links the input to the helper/error text node.
|
|
31
|
-
* - Required fields get the native `required` attribute + a visual `*`.
|
|
32
|
-
*/
|
|
33
|
-
export function TextField(props: TextFieldProps) {
|
|
34
|
-
const {
|
|
35
|
-
name,
|
|
36
|
-
rules,
|
|
37
|
-
visibleWhen,
|
|
38
|
-
layout = 'stacked',
|
|
39
|
-
size,
|
|
40
|
-
label,
|
|
41
|
-
helperText,
|
|
42
|
-
required,
|
|
43
|
-
error,
|
|
44
|
-
disabled,
|
|
45
|
-
fullWidth,
|
|
46
|
-
access,
|
|
47
|
-
sx,
|
|
48
|
-
slotProps,
|
|
49
|
-
placeholder,
|
|
50
|
-
type,
|
|
51
|
-
onChange: userOnChange,
|
|
52
|
-
onBlur: userOnBlur,
|
|
53
|
-
value: userValue,
|
|
54
|
-
defaultValue,
|
|
55
|
-
...rest
|
|
56
|
-
} = props;
|
|
57
|
-
|
|
58
|
-
// ───── Hooks (unconditional) ─────
|
|
59
|
-
const bridge = useContext(DashFormContext) as DashFormBridge | null;
|
|
60
|
-
const isVisible = useEngineVisibility(bridge?.engine, visibleWhen);
|
|
61
|
-
// Granular per-field subscription. We READ from `fieldMeta.value`
|
|
62
|
-
// (reactive snapshot) rather than `bridge.getValue(name)` (eager
|
|
63
|
-
// read with no subscription) so React re-renders this field on its
|
|
64
|
-
// own state mutations — the canonical pattern, mirrors the MUI side.
|
|
65
|
-
const fieldMeta = useDashFieldMeta(name);
|
|
66
|
-
const accessState = useAccessState(access);
|
|
67
|
-
|
|
68
|
-
const inputId = useId();
|
|
69
|
-
const helperId = `${inputId}-help`;
|
|
70
|
-
|
|
71
|
-
// StrictMode-safe unregister-on-unmount
|
|
72
|
-
const unregisterRef = useRef({ bridge, name });
|
|
73
|
-
unregisterRef.current = { bridge, name };
|
|
74
|
-
const isMountedRef = useRef(false);
|
|
75
|
-
|
|
76
|
-
useEffect(() => {
|
|
77
|
-
isMountedRef.current = true;
|
|
78
|
-
return () => {
|
|
79
|
-
isMountedRef.current = false;
|
|
80
|
-
const { bridge: cap, name: capName } = unregisterRef.current;
|
|
81
|
-
queueMicrotask(() => {
|
|
82
|
-
if (!isMountedRef.current) cap?.unregister?.(capName);
|
|
83
|
-
});
|
|
84
|
-
};
|
|
85
|
-
}, []);
|
|
86
|
-
|
|
87
|
-
// ───── Derived (every hook MUST be called above the early returns) ─────
|
|
88
|
-
const effectiveDisabled =
|
|
89
|
-
Boolean(disabled) || accessState.disabled;
|
|
90
|
-
const effectiveReadOnly = accessState.readonly;
|
|
91
|
-
|
|
92
|
-
const isFormMode = Boolean(bridge?.register);
|
|
93
|
-
|
|
94
|
-
let resolvedError = error;
|
|
95
|
-
let resolvedHelperText: typeof helperText = helperText;
|
|
96
|
-
let registration: FieldRegistration | null = null;
|
|
97
|
-
let resolvedValue: string | number | readonly string[] | undefined = userValue as
|
|
98
|
-
| string
|
|
99
|
-
| number
|
|
100
|
-
| readonly string[]
|
|
101
|
-
| undefined;
|
|
102
|
-
|
|
103
|
-
if (isFormMode && bridge) {
|
|
104
|
-
registration = bridge.register(name, rules);
|
|
105
|
-
const validation = resolveValidationState(name, bridge, error, helperText);
|
|
106
|
-
resolvedError = validation.error;
|
|
107
|
-
resolvedHelperText = validation.helperText;
|
|
108
|
-
if (userValue === undefined) {
|
|
109
|
-
// Read the reactive snapshot from `useDashFieldMeta` — NOT
|
|
110
|
-
// `bridge.getValue` — so the component re-renders on every
|
|
111
|
-
// per-field state change without the unsubscribe trick the
|
|
112
|
-
// legacy bridge used.
|
|
113
|
-
const bv = fieldMeta.value;
|
|
114
|
-
resolvedValue =
|
|
115
|
-
bv == null ? '' : (bv as string | number);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
120
|
-
if (isFormMode && bridge) {
|
|
121
|
-
bridge.setValue?.(name, e.target.value);
|
|
122
|
-
void registration?.onChange?.(e);
|
|
123
|
-
}
|
|
124
|
-
userOnChange?.(e);
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
|
|
128
|
-
if (isFormMode && bridge) {
|
|
129
|
-
registration?.onBlur?.(e);
|
|
130
|
-
}
|
|
131
|
-
userOnBlur?.(e);
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
// In form mode, RHF supplies its own callback ref via registration.
|
|
135
|
-
// Wrap it in a stable callback so React 19 unambiguously treats it as
|
|
136
|
-
// a callback ref (and to give us a single place to extend the ref
|
|
137
|
-
// chain in the future — e.g. for a user-supplied `ref` prop via
|
|
138
|
-
// `forwardRef`).
|
|
139
|
-
const registrationRefFn = registration?.ref;
|
|
140
|
-
const inputRef = useCallback(
|
|
141
|
-
(instance: HTMLInputElement | null) => {
|
|
142
|
-
if (typeof registrationRefFn === 'function') {
|
|
143
|
-
registrationRefFn(instance);
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
[registrationRefFn]
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
// ───── Render-time guards (after all hooks) ─────
|
|
150
|
-
if (!isVisible) return null;
|
|
151
|
-
if (!accessState.visible) return null;
|
|
152
|
-
|
|
153
|
-
const v = textFieldVariants({
|
|
154
|
-
size,
|
|
155
|
-
layout,
|
|
156
|
-
error: resolvedError,
|
|
157
|
-
fullWidth,
|
|
158
|
-
disabled: effectiveDisabled,
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
return (
|
|
162
|
-
<div className={cn(v.root(), sx, slotProps?.root?.className)}>
|
|
163
|
-
{label && (
|
|
164
|
-
<label
|
|
165
|
-
htmlFor={inputId}
|
|
166
|
-
className={cn(v.label(), slotProps?.label?.className)}
|
|
167
|
-
>
|
|
168
|
-
{label}
|
|
169
|
-
{required && (
|
|
170
|
-
<span
|
|
171
|
-
aria-hidden="true"
|
|
172
|
-
className={cn(v.requiredMark(), slotProps?.requiredMark?.className)}
|
|
173
|
-
>
|
|
174
|
-
*
|
|
175
|
-
</span>
|
|
176
|
-
)}
|
|
177
|
-
</label>
|
|
178
|
-
)}
|
|
179
|
-
|
|
180
|
-
<div className={cn(v.inputWrapper(), slotProps?.inputWrapper?.className)}>
|
|
181
|
-
{/*
|
|
182
|
-
* Prefix slot (Sprint 2 P4.1) — inline adornment rendered
|
|
183
|
-
* BEFORE the input. Mounts only when `slotProps.prefix.children`
|
|
184
|
-
* is provided so empty configs don't add layout cost. Common
|
|
185
|
-
* use: currency symbols (`$`, `€`), units (`@`, `#`), status
|
|
186
|
-
* icons. The slot wrapper carries `pointer-events-none` to
|
|
187
|
-
* avoid stealing focus from the input on click.
|
|
188
|
-
*/}
|
|
189
|
-
{slotProps?.prefix?.children !== undefined && (
|
|
190
|
-
<span
|
|
191
|
-
aria-hidden="true"
|
|
192
|
-
className={cn(v.prefix(), slotProps.prefix.className)}
|
|
193
|
-
>
|
|
194
|
-
{slotProps.prefix.children}
|
|
195
|
-
</span>
|
|
196
|
-
)}
|
|
197
|
-
<input
|
|
198
|
-
{...rest}
|
|
199
|
-
id={inputId}
|
|
200
|
-
name={name}
|
|
201
|
-
type={type ?? 'text'}
|
|
202
|
-
placeholder={placeholder}
|
|
203
|
-
value={isFormMode ? (resolvedValue as string | number | readonly string[]) ?? '' : userValue}
|
|
204
|
-
defaultValue={!isFormMode && userValue === undefined ? defaultValue : undefined}
|
|
205
|
-
disabled={effectiveDisabled}
|
|
206
|
-
readOnly={effectiveReadOnly}
|
|
207
|
-
required={required}
|
|
208
|
-
aria-invalid={resolvedError || undefined}
|
|
209
|
-
aria-describedby={resolvedHelperText ? helperId : undefined}
|
|
210
|
-
onChange={handleChange}
|
|
211
|
-
onBlur={handleBlur}
|
|
212
|
-
ref={inputRef}
|
|
213
|
-
className={cn(v.input(), slotProps?.input?.className)}
|
|
214
|
-
/>
|
|
215
|
-
{/*
|
|
216
|
-
* Suffix slot (Sprint 2 P4.1) — same pattern as prefix,
|
|
217
|
-
* rendered AFTER the input. Typical use: unit labels (`USD`,
|
|
218
|
-
* `kg`, `%`), trailing icons, length counters.
|
|
219
|
-
*/}
|
|
220
|
-
{slotProps?.suffix?.children !== undefined && (
|
|
221
|
-
<span
|
|
222
|
-
aria-hidden="true"
|
|
223
|
-
className={cn(v.suffix(), slotProps.suffix.className)}
|
|
224
|
-
>
|
|
225
|
-
{slotProps.suffix.children}
|
|
226
|
-
</span>
|
|
227
|
-
)}
|
|
228
|
-
</div>
|
|
229
|
-
|
|
230
|
-
{resolvedHelperText && (
|
|
231
|
-
<p
|
|
232
|
-
id={helperId}
|
|
233
|
-
className={cn(
|
|
234
|
-
resolvedError ? v.errorText() : v.helperText(),
|
|
235
|
-
resolvedError
|
|
236
|
-
? slotProps?.errorText?.className
|
|
237
|
-
: slotProps?.helperText?.className
|
|
238
|
-
)}
|
|
239
|
-
>
|
|
240
|
-
{resolvedHelperText}
|
|
241
|
-
</p>
|
|
242
|
-
)}
|
|
243
|
-
</div>
|
|
244
|
-
);
|
|
245
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import type { InputHTMLAttributes, ReactNode } from 'react';
|
|
2
|
-
import type { AccessRequirement } from '@dashforge/rbac';
|
|
3
|
-
import type { Engine } from '@dashforge/ui-core';
|
|
4
|
-
import type { TextFieldVariants } from './textField.variants.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Per-slot overrides for `<TextField>`.
|
|
8
|
-
*
|
|
9
|
-
* Each slot accepts `{ className: string }` (and, for `prefix` /
|
|
10
|
-
* `suffix`, a `children` node) so the override path is extensible —
|
|
11
|
-
* extra fields like `style`, `aria-*`, `data-*` can be added without
|
|
12
|
-
* a breaking change. Mirrors the MUI-side `slotProps` shape.
|
|
13
|
-
*
|
|
14
|
-
* **`prefix` / `suffix`** are inline adornments rendered INSIDE the
|
|
15
|
-
* `inputWrapper`, before / after the `<input>` element. Typical use:
|
|
16
|
-
* currency symbols, units, status icons. Passing only `className`
|
|
17
|
-
* (no `children`) is allowed but renders an empty slot — usually
|
|
18
|
-
* you'll always pair the two.
|
|
19
|
-
*
|
|
20
|
-
* ```tsx
|
|
21
|
-
* <TextField
|
|
22
|
-
* name="price"
|
|
23
|
-
* type="number"
|
|
24
|
-
* slotProps={{
|
|
25
|
-
* prefix: { children: '$' },
|
|
26
|
-
* suffix: { children: 'USD' },
|
|
27
|
-
* }}
|
|
28
|
-
* />
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
export interface TextFieldSlotProps {
|
|
32
|
-
root?: { className?: string };
|
|
33
|
-
label?: { className?: string };
|
|
34
|
-
requiredMark?: { className?: string };
|
|
35
|
-
inputWrapper?: { className?: string };
|
|
36
|
-
input?: { className?: string };
|
|
37
|
-
helperText?: { className?: string };
|
|
38
|
-
errorText?: { className?: string };
|
|
39
|
-
prefix?: { children?: ReactNode; className?: string };
|
|
40
|
-
suffix?: { children?: ReactNode; className?: string };
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Props for the Dashforge TW `<TextField>`.
|
|
45
|
-
*
|
|
46
|
-
* Extends the native `<input>` attributes (so `placeholder`, `type`,
|
|
47
|
-
* `autoComplete`, `aria-*` etc. all flow through), narrowed in two
|
|
48
|
-
* places:
|
|
49
|
-
*
|
|
50
|
-
* - `size` is omitted because HTML's legacy `size` attribute (number)
|
|
51
|
-
* collides with our variant axis (`'sm' | 'md' | 'lg'`).
|
|
52
|
-
* - `className` is omitted — use `sx` instead (the canonical override
|
|
53
|
-
* path that wins over variant classes via `tailwind-merge`).
|
|
54
|
-
*
|
|
55
|
-
* Plus the variant axes and Dashforge form-bridge wiring (`name`,
|
|
56
|
-
* `rules`, `visibleWhen`, `access`).
|
|
57
|
-
*/
|
|
58
|
-
export interface TextFieldProps
|
|
59
|
-
extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'className'>,
|
|
60
|
-
Pick<TextFieldVariants, 'size' | 'layout' | 'fullWidth'> {
|
|
61
|
-
/** Bridge field name (required when used inside `DashFormProvider`). */
|
|
62
|
-
name: string;
|
|
63
|
-
|
|
64
|
-
/** Visible label. */
|
|
65
|
-
label?: ReactNode;
|
|
66
|
-
|
|
67
|
-
/** Forwarded to the bridge as RHF rules — opaque to this component. */
|
|
68
|
-
rules?: unknown;
|
|
69
|
-
|
|
70
|
-
/** Engine predicate. Component returns null when false. */
|
|
71
|
-
visibleWhen?: (engine: Engine) => boolean;
|
|
72
|
-
|
|
73
|
-
/** Helper line below the input. */
|
|
74
|
-
helperText?: ReactNode;
|
|
75
|
-
|
|
76
|
-
/** Explicit error semaphore. Overrides bridge auto-error. */
|
|
77
|
-
error?: boolean;
|
|
78
|
-
|
|
79
|
-
/** Render the asterisk + set the native `required` attribute. */
|
|
80
|
-
required?: boolean;
|
|
81
|
-
|
|
82
|
-
/** RBAC requirement. */
|
|
83
|
-
access?: AccessRequirement;
|
|
84
|
-
|
|
85
|
-
/** Root-level Tailwind override — wins over variant classes via `cn()`. */
|
|
86
|
-
sx?: string;
|
|
87
|
-
|
|
88
|
-
/** Per-slot overrides — see `TextFieldSlotProps`. */
|
|
89
|
-
slotProps?: TextFieldSlotProps;
|
|
90
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { tv, type VariantProps } from 'tailwind-variants';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Tailwind-variants recipe for `<TextField>` with 7 slots.
|
|
5
|
-
*
|
|
6
|
-
* The MUI-side `TextField` supports three layout modes (floating /
|
|
7
|
-
* stacked / inline) plus a Select-integration hook. The TW-side
|
|
8
|
-
* port covers **stacked** + **inline** in F3 (label outside the
|
|
9
|
-
* input wrapper) and ships a CSS-only floating label using
|
|
10
|
-
* Tailwind's `peer` modifier.
|
|
11
|
-
*
|
|
12
|
-
* The Select integration (`__selectAvailableValues` etc.) is
|
|
13
|
-
* deferred to F4 when the dedicated `<Select>` component arrives.
|
|
14
|
-
*/
|
|
15
|
-
export const textFieldVariants = tv({
|
|
16
|
-
slots: {
|
|
17
|
-
root: 'flex',
|
|
18
|
-
label: 'block text-sm font-medium text-neutral-900 mb-1',
|
|
19
|
-
requiredMark: 'text-danger-500 ml-0.5',
|
|
20
|
-
inputWrapper: [
|
|
21
|
-
'relative flex items-center',
|
|
22
|
-
// bg-neutral-50 inverts via @dashforge/tw-tokens: #fafafa in light,
|
|
23
|
-
// #0a0a0a in dark. Same class, dark-mode aware automatically.
|
|
24
|
-
'rounded-md border bg-neutral-50',
|
|
25
|
-
'border-neutral-300',
|
|
26
|
-
'transition-colors',
|
|
27
|
-
'focus-within:border-primary-500 focus-within:ring-2 focus-within:ring-primary-500/30',
|
|
28
|
-
],
|
|
29
|
-
input: [
|
|
30
|
-
'block w-full bg-transparent outline-none',
|
|
31
|
-
'text-neutral-900 placeholder:text-neutral-400',
|
|
32
|
-
'disabled:cursor-not-allowed disabled:opacity-60',
|
|
33
|
-
'read-only:cursor-default',
|
|
34
|
-
],
|
|
35
|
-
helperText: 'mt-1 text-sm text-neutral-600',
|
|
36
|
-
errorText: 'mt-1 text-sm text-danger-600',
|
|
37
|
-
/*
|
|
38
|
-
* `prefix` / `suffix` slot — inline adornment rendered before /
|
|
39
|
-
* after the input INSIDE the inputWrapper. `shrink-0` keeps the
|
|
40
|
-
* adornment width fixed so it doesn't compete for space with the
|
|
41
|
-
* input; `select-none` + `pointer-events-none` avoids
|
|
42
|
-
* accidentally stealing focus from the input on click (the
|
|
43
|
-
* inputWrapper handles focus via `focus-within`).
|
|
44
|
-
*/
|
|
45
|
-
prefix: [
|
|
46
|
-
'shrink-0 inline-flex items-center select-none pointer-events-none',
|
|
47
|
-
'text-neutral-500',
|
|
48
|
-
'mr-2',
|
|
49
|
-
],
|
|
50
|
-
suffix: [
|
|
51
|
-
'shrink-0 inline-flex items-center select-none pointer-events-none',
|
|
52
|
-
'text-neutral-500',
|
|
53
|
-
'ml-2',
|
|
54
|
-
],
|
|
55
|
-
},
|
|
56
|
-
variants: {
|
|
57
|
-
size: {
|
|
58
|
-
sm: {
|
|
59
|
-
inputWrapper: 'h-8 px-2',
|
|
60
|
-
input: 'text-sm',
|
|
61
|
-
},
|
|
62
|
-
md: {
|
|
63
|
-
inputWrapper: 'h-10 px-3',
|
|
64
|
-
input: 'text-base',
|
|
65
|
-
},
|
|
66
|
-
lg: {
|
|
67
|
-
inputWrapper: 'h-12 px-4',
|
|
68
|
-
input: 'text-lg',
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
layout: {
|
|
72
|
-
stacked: { root: 'flex-col' },
|
|
73
|
-
inline: { root: 'flex-row items-start gap-3', label: 'mb-0 pt-2 whitespace-nowrap shrink-0' },
|
|
74
|
-
},
|
|
75
|
-
error: {
|
|
76
|
-
true: {
|
|
77
|
-
inputWrapper:
|
|
78
|
-
'border-danger-500 focus-within:border-danger-500 focus-within:ring-danger-500/30',
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
fullWidth: {
|
|
82
|
-
true: {
|
|
83
|
-
root: 'w-full',
|
|
84
|
-
inputWrapper: 'w-full',
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
disabled: {
|
|
88
|
-
true: {
|
|
89
|
-
// neutral-200 is a darker shade of the default neutral-50 surface
|
|
90
|
-
// — visually distinguishes the disabled input from the active one
|
|
91
|
-
// in both light and dark modes.
|
|
92
|
-
inputWrapper: 'opacity-60 cursor-not-allowed bg-neutral-200',
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
defaultVariants: {
|
|
97
|
-
size: 'md',
|
|
98
|
-
layout: 'stacked',
|
|
99
|
-
error: false,
|
|
100
|
-
fullWidth: false,
|
|
101
|
-
disabled: false,
|
|
102
|
-
},
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
export type TextFieldVariants = VariantProps<typeof textFieldVariants>;
|