@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,115 +0,0 @@
|
|
|
1
|
-
import type { Severity, SeverityClasses, SeverityVariant } from './severity.types.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* The 3×4 severity color matrix.
|
|
5
|
-
*
|
|
6
|
-
* Every class is a Tailwind utility that resolves through
|
|
7
|
-
* `dashforgePreset()` to a CSS variable on the host — patchable at
|
|
8
|
-
* runtime via `patchTheme({ <severity>: { …scale } })` without
|
|
9
|
-
* recompiling. **Zero hardcoded hex values, zero `dark:` variants**:
|
|
10
|
-
* the dashforge preset auto-inverts the neutral surface ladder via CSS
|
|
11
|
-
* variable swap; the severity scales themselves do NOT auto-invert
|
|
12
|
-
* (they read the same scale in light + dark, which is the desired
|
|
13
|
-
* behaviour for semantic colors).
|
|
14
|
-
*
|
|
15
|
-
* The matrix is indexed `[variant][severity]` so a consumer-facing
|
|
16
|
-
* `<Alert variant="filled" severity="warning">` resolves with a single
|
|
17
|
-
* lookup.
|
|
18
|
-
*/
|
|
19
|
-
const SEVERITY_MATRIX: Record<SeverityVariant, Record<Severity, SeverityClasses>> = {
|
|
20
|
-
standard: {
|
|
21
|
-
info: {
|
|
22
|
-
surface: 'bg-info-50 text-info-900',
|
|
23
|
-
border: 'border-info-100',
|
|
24
|
-
icon: 'text-info-600',
|
|
25
|
-
},
|
|
26
|
-
success: {
|
|
27
|
-
surface: 'bg-success-50 text-success-900',
|
|
28
|
-
border: 'border-success-100',
|
|
29
|
-
icon: 'text-success-600',
|
|
30
|
-
},
|
|
31
|
-
warning: {
|
|
32
|
-
surface: 'bg-warning-50 text-warning-900',
|
|
33
|
-
border: 'border-warning-100',
|
|
34
|
-
icon: 'text-warning-600',
|
|
35
|
-
},
|
|
36
|
-
danger: {
|
|
37
|
-
surface: 'bg-danger-50 text-danger-900',
|
|
38
|
-
border: 'border-danger-100',
|
|
39
|
-
icon: 'text-danger-600',
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
filled: {
|
|
43
|
-
info: {
|
|
44
|
-
surface: 'bg-info-600 text-info-50',
|
|
45
|
-
border: 'border-info-700',
|
|
46
|
-
icon: 'text-info-50',
|
|
47
|
-
},
|
|
48
|
-
success: {
|
|
49
|
-
surface: 'bg-success-600 text-success-50',
|
|
50
|
-
border: 'border-success-700',
|
|
51
|
-
icon: 'text-success-50',
|
|
52
|
-
},
|
|
53
|
-
warning: {
|
|
54
|
-
surface: 'bg-warning-600 text-warning-50',
|
|
55
|
-
border: 'border-warning-700',
|
|
56
|
-
icon: 'text-warning-50',
|
|
57
|
-
},
|
|
58
|
-
danger: {
|
|
59
|
-
surface: 'bg-danger-600 text-danger-50',
|
|
60
|
-
border: 'border-danger-700',
|
|
61
|
-
icon: 'text-danger-50',
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
outlined: {
|
|
65
|
-
info: {
|
|
66
|
-
surface: 'bg-transparent text-info-700',
|
|
67
|
-
border: 'border-info-300',
|
|
68
|
-
icon: 'text-info-600',
|
|
69
|
-
},
|
|
70
|
-
success: {
|
|
71
|
-
surface: 'bg-transparent text-success-700',
|
|
72
|
-
border: 'border-success-300',
|
|
73
|
-
icon: 'text-success-600',
|
|
74
|
-
},
|
|
75
|
-
warning: {
|
|
76
|
-
surface: 'bg-transparent text-warning-700',
|
|
77
|
-
border: 'border-warning-300',
|
|
78
|
-
icon: 'text-warning-600',
|
|
79
|
-
},
|
|
80
|
-
danger: {
|
|
81
|
-
surface: 'bg-transparent text-danger-700',
|
|
82
|
-
border: 'border-danger-300',
|
|
83
|
-
icon: 'text-danger-600',
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Resolve the Tailwind classes for one (variant, severity) pair.
|
|
90
|
-
*
|
|
91
|
-
* @example
|
|
92
|
-
* const { surface, border, icon } = getSeverityClasses('standard', 'warning');
|
|
93
|
-
* // surface: 'bg-warning-50 text-warning-900'
|
|
94
|
-
* // border: 'border-warning-100'
|
|
95
|
-
* // icon: 'text-warning-600'
|
|
96
|
-
*/
|
|
97
|
-
export function getSeverityClasses(
|
|
98
|
-
variant: SeverityVariant,
|
|
99
|
-
severity: Severity
|
|
100
|
-
): SeverityClasses {
|
|
101
|
-
return SEVERITY_MATRIX[variant][severity];
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* ARIA role for a severity. Follows the W3C WAI-ARIA spec:
|
|
106
|
-
* - `alert` for assertive states (warning / danger) — interrupts the
|
|
107
|
-
* screen-reader announcement queue.
|
|
108
|
-
* - `status` for polite states (info / success) — queued behind other
|
|
109
|
-
* announcements.
|
|
110
|
-
*
|
|
111
|
-
* Consumers can override via an explicit `role` prop.
|
|
112
|
-
*/
|
|
113
|
-
export function getSeverityRole(severity: Severity): 'alert' | 'status' {
|
|
114
|
-
return severity === 'warning' || severity === 'danger' ? 'alert' : 'status';
|
|
115
|
-
}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { readFileSync, readdirSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
import { describe, it, expect } from 'vitest';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Package-level theme-identity regression test.
|
|
7
|
-
*
|
|
8
|
-
* Enforces the Dashforge rule
|
|
9
|
-
* (memory: `feedback_dashforge_preset_is_identity`):
|
|
10
|
-
* the `@dashforge/tw-theme` default preset auto-inverts the neutral
|
|
11
|
-
* palette via a CSS-variable swap (`bg-neutral-50` is light surface
|
|
12
|
-
* in light mode AND dark surface in dark mode — same class, different
|
|
13
|
-
* physical color). Adding a `dark:` Tailwind variant on a
|
|
14
|
-
* `neutral-*` class creates a DOUBLE inversion that breaks dark mode.
|
|
15
|
-
*
|
|
16
|
-
* The Sprint 4.1 Table fix established the rule; Sprint 4.3 applies
|
|
17
|
-
* it to the whole catalog and adds this test as a regression guard.
|
|
18
|
-
*
|
|
19
|
-
* **Allowed exception** (Category B in `THEME-AUDIT.md`): the pattern
|
|
20
|
-
* `bg-white dark:bg-neutral-N` / `fill-white dark:fill-neutral-N` is
|
|
21
|
-
* LEGITIMATE — `bg-white` is hardcoded (no auto-inversion via CSS
|
|
22
|
-
* vars) so a `dark:` counterpart IS needed to get a dark elevated
|
|
23
|
-
* surface in dark mode. The regex below strips these legitimate
|
|
24
|
-
* patterns before scanning for the anti-pattern.
|
|
25
|
-
*
|
|
26
|
-
* Color palettes (`primary` / `secondary` / `success` / `warning` /
|
|
27
|
-
* `danger` / `info`) are EXEMPT from this rule by design — they
|
|
28
|
-
* do not auto-invert, so `dark:` shifts on them are legitimate
|
|
29
|
-
* design choices (tone refinement across modes).
|
|
30
|
-
*/
|
|
31
|
-
const COMPONENTS_DIR = join(__dirname, '..');
|
|
32
|
-
|
|
33
|
-
// The legitimate Category B patterns we allow:
|
|
34
|
-
// 1. `bg-white dark:bg-neutral-N`
|
|
35
|
-
// 2. `fill-white dark:fill-neutral-N`
|
|
36
|
-
// 3. variant-prefixed versions, e.g.
|
|
37
|
-
// `data-[state=active]:bg-white dark:data-[state=active]:bg-neutral-N`
|
|
38
|
-
//
|
|
39
|
-
// Strip these from the source before scanning for the anti-pattern.
|
|
40
|
-
const CATEGORY_B_RE =
|
|
41
|
-
/(?:[\w[\]:-]+:)?(?:bg|fill)-white\s+dark:(?:[\w[\]:-]+:)*(?:bg|fill)-neutral-\d+/g;
|
|
42
|
-
|
|
43
|
-
// Anti-pattern: any `dark:` variant on a `neutral-N` class.
|
|
44
|
-
// Covers `bg`, `text`, `border`, `ring`, `placeholder:text`,
|
|
45
|
-
// `hover:bg`, `focus-visible:bg`, `data-[state=*]:bg`, etc.
|
|
46
|
-
const NEUTRAL_DARK_RE =
|
|
47
|
-
/dark:(?:[\w[\]:-]+:)*(?:bg|text|border|ring|fill|placeholder|focus-visible|hover|focus|data)?-?neutral-\d+/;
|
|
48
|
-
|
|
49
|
-
function walkSourceFiles(dir: string): string[] {
|
|
50
|
-
const entries = readdirSync(dir, { withFileTypes: true });
|
|
51
|
-
const out: string[] = [];
|
|
52
|
-
for (const entry of entries) {
|
|
53
|
-
const full = join(dir, entry.name);
|
|
54
|
-
if (entry.isDirectory()) {
|
|
55
|
-
out.push(...walkSourceFiles(full));
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
if (!entry.isFile()) continue;
|
|
59
|
-
if (entry.name.endsWith('.test.ts')) continue;
|
|
60
|
-
if (entry.name.endsWith('.test.tsx')) continue;
|
|
61
|
-
if (entry.name.endsWith('.spec.ts')) continue;
|
|
62
|
-
if (entry.name.endsWith('.spec.tsx')) continue;
|
|
63
|
-
if (!/\.(ts|tsx)$/.test(entry.name)) continue;
|
|
64
|
-
out.push(full);
|
|
65
|
-
}
|
|
66
|
-
return out;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
describe('Catalog — theme identity rule (no `dark:` on neutral palette)', () => {
|
|
70
|
-
const files = walkSourceFiles(COMPONENTS_DIR);
|
|
71
|
-
|
|
72
|
-
it('finds the catalog source files', () => {
|
|
73
|
-
expect(files.length).toBeGreaterThan(20);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
for (const file of files) {
|
|
77
|
-
const rel = file.replace(COMPONENTS_DIR + '/', '');
|
|
78
|
-
it(`${rel} — no \`dark:*-neutral-N\` anti-pattern`, () => {
|
|
79
|
-
const source = readFileSync(file, 'utf-8');
|
|
80
|
-
const stripped = source
|
|
81
|
-
// 1. Strip legitimate Category B patterns (bg-white / fill-white
|
|
82
|
-
// paired with a `dark:` neutral target — bg-white doesn't
|
|
83
|
-
// auto-invert, so the `dark:` is intentional).
|
|
84
|
-
.replace(CATEGORY_B_RE, '')
|
|
85
|
-
// 2. Strip comments — the regex examples in THEME-AUDIT and
|
|
86
|
-
// this test source itself contain the anti-pattern as text.
|
|
87
|
-
.replace(/\/\/.*$/gm, '')
|
|
88
|
-
.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
89
|
-
|
|
90
|
-
const match = stripped.match(NEUTRAL_DARK_RE);
|
|
91
|
-
if (match) {
|
|
92
|
-
const idx = stripped.indexOf(match[0]);
|
|
93
|
-
const context = stripped.slice(
|
|
94
|
-
Math.max(0, idx - 80),
|
|
95
|
-
idx + match[0].length + 80,
|
|
96
|
-
);
|
|
97
|
-
throw new Error(
|
|
98
|
-
`Found \`dark:*-neutral-N\` anti-pattern in ${rel}:\n` +
|
|
99
|
-
` ...${context.replace(/\s+/g, ' ').trim()}...\n\n` +
|
|
100
|
-
`Rule: dashforgePreset() auto-inverts neutral palette via CSS-var swap. ` +
|
|
101
|
-
`Adding \`dark:bg-neutral-N\` (or similar) creates double inversion ` +
|
|
102
|
-
`and breaks dark mode.\n\n` +
|
|
103
|
-
`Fix: drop the \`dark:\` variant — the base class auto-inverts.\n\n` +
|
|
104
|
-
`Exception: \`bg-white dark:bg-neutral-N\` IS allowed (bg-white doesn't ` +
|
|
105
|
-
`auto-invert, the dark variant is required for proper dark elevation).\n\n` +
|
|
106
|
-
`See: libs/dashforge/tw/THEME-AUDIT.md, ` +
|
|
107
|
-
`memory \`feedback_dashforge_preset_is_identity\`.`,
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
expect(match).toBeNull();
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
});
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
3
|
-
import { renderHook } from '@testing-library/react';
|
|
4
|
-
import { RbacProvider } from '@dashforge/rbac';
|
|
5
|
-
import type {
|
|
6
|
-
RbacPolicy,
|
|
7
|
-
Subject,
|
|
8
|
-
AccessRequirement,
|
|
9
|
-
} from '@dashforge/rbac';
|
|
10
|
-
import { useAccessState } from './useAccessState.js';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* TW-side copy of the MUI useAccessState unit test, kept independent
|
|
14
|
-
* per the isolation contract. The hook contract is identical so the
|
|
15
|
-
* MUI test cases double as the source of truth for behaviour parity.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
const FULL_ACCESS_POLICY: RbacPolicy = {
|
|
19
|
-
roles: [
|
|
20
|
-
{
|
|
21
|
-
name: 'admin',
|
|
22
|
-
permissions: [{ resource: '*', action: '*', effect: 'allow' }],
|
|
23
|
-
},
|
|
24
|
-
],
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const READ_ONLY_POLICY: RbacPolicy = {
|
|
28
|
-
roles: [
|
|
29
|
-
{
|
|
30
|
-
name: 'viewer',
|
|
31
|
-
permissions: [
|
|
32
|
-
{ resource: 'document', action: 'read', effect: 'allow' },
|
|
33
|
-
{ resource: 'document', action: 'update', effect: 'deny' },
|
|
34
|
-
],
|
|
35
|
-
},
|
|
36
|
-
],
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const adminSubject: Subject = { id: 'admin-user', roles: ['admin'] };
|
|
40
|
-
const viewerSubject: Subject = { id: 'viewer-user', roles: ['viewer'] };
|
|
41
|
-
|
|
42
|
-
function wrapWithRbac(policy: RbacPolicy, subject: Subject) {
|
|
43
|
-
return function Wrapper({ children }: { children: React.ReactNode }) {
|
|
44
|
-
return (
|
|
45
|
-
<RbacProvider policy={policy} subject={subject}>
|
|
46
|
-
{children}
|
|
47
|
-
</RbacProvider>
|
|
48
|
-
);
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
describe('useAccessState (tw)', () => {
|
|
53
|
-
describe('no access requirement', () => {
|
|
54
|
-
it('returns default full-access state when access is undefined', () => {
|
|
55
|
-
const { result } = renderHook(() => useAccessState(undefined));
|
|
56
|
-
expect(result.current).toEqual({
|
|
57
|
-
visible: true,
|
|
58
|
-
disabled: false,
|
|
59
|
-
readonly: false,
|
|
60
|
-
granted: true,
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('returns stable identity across re-renders', () => {
|
|
65
|
-
const { result, rerender } = renderHook(() => useAccessState(undefined));
|
|
66
|
-
const first = result.current;
|
|
67
|
-
rerender();
|
|
68
|
-
expect(result.current).toBe(first);
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
describe('access requirement WITHOUT RbacProvider (fail-safe)', () => {
|
|
73
|
-
let warnSpy: ReturnType<typeof vi.spyOn>;
|
|
74
|
-
beforeEach(() => {
|
|
75
|
-
warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
|
|
76
|
-
});
|
|
77
|
-
afterEach(() => {
|
|
78
|
-
warnSpy.mockRestore();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('returns DEFAULT_ACCESS_STATE (fails open) when no provider mounted', () => {
|
|
82
|
-
const access: AccessRequirement = { resource: 'doc', action: 'read' };
|
|
83
|
-
const { result } = renderHook(() => useAccessState(access));
|
|
84
|
-
expect(result.current.visible).toBe(true);
|
|
85
|
-
expect(result.current.granted).toBe(true);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('logs a dev-mode warning naming the missing requirement', () => {
|
|
89
|
-
const access: AccessRequirement = { resource: 'doc', action: 'update' };
|
|
90
|
-
renderHook(() => useAccessState(access));
|
|
91
|
-
expect(warnSpy).toHaveBeenCalled();
|
|
92
|
-
const msg = String(warnSpy.mock.calls[0]?.[0] ?? '');
|
|
93
|
-
expect(msg).toContain('No RbacProvider');
|
|
94
|
-
expect(msg).toContain('doc');
|
|
95
|
-
expect(msg).toContain('update');
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
describe('access requirement WITH RbacProvider', () => {
|
|
100
|
-
it('grants when policy allows', () => {
|
|
101
|
-
const access: AccessRequirement = { resource: 'document', action: 'read' };
|
|
102
|
-
const { result } = renderHook(() => useAccessState(access), {
|
|
103
|
-
wrapper: wrapWithRbac(FULL_ACCESS_POLICY, adminSubject),
|
|
104
|
-
});
|
|
105
|
-
expect(result.current.granted).toBe(true);
|
|
106
|
-
expect(result.current.visible).toBe(true);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('denies (hide) when policy forbids and onUnauthorized=hide', () => {
|
|
110
|
-
const access: AccessRequirement = {
|
|
111
|
-
resource: 'document',
|
|
112
|
-
action: 'update',
|
|
113
|
-
onUnauthorized: 'hide',
|
|
114
|
-
};
|
|
115
|
-
const { result } = renderHook(() => useAccessState(access), {
|
|
116
|
-
wrapper: wrapWithRbac(READ_ONLY_POLICY, viewerSubject),
|
|
117
|
-
});
|
|
118
|
-
expect(result.current.granted).toBe(false);
|
|
119
|
-
expect(result.current.visible).toBe(false);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('denies (disable) when policy forbids and onUnauthorized=disable', () => {
|
|
123
|
-
const access: AccessRequirement = {
|
|
124
|
-
resource: 'document',
|
|
125
|
-
action: 'update',
|
|
126
|
-
onUnauthorized: 'disable',
|
|
127
|
-
};
|
|
128
|
-
const { result } = renderHook(() => useAccessState(access), {
|
|
129
|
-
wrapper: wrapWithRbac(READ_ONLY_POLICY, viewerSubject),
|
|
130
|
-
});
|
|
131
|
-
expect(result.current.granted).toBe(false);
|
|
132
|
-
expect(result.current.visible).toBe(true);
|
|
133
|
-
expect(result.current.disabled).toBe(true);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it('denies (readonly) when policy forbids and onUnauthorized=readonly', () => {
|
|
137
|
-
const access: AccessRequirement = {
|
|
138
|
-
resource: 'document',
|
|
139
|
-
action: 'update',
|
|
140
|
-
onUnauthorized: 'readonly',
|
|
141
|
-
};
|
|
142
|
-
const { result } = renderHook(() => useAccessState(access), {
|
|
143
|
-
wrapper: wrapWithRbac(READ_ONLY_POLICY, viewerSubject),
|
|
144
|
-
});
|
|
145
|
-
expect(result.current.granted).toBe(false);
|
|
146
|
-
expect(result.current.readonly).toBe(true);
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
describe('memoization', () => {
|
|
151
|
-
it('returns identical reference when access reference stays the same', () => {
|
|
152
|
-
const access: AccessRequirement = { resource: 'doc', action: 'read' };
|
|
153
|
-
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
|
|
154
|
-
const { result, rerender } = renderHook(() => useAccessState(access));
|
|
155
|
-
const first = result.current;
|
|
156
|
-
rerender();
|
|
157
|
-
expect(result.current).toBe(first);
|
|
158
|
-
warnSpy.mockRestore();
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
});
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { useMemo } from 'react';
|
|
2
|
-
import { useRbacOptional, resolveAccessState } from '@dashforge/rbac';
|
|
3
|
-
import type { AccessRequirement, AccessState } from '@dashforge/rbac';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Default full-access state. Returned when no access requirement is
|
|
7
|
-
* supplied **or** when no `RbacProvider` is mounted (fail-safe default).
|
|
8
|
-
*
|
|
9
|
-
* @internal
|
|
10
|
-
*/
|
|
11
|
-
const DEFAULT_ACCESS_STATE: AccessState = {
|
|
12
|
-
visible: true,
|
|
13
|
-
disabled: false,
|
|
14
|
-
readonly: false,
|
|
15
|
-
granted: true,
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Hook to resolve RBAC access state for a UI component.
|
|
20
|
-
*
|
|
21
|
-
* **TW-side copy of the MUI implementation** in `@dashforge/ui/src/hooks/useAccessState.ts`.
|
|
22
|
-
* Per the architecture plan v2 (2026-05-15) the two ecosystems share
|
|
23
|
-
* only the bridge layer (`@dashforge/forms` + `@dashforge/ui-core` +
|
|
24
|
-
* `@dashforge/rbac`); UI-side hooks are duplicated intentionally so
|
|
25
|
-
* the MUI and TW ecosystems remain fully isolated.
|
|
26
|
-
*
|
|
27
|
-
* The behaviour is byte-equivalent to the MUI side:
|
|
28
|
-
*
|
|
29
|
-
* - If `access` is undefined → returns `DEFAULT_ACCESS_STATE`.
|
|
30
|
-
* - If `access` is supplied but no `RbacProvider` is mounted → returns
|
|
31
|
-
* `DEFAULT_ACCESS_STATE` and emits a dev-only `console.warn` so the
|
|
32
|
-
* developer notices the missing provider. Failing **open** is the
|
|
33
|
-
* safer default (RBAC silently denying everything would be a hidden
|
|
34
|
-
* regression in development).
|
|
35
|
-
* - Otherwise → evaluates the requirement against the active
|
|
36
|
-
* `rbac.engine` + `rbac.subject` via `resolveAccessState`.
|
|
37
|
-
*
|
|
38
|
-
* The memo dependency list is `[access, rbac]` — a stable RBAC
|
|
39
|
-
* context + identical access requirement re-uses the same result
|
|
40
|
-
* object so downstream `useMemo` / `React.memo` comparisons stay
|
|
41
|
-
* cheap.
|
|
42
|
-
*
|
|
43
|
-
* @example
|
|
44
|
-
* ```tsx
|
|
45
|
-
* function MyComponent({ access }: { access?: AccessRequirement }) {
|
|
46
|
-
* const accessState = useAccessState(access);
|
|
47
|
-
* if (!accessState.visible) return null;
|
|
48
|
-
* return <input disabled={accessState.disabled} readOnly={accessState.readonly} />;
|
|
49
|
-
* }
|
|
50
|
-
* ```
|
|
51
|
-
*/
|
|
52
|
-
export function useAccessState(
|
|
53
|
-
access: AccessRequirement | undefined
|
|
54
|
-
): AccessState {
|
|
55
|
-
// Hooks are called unconditionally to comply with the rules of hooks.
|
|
56
|
-
// `useRbacOptional` returns `null` when no provider is mounted
|
|
57
|
-
// (it does NOT throw — that's intentional, see its doc).
|
|
58
|
-
const rbac = useRbacOptional();
|
|
59
|
-
|
|
60
|
-
return useMemo(() => {
|
|
61
|
-
if (!access) {
|
|
62
|
-
return DEFAULT_ACCESS_STATE;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (!rbac) {
|
|
66
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
67
|
-
|
|
68
|
-
console.warn(
|
|
69
|
-
'[useAccessState] No RbacProvider found but access requirement was provided. ' +
|
|
70
|
-
'Defaulting to full access. Wrap your component tree with <RbacProvider> to enable RBAC. ' +
|
|
71
|
-
`Resource: ${access.resource}, Action: ${access.action}`
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
return DEFAULT_ACCESS_STATE;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return resolveAccessState(access, (request) =>
|
|
78
|
-
rbac.engine.can(rbac.subject, request)
|
|
79
|
-
);
|
|
80
|
-
}, [access, rbac]);
|
|
81
|
-
}
|