@jmruthers/pace-core 0.6.10 → 0.6.11
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 -0
- package/audit-tool/00-dependencies.cjs +46 -13
- package/audit-tool/audits/01-pace-core-compliance.cjs +96 -21
- package/audit-tool/audits/02-project-structure.cjs +13 -3
- package/audit-tool/audits/03-architecture.cjs +78 -4
- package/audit-tool/audits/04-code-quality.cjs +9 -2
- package/audit-tool/audits/05-styling.cjs +19 -7
- package/audit-tool/audits/06-security-rbac.cjs +105 -14
- package/audit-tool/audits/07-api-tech-stack.cjs +31 -15
- package/audit-tool/audits/08-testing-documentation.cjs +11 -3
- package/audit-tool/audits/09-operations.cjs +19 -7
- package/audit-tool/index.cjs +22 -11
- package/audit-tool/utils/report-utils.cjs +4 -0
- package/cursor-rules/01-pace-core-compliance.mdc +1 -0
- package/cursor-rules/02-project-structure.mdc +1 -0
- package/cursor-rules/03-architecture.mdc +3 -1
- package/cursor-rules/04-code-quality.mdc +1 -0
- package/cursor-rules/05-styling.mdc +41 -7
- package/cursor-rules/06-security-rbac.mdc +2 -1
- package/cursor-rules/07-api-tech-stack.mdc +1 -0
- package/cursor-rules/08-testing-documentation.mdc +1 -0
- package/cursor-rules/09-operations.mdc +1 -0
- package/dist/{DataTable-SAXFG4XI.js → DataTable-EFYP2QLE.js} +10 -7
- package/dist/{InactivityServiceProvider-DHryoh6K.d.ts → InactivityServiceProvider-BbxwwDz1.d.ts} +10 -1
- package/dist/{UnifiedAuthProvider-CiBAl9-s.d.ts → UnifiedAuthProvider-Bkt_tzdS.d.ts} +56 -24
- package/dist/{api-F47QJ7FX.js → api-BZR2CYXL.js} +3 -2
- package/dist/api-result-USV1Czr-.d.ts +51 -0
- package/dist/{audit-Z6ZZBWLU.js → audit-HI2DHUVU.js} +2 -1
- package/dist/{auth-BZOJqrdd.d.ts → auth-JvdRVaud.d.ts} +1 -1
- package/dist/{chunk-KSNLMI7N.js → chunk-2DL2WSOE.js} +1 -155
- package/dist/{chunk-MPY44PWB.js → chunk-2OEVOGGR.js} +4648 -3560
- package/dist/chunk-44CNXN4P.js +15 -0
- package/dist/{chunk-Y4PF6HIM.js → chunk-4R3T5ENU.js} +867 -786
- package/dist/{chunk-LNHFAF4X.js → chunk-7A6IMHH2.js} +289 -247
- package/dist/chunk-CU2BU2MQ.js +2 -0
- package/dist/{chunk-JJEYZ3DX.js → chunk-D6BMFMQZ.js} +37 -2
- package/dist/{chunk-BCTXBU6U.js → chunk-ENLXB7GP.js} +88 -71
- package/dist/{chunk-FBZ7U3ID.js → chunk-J2KQK6DG.js} +937 -987
- package/dist/{chunk-TFIPNIPE.js → chunk-KJXRL3XE.js} +3300 -2245
- package/dist/{chunk-3GWSPISD.js → chunk-L5LFKKLJ.js} +1 -1
- package/dist/{chunk-X5EAU5G7.js → chunk-PCSHBLPB.js} +132 -114
- package/dist/{chunk-NIU6DPQV.js → chunk-QRYSEPHB.js} +2 -0
- package/dist/{chunk-KYURMOQM.js → chunk-V7FTM2LU.js} +423 -320
- package/dist/chunk-WY6Y7KC3.js +264 -0
- package/dist/{chunk-FN52B75D.js → chunk-XOJME5T7.js} +176 -15
- package/dist/{chunk-7YDC7LMU.js → chunk-XPFVT3GN.js} +71 -66
- package/dist/{chunk-66R6RLUZ.js → chunk-YFTFFJIV.js} +3 -3
- package/dist/{chunk-W46INAVW.js → chunk-YYTWKVHO.js} +688 -570
- package/dist/components.d.ts +8 -7
- package/dist/components.js +17 -15
- package/dist/{database.generated-DT8JTZiP.d.ts → database.generated-qkdoiVrJ.d.ts} +45 -10
- package/dist/eslint-rules/index.cjs +3 -0
- package/dist/eslint-rules/rules/03-architecture.cjs +74 -0
- package/dist/eslint-rules/rules/06-security-rbac.cjs +74 -0
- package/dist/{event-WTAQuGcq.d.ts → event-BfCox3N2.d.ts} +36 -10
- package/dist/{file-reference-BavO2eQj.d.ts → file-reference-DU1hcawx.d.ts} +29 -13
- package/dist/hooks.d.ts +22 -9
- package/dist/hooks.js +34 -25
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +66 -177
- package/dist/index.js +316 -340
- package/dist/pagination-BW1mqywp.d.ts +201 -0
- package/dist/providers.d.ts +6 -5
- package/dist/providers.js +5 -3
- package/dist/rbac/index.d.ts +123 -138
- package/dist/rbac/index.js +10 -8
- package/dist/theming/runtime.d.ts +19 -2
- package/dist/theming/runtime.js +1 -1
- package/dist/{timezone-K-ptz3HO.d.ts → timezone-BTWWXKVY.d.ts} +1 -1
- package/dist/types.d.ts +17 -10
- package/dist/types.js +1 -0
- package/dist/{usePublicPageContext-vxBlEHO9.d.ts → usePublicPageContext-B91dGYW1.d.ts} +433 -356
- package/dist/{usePublicRouteParams-G3Ks53mk.d.ts → usePublicRouteParams-BgV6VhMi.d.ts} +73 -4
- package/dist/utils.d.ts +163 -145
- package/dist/utils.js +42 -25
- package/docs/api/modules.md +782 -643
- package/docs/api-reference/rpc-functions.md +12 -3
- package/docs/core-concepts/rbac-system.md +8 -0
- package/docs/getting-started/cursor-rules.md +17 -20
- package/docs/getting-started/dependencies.md +1 -1
- package/docs/getting-started/setup.md +235 -0
- package/docs/implementation-guides/authentication.md +27 -0
- package/docs/implementation-guides/data-tables.md +176 -3
- package/docs/migration/ApiResult-migration.md +25 -0
- package/docs/rbac/api-reference.md +33 -31
- package/docs/standards/0-standards-overview.md +50 -15
- package/docs/standards/1-pace-core-compliance-standards.md +62 -57
- package/docs/standards/2-project-structure-standards.md +33 -16
- package/docs/standards/3-architecture-standards.md +41 -1
- package/docs/standards/4-code-quality-standards.md +26 -6
- package/docs/standards/5-styling-standards.md +35 -1
- package/docs/standards/6-security-rbac-standards.md +66 -0
- package/docs/standards/7-api-tech-stack-standards.md +25 -14
- package/docs/standards/8-testing-documentation-standards.md +31 -0
- package/docs/standards/9-operations-standards.md +19 -0
- package/docs/standards/README.md +20 -201
- package/docs/testing/test-setup-for-consumers.md +2 -0
- package/docs/troubleshooting/common-issues.md +17 -1
- package/docs/troubleshooting/organisation-context-setup.md +8 -0
- package/docs/troubleshooting/print-event-name-css-variable-analysis.md +217 -0
- package/eslint-config-pace-core.cjs +20 -0
- package/package.json +14 -20
- package/scripts/{build-docs-incremental.js → build-docs.js} +3 -2
- package/scripts/setup.cjs +536 -0
- package/scripts/validate.cjs +480 -0
- package/src/__tests__/helpers/{__tests__/component-test-utils.test.tsx → component-test-utils.test.tsx} +3 -3
- package/src/__tests__/helpers/{__tests__/optimized-test-setup.test.ts → optimized-test-setup.test.ts} +2 -2
- package/src/__tests__/helpers/{__tests__/supabaseMock.test.ts → supabaseMock.test.ts} +2 -2
- package/src/__tests__/helpers/{__tests__/test-providers.test.tsx → test-providers.test.tsx} +1 -1
- package/src/__tests__/helpers/test-providers.tsx +37 -39
- package/src/__tests__/helpers/{__tests__/test-utils.test.tsx → test-utils.test.tsx} +4 -3
- package/src/__tests__/helpers/{__tests__/timer-utils.test.ts → timer-utils.test.ts} +2 -2
- package/src/assets/app-icons/index.test.ts +304 -0
- package/src/components/AddressField/AddressField.test.tsx +1 -1
- package/src/components/AddressField/AddressField.tsx +238 -212
- package/src/components/Button/Button.tsx +1 -1
- package/src/components/Card/Card.test.tsx +172 -17
- package/src/components/Card/Card.tsx +19 -10
- package/src/components/ContextSelector/ContextSelector.internals.tsx +204 -0
- package/src/components/ContextSelector/{__tests__/ContextSelector.test.tsx → ContextSelector.test.tsx} +6 -6
- package/src/components/ContextSelector/ContextSelector.tsx +66 -280
- package/src/components/ContextSelector/ContextSelector.types.ts +35 -0
- package/src/components/ContextSelector/useContextSelectorState.tsx +195 -0
- package/src/components/DataTable/AUDIT_REPORT.md +59 -44
- package/src/components/DataTable/{__tests__/DataTable.comprehensive.test.tsx → DataTable.comprehensive.test.tsx} +6 -6
- package/src/components/DataTable/{__tests__/DataTable.default-state.test.tsx → DataTable.default-state.test.tsx} +5 -5
- package/src/components/DataTable/{__tests__/DataTable.export.test.tsx → DataTable.export.test.tsx} +10 -10
- package/src/components/DataTable/{__tests__/DataTable.grouping-aggregation.test.tsx → DataTable.grouping-aggregation.test.tsx} +6 -6
- package/src/components/DataTable/{__tests__/DataTable.hooks.test.tsx → DataTable.hooks.test.tsx} +6 -6
- package/src/components/DataTable/{__tests__/DataTable.select-label-display.test.tsx → DataTable.select-label-display.test.tsx} +6 -6
- package/src/components/DataTable/DataTable.test.tsx +787 -416
- package/src/components/DataTable/DataTable.tsx +12 -12
- package/src/components/DataTable/DataTableCore.integration.test.tsx +458 -0
- package/src/components/DataTable/{__tests__/DataTableCore.test-setup.ts → DataTableCore.test-setup.ts} +10 -9
- package/src/components/DataTable/{__tests__/DataTableCore.test.tsx → DataTableCore.test.tsx} +8 -8
- package/src/components/DataTable/{__tests__/README.md → README.md} +17 -7
- package/src/components/DataTable/TESTING.md +101 -0
- package/src/components/DataTable/{__tests__/a11y.basic.test.tsx → a11y.basic.test.tsx} +34 -34
- package/src/components/DataTable/components/DataTableCore.tsx +104 -864
- package/src/components/DataTable/components/{__tests__/GroupingDropdown.test.tsx → GroupingDropdown.test.tsx} +17 -8
- package/src/components/DataTable/components/GroupingDropdown.tsx +2 -2
- package/src/components/DataTable/components/ImportModal.tsx +61 -559
- package/src/components/DataTable/components/ImportModalFileSection.tsx +148 -0
- package/src/components/DataTable/context/{__tests__/DataTableContext.test.tsx → DataTableContext.test.tsx} +2 -2
- package/src/components/DataTable/context/DataTableContext.tsx +7 -6
- package/src/components/DataTable/core/{__tests__/ColumnFactory.test.ts → ColumnFactory.test.ts} +2 -2
- package/src/components/DataTable/hooks/{__tests__/useColumnOrderPersistence.test.ts → useColumnOrderPersistence.test.ts} +2 -2
- package/src/components/DataTable/hooks/{__tests__/useColumnVisibilityPersistence.test.ts → useColumnVisibilityPersistence.test.ts} +2 -2
- package/src/components/DataTable/hooks/{__tests__/useDataTableConfiguration.test.ts → useDataTableConfiguration.test.ts} +3 -3
- package/src/components/DataTable/hooks/useDataTableConfiguration.ts +14 -2
- package/src/components/DataTable/hooks/{__tests__/useDataTableDataPipeline.test.ts → useDataTableDataPipeline.test.ts} +6 -6
- package/src/components/DataTable/hooks/useDataTableDeletionBatching.test.ts +127 -0
- package/src/components/DataTable/hooks/useDataTableDeletionBatching.ts +106 -0
- package/src/components/DataTable/hooks/useDataTableEffectiveActions.test.ts +461 -0
- package/src/components/DataTable/hooks/useDataTableEffectiveActions.ts +238 -0
- package/src/components/DataTable/hooks/useDataTableLayoutHandlers.test.ts +296 -0
- package/src/components/DataTable/hooks/useDataTableLayoutHandlers.ts +175 -0
- package/src/components/DataTable/hooks/useDataTablePaginationSync.test.ts +203 -0
- package/src/components/DataTable/hooks/useDataTablePaginationSync.ts +109 -0
- package/src/components/DataTable/hooks/{__tests__/useDataTablePermissions.test.ts → useDataTablePermissions.test.ts} +11 -11
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +79 -247
- package/src/components/DataTable/hooks/useDataTablePipeline.test.tsx +219 -0
- package/src/components/DataTable/hooks/useDataTablePipeline.tsx +239 -0
- package/src/components/DataTable/hooks/useDataTableRenderGuard.test.tsx +316 -0
- package/src/components/DataTable/hooks/useDataTableRenderGuard.tsx +195 -0
- package/src/components/DataTable/hooks/useDataTableScope.test.ts +110 -0
- package/src/components/DataTable/hooks/useDataTableScope.ts +123 -0
- package/src/components/DataTable/hooks/{__tests__/useDataTableState.test.ts → useDataTableState.test.ts} +47 -5
- package/src/components/DataTable/hooks/useDataTableState.ts +145 -94
- package/src/components/DataTable/hooks/useDataTableStateAndPersistence.test.ts +277 -0
- package/src/components/DataTable/hooks/useDataTableStateAndPersistence.ts +222 -0
- package/src/components/DataTable/hooks/useDataTableSuperAdmin.test.ts +93 -0
- package/src/components/DataTable/hooks/useDataTableSuperAdmin.ts +86 -0
- package/src/components/DataTable/hooks/useDataTableTableInstance.test.ts +185 -0
- package/src/components/DataTable/hooks/useDataTableTableInstance.ts +178 -0
- package/src/components/DataTable/hooks/{__tests__/useEffectiveColumnOrder.test.ts → useEffectiveColumnOrder.test.ts} +2 -2
- package/src/components/DataTable/hooks/{__tests__/useHierarchicalState.test.ts → useHierarchicalState.test.ts} +2 -2
- package/src/components/DataTable/{components/hooks → hooks}/useImportModalFocus.test.ts +3 -3
- package/src/components/DataTable/{components/hooks → hooks}/useImportModalFocus.ts +2 -2
- package/src/components/DataTable/hooks/useImportModalState.test.ts +390 -0
- package/src/components/DataTable/hooks/useImportModalState.ts +345 -0
- package/src/components/DataTable/hooks/{__tests__/useKeyboardNavigation.test.ts → useKeyboardNavigation.test.ts} +3 -3
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +309 -269
- package/src/components/DataTable/{components/hooks → hooks}/usePermissionTracking.test.ts +3 -3
- package/src/components/DataTable/{components/hooks → hooks}/usePermissionTracking.ts +3 -3
- package/src/components/DataTable/hooks/{__tests__/useServerSideDataEffect.test.ts → useServerSideDataEffect.test.ts} +2 -2
- package/src/components/DataTable/hooks/useServerSideDataEffect.ts +14 -3
- package/src/components/DataTable/hooks/{__tests__/useTableColumns.test.ts → useTableColumns.test.ts} +2 -2
- package/src/components/DataTable/hooks/{__tests__/useTableHandlers.test.ts → useTableHandlers.test.ts} +25 -4
- package/src/components/DataTable/hooks/useTableHandlers.ts +5 -2
- package/src/components/DataTable/index.ts +18 -17
- package/src/components/DataTable/{__tests__/keyboard.test.tsx → keyboard.test.tsx} +41 -63
- package/src/components/DataTable/{__tests__/mocks → mocks}/MockRBACProvider.tsx +1 -1
- package/src/components/DataTable/{__tests__/pagination.modes.test.tsx → pagination.modes.test.tsx} +6 -6
- package/src/components/DataTable/{__tests__/ssr.strict-mode.test.tsx → ssr.strict-mode.test.tsx} +2 -2
- package/src/components/DataTable/{__tests__/styles.test.ts → styles.test.ts} +1 -4
- package/src/components/DataTable/styles.ts +0 -1
- package/src/components/DataTable/test-utils/MockDataTableComponents.tsx +55 -0
- package/src/components/DataTable/{__tests__/test-utils → test-utils}/dataFactories.ts +2 -2
- package/src/components/DataTable/test-utils/featureConfig.ts +10 -0
- package/src/components/DataTable/{__tests__/test-utils/sharedTestUtils.tsx → test-utils/sharedTestUtils.ts} +97 -66
- package/src/components/DataTable/{__tests__/test-utils.ts → test-utils.ts} +1 -1
- package/src/components/DataTable/types/actions.ts +71 -0
- package/src/components/DataTable/types/base.ts +39 -0
- package/src/components/DataTable/types/columns.ts +125 -0
- package/src/components/DataTable/types/export.ts +32 -0
- package/src/components/DataTable/types/features.ts +81 -0
- package/src/components/DataTable/types/hierarchical.ts +44 -0
- package/src/components/DataTable/types/index.ts +43 -0
- package/src/components/DataTable/types/pagination.ts +85 -0
- package/src/components/DataTable/types/performance.ts +47 -0
- package/src/components/DataTable/types/props.ts +62 -0
- package/src/components/DataTable/types/rbac.ts +45 -0
- package/src/components/DataTable/{components/__tests__ → ui/layout}/DataTableCore.test.tsx +430 -28
- package/src/components/DataTable/ui/layout/DataTableCore.tsx +345 -0
- package/src/components/DataTable/{components/__tests__ → ui/layout}/DataTableErrorBoundary.test.tsx +4 -4
- package/src/components/DataTable/{components → ui/layout}/DataTableErrorBoundary.tsx +7 -7
- package/src/components/DataTable/ui/layout/DataTableLayout.test.tsx +1352 -0
- package/src/components/DataTable/ui/layout/DataTableLayout.tsx +661 -0
- package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.test.tsx +91 -0
- package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.tsx +43 -0
- package/src/components/DataTable/ui/modals/DataTableModals.test.tsx +749 -0
- package/src/components/DataTable/{components → ui/modals}/DataTableModals.tsx +36 -28
- package/src/components/DataTable/ui/modals/ImportModal.test.tsx +1834 -0
- package/src/components/DataTable/ui/modals/ImportModal.tsx +197 -0
- package/src/components/DataTable/ui/modals/ImportModalFailedRowsSection.tsx +60 -0
- package/src/components/DataTable/ui/modals/ImportModalFileSection.tsx +148 -0
- package/src/components/DataTable/ui/modals/ImportModalPreviewSection.tsx +60 -0
- package/src/components/DataTable/ui/modals/ImportModalSummarySection.tsx +59 -0
- package/src/components/DataTable/ui/modals/importModalPersistence.ts +73 -0
- package/src/components/DataTable/{components/__tests__ → ui/shared}/AccessDeniedPage.test.tsx +2 -2
- package/src/components/DataTable/{components → ui/shared}/AccessDeniedPage.tsx +2 -2
- package/src/components/DataTable/{components/__tests__ → ui/shared}/ActionButtons.test.tsx +6 -4
- package/src/components/DataTable/{components → ui/shared}/ActionButtons.tsx +4 -4
- package/src/components/DataTable/{components/__tests__ → ui/shared}/ColumnFilter.test.tsx +29 -16
- package/src/components/DataTable/{components → ui/shared}/ColumnFilter.tsx +4 -4
- package/src/components/DataTable/{components/__tests__ → ui/shared}/PaginationControls.test.tsx +38 -16
- package/src/components/DataTable/{components → ui/shared}/PaginationControls.tsx +21 -15
- package/src/components/DataTable/{components/__tests__ → ui/shared}/SortIndicator.test.tsx +2 -2
- package/src/components/DataTable/{components → ui/shared}/SortIndicator.tsx +1 -1
- package/src/components/DataTable/{components/__tests__ → ui/table}/EditFields.test.tsx +3 -3
- package/src/components/DataTable/{components → ui/table}/EditFields.tsx +138 -69
- package/src/components/DataTable/{components/__tests__ → ui/table}/EditableRow.test.tsx +36 -27
- package/src/components/DataTable/{components → ui/table}/EditableRow.tsx +86 -104
- package/src/components/DataTable/{components/__tests__ → ui/table}/EmptyState.test.tsx +2 -62
- package/src/components/DataTable/{components → ui/table}/EmptyState.tsx +7 -15
- package/src/components/DataTable/{components/__tests__ → ui/table}/FilterRow.test.tsx +5 -4
- package/src/components/DataTable/{components → ui/table}/FilterRow.tsx +3 -3
- package/src/components/DataTable/{components/__tests__ → ui/table}/LoadingState.test.tsx +6 -10
- package/src/components/DataTable/{components → ui/table}/LoadingState.tsx +4 -4
- package/src/components/DataTable/{components/__tests__ → ui/table}/RowComponent.test.tsx +412 -17
- package/src/components/DataTable/{components → ui/table}/RowComponent.tsx +183 -177
- package/src/components/DataTable/{components/__tests__ → ui/table}/UnifiedTableBody.test.tsx +425 -16
- package/src/components/DataTable/ui/table/UnifiedTableBody.tsx +440 -0
- package/src/components/DataTable/{components/__tests__ → ui/table}/cellValueUtils.test.ts +2 -2
- package/src/components/DataTable/{components → ui/table}/cellValueUtils.ts +1 -1
- package/src/components/DataTable/{components/__tests__ → ui/toolbar}/BulkOperationsDropdown.test.tsx +12 -5
- package/src/components/DataTable/{components → ui/toolbar}/BulkOperationsDropdown.tsx +3 -3
- package/src/components/DataTable/{components/__tests__ → ui/toolbar}/ColumnVisibilityDropdown.test.tsx +7 -4
- package/src/components/DataTable/{components → ui/toolbar}/ColumnVisibilityDropdown.tsx +7 -7
- package/src/components/DataTable/{components/__tests__ → ui/toolbar}/DataTableToolbar.test.tsx +4 -4
- package/src/components/DataTable/{components → ui/toolbar}/DataTableToolbar.tsx +4 -4
- package/src/components/DataTable/ui/toolbar/GroupingDropdown.test.tsx +621 -0
- package/src/components/DataTable/ui/toolbar/GroupingDropdown.tsx +107 -0
- package/src/components/DataTable/utils/{__tests__/a11yUtils.test.ts → a11yUtils.test.ts} +2 -2
- package/src/components/DataTable/utils/{__tests__/aggregationUtils.test.ts → aggregationUtils.test.ts} +3 -3
- package/src/components/DataTable/utils/{__tests__/columnUtils.test.ts → columnUtils.test.ts} +2 -2
- package/src/components/DataTable/utils/csvParse.test.ts +74 -0
- package/src/components/DataTable/utils/csvParse.ts +65 -0
- package/src/components/DataTable/utils/{__tests__/errorHandling.test.ts → errorHandling.test.ts} +2 -2
- package/src/components/DataTable/utils/{__tests__/exportUtils.test.ts → exportUtils.test.ts} +3 -3
- package/src/components/DataTable/utils/{__tests__/flexibleImport.test.ts → flexibleImport.test.ts} +2 -2
- package/src/components/DataTable/utils/flexibleImport.ts +3 -186
- package/src/components/DataTable/utils/{__tests__/hierarchicalSorting.test.ts → hierarchicalSorting.test.ts} +3 -3
- package/src/components/DataTable/utils/{__tests__/hierarchicalUtils.test.ts → hierarchicalUtils.test.ts} +3 -3
- package/src/components/DataTable/utils/importDateParser.test.ts +162 -0
- package/src/components/DataTable/utils/importDateParser.ts +114 -0
- package/src/components/DataTable/utils/importValueParser.test.ts +138 -0
- package/src/components/DataTable/utils/importValueParser.ts +91 -0
- package/src/components/DataTable/utils/{__tests__/paginationUtils.test.ts → paginationUtils.test.ts} +2 -2
- package/src/components/DataTable/utils/paginationUtils.ts +6 -3
- package/src/components/DataTable/utils/{__tests__/performanceUtils.test.ts → performanceUtils.test.ts} +3 -3
- package/src/components/DataTable/utils/{__tests__/rowUtils.test.ts → rowUtils.test.ts} +3 -3
- package/src/components/DataTable/utils/{__tests__/selectFieldUtils.test.ts → selectFieldUtils.test.ts} +66 -3
- package/src/components/DataTable/utils/selectFieldUtils.ts +97 -60
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +1 -1
- package/src/components/DateTimeField/DateTimeField.test.tsx +1 -1
- package/src/components/Dialog/Dialog.test-utils.ts +49 -0
- package/src/components/Dialog/Dialog.test.tsx +896 -89
- package/src/components/Dialog/Dialog.tsx +174 -882
- package/src/components/Dialog/dialogLock.test.ts +238 -0
- package/src/components/Dialog/dialogLock.ts +98 -0
- package/src/components/Dialog/index.ts +2 -0
- package/src/components/Dialog/useDialogDimensions.test.ts +163 -0
- package/src/components/Dialog/useDialogDimensions.ts +140 -0
- package/src/components/Dialog/useDialogLifecycle.test.ts +358 -0
- package/src/components/Dialog/useDialogLifecycle.ts +135 -0
- package/src/components/Dialog/useDialogPersistence.test.ts +381 -0
- package/src/components/Dialog/useDialogPersistence.ts +357 -0
- package/src/components/FileDisplay/FileDisplay.test.tsx +40 -40
- package/src/components/FileDisplay/FileDisplay.tsx +24 -656
- package/src/components/FileDisplay/FileDisplayContent.test.tsx +395 -0
- package/src/components/FileDisplay/FileDisplayContent.tsx +242 -0
- package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.test.tsx +74 -0
- package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.tsx +38 -0
- package/src/components/FileDisplay/FileDisplayEmptyView.test.tsx +33 -0
- package/src/components/FileDisplay/FileDisplayEmptyView.tsx +33 -0
- package/src/components/FileDisplay/FileDisplayErrorView.test.tsx +71 -0
- package/src/components/FileDisplay/FileDisplayErrorView.tsx +50 -0
- package/src/components/FileDisplay/FileDisplayLoadingFallbackView.test.tsx +22 -0
- package/src/components/FileDisplay/FileDisplayLoadingFallbackView.tsx +22 -0
- package/src/components/FileDisplay/FileDisplayLoadingView.test.tsx +21 -0
- package/src/components/FileDisplay/FileDisplayLoadingView.tsx +23 -0
- package/src/components/FileDisplay/FileDisplayMultipleFilesView.test.tsx +101 -0
- package/src/components/FileDisplay/FileDisplayMultipleFilesView.tsx +109 -0
- package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.test.tsx +58 -0
- package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.tsx +48 -0
- package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.test.tsx +111 -0
- package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.tsx +270 -0
- package/src/components/FileDisplay/FileDisplaySingleImageView.test.tsx +78 -0
- package/src/components/FileDisplay/FileDisplaySingleImageView.tsx +67 -0
- package/src/components/FileDisplay/fallbackUtils.test.ts +50 -0
- package/src/components/FileDisplay/fallbackUtils.ts +44 -0
- package/src/components/FileDisplay/fetchFileDisplayData.ts +24 -0
- package/src/components/FileDisplay/fetchFileDisplayData.unit.test.ts +183 -0
- package/src/components/FileDisplay/fileDisplayUtils.test.ts +58 -0
- package/src/components/FileDisplay/fileDisplayUtils.ts +24 -0
- package/src/{hooks/__tests__ → components/FileDisplay}/useFileDisplay.test.ts +40 -42
- package/src/components/FileDisplay/useFileDisplay.ts +515 -0
- package/src/{hooks/__tests__ → components/FileDisplay}/useFileDisplay.unit.test.ts +406 -77
- package/src/components/FileDisplay/useFileDisplayData.ts +126 -0
- package/src/{hooks/public → components/FileDisplay}/usePublicFileDisplay.test.ts +94 -88
- package/src/components/FileDisplay/usePublicFileDisplay.ts +579 -0
- package/src/components/FileUpload/FileUpload.test.tsx +16 -10
- package/src/components/FileUpload/FileUpload.tsx +107 -525
- package/src/components/FileUpload/FileUploadDropZone.tsx +112 -0
- package/src/components/FileUpload/FileUploadProgressItem.tsx +86 -0
- package/src/components/FileUpload/FileUploadProgressList.tsx +40 -0
- package/src/components/FileUpload/useFileUploadManager.test.ts +308 -0
- package/src/components/FileUpload/useFileUploadManager.ts +454 -0
- package/src/components/FileUpload/useResolvedAppId.test.ts +102 -0
- package/src/components/FileUpload/useResolvedAppId.ts +77 -0
- package/src/components/Footer/Footer.test.tsx +6 -292
- package/src/components/Footer/Footer.tsx +8 -125
- package/src/components/Form/Form.test.tsx +44 -27
- package/src/components/Form/Form.tsx +64 -287
- package/src/components/Form/useFormPersistence.ts +257 -0
- package/src/components/Header/Header.test.tsx +17 -18
- package/src/components/Header/Header.tsx +10 -1
- package/src/components/Input/Input.tsx +1 -1
- package/src/components/Label/Label.test.tsx +1 -1
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +1 -1
- package/src/components/NavigationMenu/HierarchicalNavItem.tsx +104 -0
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +1029 -26
- package/src/components/NavigationMenu/NavigationMenu.tsx +61 -361
- package/src/components/NavigationMenu/index.ts +6 -1
- package/src/components/NavigationMenu/navigationPermissionHelper.ts +188 -0
- package/src/components/NavigationMenu/{__tests__/useNavigationFiltering.test.ts → useNavigationFiltering.test.ts} +68 -53
- package/src/components/NavigationMenu/useNavigationFiltering.ts +197 -296
- package/src/components/NavigationMenu/useNavigationScope.ts +125 -0
- package/src/components/PaceAppLayout/PaceAppLayout.edge-cases.test.tsx +77 -62
- package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +3 -3
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +16 -19
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +529 -5
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +280 -756
- package/src/components/PaceAppLayout/useFilteredNavItems.ts +304 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutConfig.ts +142 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutGate.tsx +150 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutPermissions.ts +162 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutScope.ts +79 -0
- package/src/components/PaceAppLayout/useRoleBasedRouteAccess.ts +157 -0
- package/src/components/PaceAppLayout/useSuperAdminFallback.ts +58 -0
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +31 -25
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +31 -122
- package/src/components/PaceLoginPage/useLoginAppAccess.ts +153 -0
- package/src/components/Progress/Progress.tsx +1 -2
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +29 -235
- package/src/components/ProtectedRoute/useProtectedRouteState.ts +128 -0
- package/src/components/ProtectedRoute/useVisibilityRedirectGrace.ts +89 -0
- package/src/components/PublicLayout/PublicLayout.test.tsx +217 -36
- package/src/components/PublicLayout/PublicPageLayout.tsx +132 -73
- package/src/components/PublicLayout/PublicPageProvider.tsx +5 -1
- package/src/components/Select/Select.test.tsx +1 -1
- package/src/components/Select/Select.tsx +28 -18
- package/src/components/Select/{__tests__/context.test.tsx → context.test.tsx} +3 -3
- package/src/components/Select/{utils/__tests__/text.test.tsx → text.test.tsx} +2 -2
- package/src/components/Select/{utils/text.ts → text.ts} +1 -1
- package/src/components/Select/{hooks/__tests__/useSelectEvents.test.ts → useSelectEvents.test.ts} +5 -5
- package/src/components/Select/{hooks/useSelectEvents.ts → useSelectEvents.ts} +2 -2
- package/src/components/Select/{hooks/__tests__/useSelectSearch.test.tsx → useSelectSearch.test.tsx} +7 -7
- package/src/components/Select/{hooks/useSelectSearch.ts → useSelectSearch.ts} +2 -2
- package/src/components/Select/{hooks/__tests__/useSelectState.test.ts → useSelectState.test.ts} +16 -2
- package/src/components/Select/{hooks/useSelectState.ts → useSelectState.ts} +3 -3
- package/src/components/Table/Table.test.tsx +348 -0
- package/src/components/Tabs/Tabs.test.tsx +270 -0
- package/src/components/Tabs/Tabs.tsx +1 -1
- package/src/components/Toast/Toast.test.tsx +420 -0
- package/src/components/{__tests__/index.test.ts → index.test.ts} +2 -2
- package/src/constants/{__tests__/performance.test.ts → performance.test.ts} +2 -2
- package/src/hooks/{__tests__/ServiceHooks.test.tsx → ServiceHooks.test.tsx} +8 -8
- package/src/hooks/{__tests__/hooks.integration.test.tsx → hooks.integration.test.tsx} +11 -11
- package/src/hooks/index.ts +7 -4
- package/src/hooks/{__tests__/index.unit.test.ts → index.unit.test.ts} +2 -2
- package/src/hooks/public/usePublicEvent.test.ts +1 -1
- package/src/hooks/public/usePublicEventLogo.test.ts +1 -1
- package/src/hooks/public/usePublicRouteParams.test.ts +1 -1
- package/src/hooks/services/useAuth.ts +9 -7
- package/src/hooks/useAddressAutocomplete.test.ts +22 -22
- package/src/hooks/useAddressAutocomplete.ts +90 -75
- package/src/hooks/{__tests__/useAppConfig.unit.test.ts → useAppConfig.unit.test.ts} +328 -22
- package/src/hooks/{__tests__/useComponentPerformance.unit.test.tsx → useComponentPerformance.unit.test.tsx} +27 -41
- package/src/hooks/useDataTablePerformance.ts +100 -120
- package/src/hooks/{__tests__/useDataTablePerformance.unit.test.ts → useDataTablePerformance.unit.test.ts} +5 -5
- package/src/hooks/{__tests__/useDataTableState.test.ts → useDataTableState.test.ts} +2 -2
- package/src/hooks/{__tests__/useDebounce.unit.test.ts → useDebounce.unit.test.ts} +2 -2
- package/src/hooks/useEventTheme.test.ts +4 -1
- package/src/hooks/useEventTheme.ts +49 -21
- package/src/hooks/useEvents.ts +41 -1
- package/src/hooks/{__tests__/useEvents.unit.test.ts → useEvents.unit.test.ts} +5 -5
- package/src/hooks/useFileReference.test.ts +44 -41
- package/src/hooks/useFileReference.ts +182 -173
- package/src/hooks/useFileUrl.ts +1 -1
- package/src/hooks/{__tests__/useFileUrl.unit.test.ts → useFileUrl.unit.test.ts} +26 -36
- package/src/hooks/{__tests__/useFileUrlCache.test.ts → useFileUrlCache.test.ts} +8 -8
- package/src/hooks/useFileUrlCache.ts +1 -1
- package/src/hooks/{__tests__/useFocusManagement.unit.test.ts → useFocusManagement.unit.test.ts} +2 -2
- package/src/hooks/{__tests__/useFocusTrap.unit.test.tsx → useFocusTrap.unit.test.tsx} +2 -2
- package/src/hooks/{__tests__/useFormDialog.test.ts → useFormDialog.test.ts} +2 -2
- package/src/hooks/useInactivityTracker.ts +138 -131
- package/src/hooks/{__tests__/useInactivityTracker.unit.test.ts → useInactivityTracker.unit.test.ts} +3 -3
- package/src/hooks/{__tests__/useIsMobile.unit.test.ts → useIsMobile.unit.test.ts} +2 -2
- package/src/hooks/useIsPrint.ts +62 -0
- package/src/hooks/useIsPrint.unit.test.ts +545 -0
- package/src/hooks/{__tests__/useKeyboardShortcuts.unit.test.ts → useKeyboardShortcuts.unit.test.ts} +2 -2
- package/src/hooks/{__tests__/useOrganisationPermissions.unit.test.tsx → useOrganisationPermissions.unit.test.tsx} +4 -4
- package/src/hooks/useOrganisationSecurity.test.ts +3 -3
- package/src/hooks/useOrganisationSecurity.ts +190 -201
- package/src/hooks/{__tests__/useOrganisationSecurity.unit.test.tsx → useOrganisationSecurity.unit.test.tsx} +61 -63
- package/src/hooks/{__tests__/useOrganisations.unit.test.ts → useOrganisations.unit.test.ts} +5 -5
- package/src/hooks/{__tests__/usePerformanceMonitor.unit.test.ts → usePerformanceMonitor.unit.test.ts} +13 -14
- package/src/hooks/{__tests__/usePermissionCache.test.ts → usePermissionCache.test.ts} +26 -27
- package/src/hooks/usePermissionCache.ts +276 -271
- package/src/hooks/{__tests__/usePreventTabReload.test.ts → usePreventTabReload.test.ts} +2 -2
- package/src/hooks/{__tests__/usePublicEvent.simple.test.ts → usePublicEvent.simple.test.ts} +4 -4
- package/src/hooks/{__tests__/usePublicEvent.test.ts → usePublicEvent.test.ts} +4 -4
- package/src/hooks/{__tests__/usePublicEvent.unit.test.ts → usePublicEvent.unit.test.ts} +4 -4
- package/src/hooks/{__tests__/usePublicFileDisplay.test.ts → usePublicFileDisplay.test.ts} +12 -12
- package/src/hooks/{__tests__/usePublicRouteParams.unit.test.ts → usePublicRouteParams.unit.test.ts} +3 -3
- package/src/hooks/{__tests__/useQueryCache.test.ts → useQueryCache.test.ts} +2 -2
- package/src/hooks/useQueryCache.ts +0 -2
- package/src/hooks/{__tests__/useRBAC.unit.test.ts → useRBAC.unit.test.ts} +55 -38
- package/src/hooks/{__tests__/useSessionDraft.test.ts → useSessionDraft.test.ts} +2 -2
- package/src/hooks/{__tests__/useSessionRestoration.unit.test.tsx → useSessionRestoration.unit.test.tsx} +10 -19
- package/src/hooks/useStorage.ts +21 -16
- package/src/hooks/{__tests__/useStorage.unit.test.ts → useStorage.unit.test.ts} +38 -75
- package/src/hooks/{__tests__/useToast.test.ts → useToast.test.ts} +2 -2
- package/src/hooks/{__tests__/useToast.unit.test.tsx → useToast.unit.test.tsx} +2 -2
- package/src/hooks/{__tests__/useZodForm.unit.test.tsx → useZodForm.unit.test.tsx} +2 -2
- package/src/icons/{__tests__/index.test.ts → index.test.ts} +2 -2
- package/src/icons/index.ts +2 -0
- package/src/{__tests__/index.test.ts → index.test.ts} +3 -7
- package/src/index.ts +15 -7
- package/src/providers/{__tests__/AuthProvider.test.tsx → AuthProvider.test.tsx} +3 -3
- package/src/providers/{__tests__/EventProvider.test.tsx → EventProvider.test.tsx} +3 -3
- package/src/providers/InactivityProvider.test-helper.tsx +40 -0
- package/src/providers/{__tests__/InactivityProvider.test.tsx → InactivityProvider.test.tsx} +14 -21
- package/src/providers/{__tests__/ProviderLifecycle.test.tsx → ProviderLifecycle.test.tsx} +4 -4
- package/src/providers/{__tests__/UnifiedAuthProvider.test.tsx → UnifiedAuthProvider.test.tsx} +1 -1
- package/src/providers/{__tests__/index.test.ts → index.test.ts} +2 -2
- package/src/providers/services/{__tests__/AuthServiceProvider.integration.test.tsx → AuthServiceProvider.integration.test.tsx} +4 -4
- package/src/providers/services/{__tests__/AuthServiceProvider.test.tsx → AuthServiceProvider.test.tsx} +7 -7
- package/src/providers/services/{__tests__/EventServiceProvider.test.tsx → EventServiceProvider.test.tsx} +7 -7
- package/src/providers/services/{__tests__/InactivityServiceProvider.test.tsx → InactivityServiceProvider.test.tsx} +5 -5
- package/src/providers/services/{__tests__/OrganisationServiceProvider.test.tsx → OrganisationServiceProvider.test.tsx} +6 -6
- package/src/providers/services/UnifiedAuthContext.ts +30 -27
- package/src/providers/services/{__tests__/UnifiedAuthProvider.advanced.test.tsx → UnifiedAuthProvider.advanced.test.tsx} +8 -9
- package/src/providers/services/{__tests__/UnifiedAuthProvider.appId.test.tsx → UnifiedAuthProvider.appId.test.tsx} +25 -25
- package/src/providers/services/{__tests__/UnifiedAuthProvider.integration.test.tsx → UnifiedAuthProvider.integration.test.tsx} +14 -11
- package/src/providers/services/UnifiedAuthProvider.tsx +115 -360
- package/src/providers/services/{__tests__/contexts.test.tsx → contexts.test.tsx} +6 -6
- package/src/providers/services/{__tests__/useUnifiedAuth.test.tsx → useUnifiedAuth.test.tsx} +6 -6
- package/src/providers/services/useUnifiedAuthContextValue.ts +279 -0
- package/src/providers/useInactivity.test-helper.ts +27 -0
- package/src/rbac/{__tests__/adapters.comprehensive.test.tsx → adapters.comprehensive.test.tsx} +24 -24
- package/src/rbac/adapters.test.tsx +22 -22
- package/src/rbac/adapters.tsx +29 -29
- package/src/rbac/api.test.ts +973 -42
- package/src/rbac/api.ts +228 -253
- package/src/rbac/{__tests__/audit-batched.test.ts → audit-batched.test.ts} +6 -6
- package/src/rbac/audit.ts +4 -1
- package/src/rbac/{__tests__/auth-rbac-security.integration.test.tsx → auth-rbac-security.integration.test.tsx} +1 -1
- package/src/rbac/{__tests__/auth-rbac.e2e.test.tsx → auth-rbac.e2e.test.tsx} +27 -34
- package/src/rbac/cache-invalidation.test.ts +715 -0
- package/src/rbac/components/{__tests__/AccessDenied.test.tsx → AccessDenied.test.tsx} +3 -3
- package/src/rbac/components/{__tests__/NavigationGuard.test.tsx → NavigationGuard.test.tsx} +13 -11
- package/src/{__tests__/rbac/PagePermissionGuard.test.tsx → rbac/components/PagePermissionGuard.guard.test.tsx} +33 -19
- package/src/rbac/components/{__tests__/PagePermissionGuard.performance.test.tsx → PagePermissionGuard.performance.test.tsx} +30 -9
- package/src/rbac/components/{__tests__/PagePermissionGuard.race-condition.test.tsx → PagePermissionGuard.race-condition.test.tsx} +7 -7
- package/src/rbac/components/{__tests__/PagePermissionGuard.test.tsx → PagePermissionGuard.test.tsx} +10 -10
- package/src/rbac/components/PagePermissionGuard.tsx +177 -372
- package/src/rbac/components/{__tests__/PagePermissionGuard.verification.test.tsx → PagePermissionGuard.verification.test.tsx} +7 -7
- package/src/rbac/config.ts +58 -18
- package/src/rbac/{__tests__/engine.comprehensive.test.ts → engine.comprehensive.test.ts} +3 -3
- package/src/rbac/engine.test.ts +494 -0
- package/src/rbac/errors.ts +89 -55
- package/src/rbac/hooks/permissions/runPermissionCheck.ts +77 -0
- package/src/rbac/hooks/permissions/{__tests__/useAccessLevel.test.ts → useAccessLevel.test.ts} +40 -40
- package/src/rbac/hooks/permissions/useAccessLevel.ts +16 -6
- package/src/rbac/hooks/permissions/{__tests__/useCan.test.ts → useCan.test.ts} +41 -41
- package/src/rbac/hooks/permissions/useCan.ts +170 -252
- package/src/rbac/hooks/permissions/{__tests__/useMultiplePermissions.test.ts → useMultiplePermissions.test.ts} +49 -49
- package/src/rbac/hooks/permissions/useMultiplePermissions.ts +6 -2
- package/src/rbac/hooks/permissions/{__tests__/usePermissions.test.ts → usePermissions.test.ts} +10 -12
- package/src/rbac/hooks/permissions/usePermissions.ts +36 -65
- package/src/rbac/hooks/useCan.test.ts +42 -42
- package/src/rbac/hooks/usePageAccessLogging.ts +160 -0
- package/src/rbac/hooks/usePageGuardScope.ts +117 -0
- package/src/rbac/hooks/usePagePermissionCheck.ts +67 -0
- package/src/rbac/hooks/{__tests__/usePermissions.integration.test.ts → usePermissions.integration.test.ts} +9 -9
- package/src/{__tests__/hooks/usePermissions.test.ts → rbac/hooks/usePermissions.stability.test.ts} +18 -18
- package/src/rbac/hooks/usePermissions.test.ts +54 -54
- package/src/rbac/hooks/useRBAC.test.ts +313 -217
- package/src/rbac/hooks/useRBAC.ts +145 -81
- package/src/rbac/hooks/useResourcePermissions.test.ts +25 -25
- package/src/rbac/hooks/useResourcePermissions.ts +68 -134
- package/src/rbac/hooks/useResourcePermissionsSuperAdmin.ts +67 -0
- package/src/rbac/hooks/useRoleManagement.test.ts +27 -112
- package/src/rbac/hooks/useRoleManagement.ts +153 -585
- package/src/rbac/hooks/{__tests__/useSecureSupabase.test.ts → useSecureSupabase.test.ts} +17 -17
- package/src/rbac/hooks/useSecureSupabase.ts +10 -2
- package/src/rbac/hooks/useSuperAdminCheck.ts +80 -0
- package/src/rbac/{__tests__/performance.test.ts → performance.test.ts} +1 -1
- package/src/rbac/{__tests__/rbac-core.test.tsx → rbac-core.test.tsx} +3 -3
- package/src/rbac/{__tests__/rbac-engine-core-logic.test.ts → rbac-engine-core-logic.test.ts} +2 -2
- package/src/rbac/{__tests__/rbac-engine-simplified.test.ts → rbac-engine-simplified.test.ts} +3 -3
- package/src/rbac/{__tests__/rbac-functions.test.ts → rbac-functions.test.ts} +57 -0
- package/src/rbac/{__tests__/rbac-role-isolation.test.ts → rbac-role-isolation.test.ts} +2 -2
- package/src/rbac/request-deduplication.test.ts +14 -9
- package/src/rbac/request-deduplication.ts +5 -4
- package/src/rbac/{__tests__/scenarios.user-role.test.tsx → scenarios.user-role.test.tsx} +23 -23
- package/src/rbac/secureClient.test.ts +514 -83
- package/src/rbac/secureClient.ts +8 -2
- package/src/rbac/security.test.ts +323 -0
- package/src/rbac/types/roleManagement.ts +66 -0
- package/src/rbac/utils/{__tests__/clientSecurity.test.ts → clientSecurity.test.ts} +4 -4
- package/src/rbac/utils/{__tests__/contextValidator.test.ts → contextValidator.test.ts} +4 -4
- package/src/rbac/utils/contextValidator.ts +5 -1
- package/src/rbac/utils/{__tests__/deep-equal.test.ts → deep-equal.test.ts} +1 -1
- package/src/rbac/utils/{__tests__/eventContext.test.ts → eventContext.test.ts} +36 -21
- package/src/rbac/utils/eventContext.ts +37 -33
- package/src/rbac/utils/fetchPermissionMap.ts +13 -0
- package/src/rbac/utils/permissionMapHelpers.ts +34 -0
- package/src/rbac/utils/roleManagementRpc.ts +303 -0
- package/src/services/{__tests__/AuthService.edge-cases.test.ts → AuthService.edge-cases.test.ts} +19 -19
- package/src/services/{__tests__/AuthService.restoreSession.test.ts → AuthService.restoreSession.test.ts} +2 -2
- package/src/services/{__tests__/AuthService.test.ts → AuthService.test.ts} +89 -55
- package/src/services/AuthService.ts +184 -205
- package/src/services/{__tests__/BaseService.edge-cases.test.ts → BaseService.edge-cases.test.ts} +3 -3
- package/src/services/{__tests__/BaseService.test.ts → BaseService.test.ts} +2 -2
- package/src/services/{__tests__/EventService.edge-cases.test.ts → EventService.edge-cases.test.ts} +27 -24
- package/src/services/{__tests__/EventService.eventColours.test.ts → EventService.eventColours.test.ts} +1 -1
- package/src/services/{__tests__/EventService.test.ts → EventService.test.ts} +256 -24
- package/src/services/EventService.ts +242 -312
- package/src/services/{__tests__/InactivityService.edge-cases.test.ts → InactivityService.edge-cases.test.ts} +3 -3
- package/src/services/{__tests__/InactivityService.lifecycle.test.ts → InactivityService.lifecycle.test.ts} +2 -2
- package/src/services/{__tests__/InactivityService.test.ts → InactivityService.test.ts} +179 -4
- package/src/services/InactivityService.ts +172 -213
- package/src/services/{__tests__/OrganisationService.edge-cases.test.ts → OrganisationService.edge-cases.test.ts} +5 -5
- package/src/services/{__tests__/OrganisationService.pagination.test.ts → OrganisationService.pagination.test.ts} +4 -4
- package/src/services/{__tests__/OrganisationService.test.ts → OrganisationService.test.ts} +410 -7
- package/src/services/OrganisationService.ts +184 -238
- package/src/services/base/BaseService.test.ts +1 -1
- package/src/services/interfaces/{__tests__/IAuthService.test.ts → IAuthService.test.ts} +21 -27
- package/src/services/interfaces/IAuthService.ts +10 -9
- package/src/services/interfaces/{__tests__/IEventService.test.ts → IEventService.test.ts} +4 -4
- package/src/services/interfaces/{__tests__/IInactivityService.test.ts → IInactivityService.test.ts} +3 -3
- package/src/services/interfaces/{__tests__/IOrganisationService.test.ts → IOrganisationService.test.ts} +3 -3
- package/src/styles/core.css +243 -12
- package/src/theming/{__tests__/parseEventColours.test.ts → parseEventColours.test.ts} +1 -1
- package/src/theming/{__tests__/runtime.test.ts → runtime.test.ts} +8 -17
- package/src/theming/runtime.ts +71 -2
- package/src/types/api-result.ts +53 -0
- package/src/types/{__tests__/core.test.ts → core.test.ts} +2 -2
- package/src/types/{__tests__/database-generated.test.ts → database-generated.test.ts} +3 -3
- package/src/types/database.generated.ts +45 -10
- package/src/types/event.ts +38 -18
- package/src/types/{__tests__/file-reference.test.ts → file-reference.test.ts} +13 -13
- package/src/types/file-reference.ts +37 -12
- package/src/types/{__tests__/guards.test.ts → guards.test.ts} +2 -2
- package/src/types/{__tests__/index.test.ts → index.test.ts} +2 -2
- package/src/types/index.ts +3 -0
- package/src/types/{__tests__/organisation.roles.test.ts → organisation.roles.test.ts} +1 -1
- package/src/types/{__tests__/organisation.test.ts → organisation.test.ts} +3 -31
- package/src/types/organisation.ts +15 -15
- package/src/types/supabase.ts +13 -4
- package/src/types/{__tests__/theme.test.ts → theme.test.ts} +1 -1
- package/src/types/{__tests__/type-validation.test.ts → type-validation.test.ts} +1 -1
- package/src/types/{__tests__/validation.test.ts → validation.test.ts} +2 -2
- package/src/utils/app/appIdResolver.test.ts +98 -71
- package/src/utils/app/appIdResolver.ts +31 -20
- package/src/utils/{__tests__/appConfig.unit.test.ts → appConfig.unit.test.ts} +1 -1
- package/src/utils/{__tests__/audit.unit.test.ts → audit.unit.test.ts} +1 -1
- package/src/utils/{__tests__/auth-utils.unit.test.ts → auth-utils.unit.test.ts} +16 -17
- package/src/utils/{__tests__/bundleAnalysis.unit.test.ts → bundleAnalysis.unit.test.ts} +35 -35
- package/src/utils/{__tests__/cn.unit.test.ts → cn.unit.test.ts} +1 -1
- package/src/utils/context/organisationContext.test.ts +105 -91
- package/src/utils/context/organisationContext.ts +29 -40
- package/src/utils/core/{__tests__/cn.test.ts → cn.test.ts} +3 -3
- package/src/utils/core/{__tests__/debugLogger.test.ts → debugLogger.test.ts} +2 -2
- package/src/utils/core/{__tests__/logger.test.ts → logger.test.ts} +2 -2
- package/src/utils/core/mergeRefs.ts +24 -0
- package/src/utils/{__tests__/debugLogger.test.ts → debugLogger.test.ts} +1 -1
- package/src/utils/{__tests__/deviceFingerprint.unit.test.ts → deviceFingerprint.unit.test.ts} +1 -1
- package/src/utils/dynamic/createLazyComponent.tsx +9 -1
- package/src/utils/dynamic/{__tests__/dynamicUtils.test.ts → dynamicUtils.test.ts} +2 -2
- package/src/utils/dynamic/{__tests__/lazyLoad.test.tsx → lazyLoad.test.tsx} +2 -2
- package/src/utils/{__tests__/dynamicUtils.unit.test.ts → dynamicUtils.unit.test.ts} +1 -1
- package/src/utils/file-reference/{__tests__/file-reference.test.ts → file-reference.test.ts} +214 -289
- package/src/utils/file-reference/index.ts +330 -347
- package/src/utils/{__tests__/formatDate.unit.test.ts → formatDate.unit.test.ts} +2 -2
- package/src/utils/formatting/formatDateTimeTimezone.test.ts +1 -1
- package/src/utils/formatting/formatNumber.test.ts +1 -1
- package/src/utils/{__tests__/formatting.unit.test.ts → formatting.unit.test.ts} +1 -1
- package/src/utils/google-places/googlePlacesUtils.test.ts +70 -48
- package/src/utils/google-places/googlePlacesUtils.ts +67 -99
- package/src/utils/google-places/loadGoogleMapsScript.test.ts +25 -22
- package/src/utils/google-places/loadGoogleMapsScript.ts +138 -117
- package/src/utils/{__tests__/index.unit.test.ts → index.unit.test.ts} +1 -1
- package/src/utils/{__tests__/lazyLoad.unit.test.tsx → lazyLoad.unit.test.tsx} +13 -14
- package/src/utils/location/location.test.ts +1 -1
- package/src/utils/{__tests__/logger.unit.test.ts → logger.unit.test.ts} +1 -1
- package/src/utils/{__tests__/organisationContext.unit.test.ts → organisationContext.unit.test.ts} +37 -48
- package/src/utils/performance/{__tests__/bundleAnalysis.test.ts → bundleAnalysis.test.ts} +2 -2
- package/src/utils/performance/{__tests__/performanceBenchmark.test.ts → performanceBenchmark.test.ts} +2 -2
- package/src/utils/performance/{__tests__/performanceBudgets.test.ts → performanceBudgets.test.ts} +2 -2
- package/src/utils/{__tests__/performanceBenchmark.test.ts → performanceBenchmark.test.ts} +2 -2
- package/src/utils/{__tests__/performanceBudgets.unit.test.ts → performanceBudgets.unit.test.ts} +2 -2
- package/src/utils/{__tests__/permissionTypes.unit.test.ts → permissionTypes.unit.test.ts} +1 -1
- package/src/utils/{__tests__/permissionUtils.unit.test.ts → permissionUtils.unit.test.ts} +1 -1
- package/src/utils/permissions/{__tests__/permissionTypes.test.ts → permissionTypes.test.ts} +2 -2
- package/src/utils/persistence/{__tests__/keyDerivation.test.ts → keyDerivation.test.ts} +2 -2
- package/src/utils/persistence/{__tests__/sensitiveFieldDetection.test.ts → sensitiveFieldDetection.test.ts} +2 -2
- package/src/utils/{__tests__/request-deduplication.test.ts → request-deduplication.test.ts} +2 -2
- package/src/utils/{__tests__/sanitization.unit.test.ts → sanitization.unit.test.ts} +1 -1
- package/src/utils/{__tests__/schemaUtils.unit.test.ts → schemaUtils.unit.test.ts} +1 -1
- package/src/utils/{__tests__/secureDataAccess.unit.test.ts → secureDataAccess.unit.test.ts} +2 -2
- package/src/utils/{__tests__/secureErrors.unit.test.ts → secureErrors.unit.test.ts} +4 -4
- package/src/utils/{__tests__/secureStorage.unit.test.ts → secureStorage.unit.test.ts} +1 -1
- package/src/utils/security/auth-utils.ts +34 -23
- package/src/utils/security/secureDataAccess.ts +241 -281
- package/src/utils/security/secureErrors.test.ts +1 -1
- package/src/utils/security/secureStorage.test.ts +1 -1
- package/src/utils/security/security.test.ts +25 -17
- package/src/utils/security/security.ts +15 -18
- package/src/utils/security/securityMonitor.test.ts +1 -1
- package/src/utils/{__tests__/security.unit.test.ts → security.unit.test.ts} +21 -15
- package/src/utils/{__tests__/securityMonitor.unit.test.ts → securityMonitor.unit.test.ts} +1 -1
- package/src/utils/{__tests__/sessionTracking.unit.test.ts → sessionTracking.unit.test.ts} +12 -12
- package/src/utils/storage/{__tests__/config.unit.test.ts → config.unit.test.ts} +2 -2
- package/src/utils/storage/helpers.test.ts +88 -102
- package/src/utils/storage/helpers.ts +173 -251
- package/src/utils/storage/{__tests__/index.unit.test.ts → index.unit.test.ts} +3 -3
- package/src/utils/storage/types.ts +7 -0
- package/src/utils/supabase/createBaseClient.test.ts +1 -1
- package/src/utils/timezone/timezone.test.ts +1 -1
- package/src/utils/{__tests__/timezone.test.ts → timezone.test.ts} +2 -2
- package/src/utils/validation/{__tests__/common.test.ts → common.test.ts} +2 -2
- package/src/utils/validation/{__tests__/csrf.test.ts → csrf.test.ts} +56 -28
- package/src/utils/validation/csrf.ts +42 -41
- package/src/utils/validation/{__tests__/htmlSanitization.unit.test.ts → htmlSanitization.unit.test.ts} +2 -2
- package/src/utils/validation/{__tests__/passwordSchema.test.ts → passwordSchema.test.ts} +2 -2
- package/src/utils/validation/{__tests__/schema.test.ts → schema.test.ts} +2 -2
- package/src/utils/validation/{__tests__/sqlInjectionProtection.test.ts → sqlInjectionProtection.test.ts} +2 -2
- package/src/utils/validation/{__tests__/user.test.ts → user.test.ts} +2 -2
- package/src/utils/validation/{__tests__/validation.test.ts → validation.test.ts} +2 -2
- package/src/utils/validation/{__tests__/validationUtils.test.ts → validationUtils.test.ts} +2 -2
- package/src/utils/{__tests__/validation.unit.test.ts → validation.unit.test.ts} +1 -1
- package/src/utils/{__tests__/validationUtils.unit.test.ts → validationUtils.unit.test.ts} +5 -2
- package/dist/UnifiedAuthProvider-BBD2PS3Q.js +0 -7
- package/dist/chunk-KPYQWGFQ.js +0 -183
- package/dist/types-D05dCGma.d.ts +0 -521
- package/scripts/eslint-audit.cjs +0 -222
- package/scripts/generate-docs.js +0 -157
- package/scripts/install-cursor-rules.cjs +0 -255
- package/scripts/install-eslint-config.cjs +0 -349
- package/scripts/setup-build-cache.js +0 -73
- package/scripts/validate-pre-publish.js +0 -145
- package/src/__tests__/integration/UserProfile.test.tsx +0 -124
- package/src/__tests__/public-recipe-view.test.ts +0 -228
- package/src/__tests__/rls-policies.test.ts +0 -472
- package/src/components/DataTable/__tests__/DataTable.test.tsx +0 -876
- package/src/components/DataTable/components/DataTableLayout.tsx +0 -584
- package/src/components/DataTable/components/UnifiedTableBody.tsx +0 -395
- package/src/components/DataTable/components/__tests__/DataTableLayout.test.tsx +0 -467
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +0 -358
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +0 -957
- package/src/components/DataTable/core/ActionManager.ts +0 -235
- package/src/components/DataTable/core/ColumnManager.ts +0 -204
- package/src/components/DataTable/core/DataManager.ts +0 -190
- package/src/components/DataTable/core/LocalDataAdapter.ts +0 -274
- package/src/components/DataTable/core/PluginRegistry.ts +0 -229
- package/src/components/DataTable/core/StateManager.ts +0 -312
- package/src/components/DataTable/core/__tests__/ActionManager.test.ts +0 -235
- package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +0 -141
- package/src/components/DataTable/core/__tests__/DataManager.test.ts +0 -178
- package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +0 -133
- package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +0 -142
- package/src/components/DataTable/core/__tests__/StateManager.test.ts +0 -158
- package/src/components/DataTable/core/interfaces.ts +0 -338
- package/src/components/DataTable/types.ts +0 -764
- package/src/hooks/public/usePublicFileDisplay.ts +0 -534
- package/src/hooks/useFileDisplay.ts +0 -748
- package/src/providers/OrganisationProvider.test.tsx +0 -40
- package/src/providers/OrganisationProvider.tsx +0 -92
- package/src/providers/__tests__/InactivityProvider.test-helper.tsx +0 -65
- package/src/providers/__tests__/OrganisationProvider.test.tsx +0 -616
- package/src/providers/__tests__/OrganisationProvider.wrapper.test.tsx +0 -591
- package/src/rbac/__tests__/cache-invalidation.test.ts +0 -393
- /package/src/components/DataTable/{components/__tests__ → ui}/COVERAGE_NOTE.md +0 -0
- /package/src/components/DataTable/utils/{__tests__/COVERAGE_NOTE.md → COVERAGE_NOTE.md} +0 -0
- /package/src/hooks/{__tests__/useApiFetch.unit.test.ts → useApiFetch.unit.test.ts} +0 -0
- /package/src/providers/{__tests__/README.md → README.md} +0 -0
- /package/src/rbac/{__tests__/index.test.ts → index.test.ts} +0 -0
- /package/src/rbac/{__tests__/rbac-integration.test.ts → rbac-integration.test.ts} +0 -0
- /package/src/types/{__tests__/README.md → README.md} +0 -0
|
@@ -0,0 +1,1352 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file DataTableLayout Component Tests
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Components/DataTable/Components
|
|
5
|
+
* @since 0.1.0
|
|
6
|
+
*
|
|
7
|
+
* Comprehensive tests for DataTableLayout component covering rendering, export functionality,
|
|
8
|
+
* toolbar integration, and user interactions.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import { render, screen, renderHook, waitFor } from '@testing-library/react';
|
|
13
|
+
import userEvent from '@testing-library/user-event';
|
|
14
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
15
|
+
import { useReactTable, getCoreRowModel, getFilteredRowModel } from '@tanstack/react-table';
|
|
16
|
+
import { DataTableLayout } from './DataTableLayout';
|
|
17
|
+
import type { DataRecord, DataTableColumn, NormalizedDataTableFeatureConfig } from '../../types';
|
|
18
|
+
import { createTestData, createTestColumns } from '../../test-utils/dataFactories';
|
|
19
|
+
|
|
20
|
+
// Mock dependencies
|
|
21
|
+
vi.mock('../toolbar/DataTableToolbar', () => ({
|
|
22
|
+
DataTableToolbar: ({ onCreateRow, onExport, onDeleteSelected, rowSelection }: any) => {
|
|
23
|
+
const selectedCount = Object.values(rowSelection || {}).filter(Boolean).length;
|
|
24
|
+
const handleDeleteClick = () => {
|
|
25
|
+
// Call handler even if disabled to test toast logic
|
|
26
|
+
// This allows testing the wrapped handler's toast logic
|
|
27
|
+
if (onDeleteSelected) {
|
|
28
|
+
onDeleteSelected(rowSelection || {});
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
return (
|
|
32
|
+
<div data-testid="data-table-toolbar">
|
|
33
|
+
{onCreateRow && <button onClick={onCreateRow} data-testid="create-row-btn">Create</button>}
|
|
34
|
+
{onExport && <button onClick={onExport} data-testid="export-btn">Export</button>}
|
|
35
|
+
{onDeleteSelected && (
|
|
36
|
+
<button
|
|
37
|
+
onClick={handleDeleteClick}
|
|
38
|
+
data-testid="delete-selected-btn"
|
|
39
|
+
disabled={selectedCount === 0}
|
|
40
|
+
// Store handler reference for testing
|
|
41
|
+
data-handler={onDeleteSelected ? 'present' : 'missing'}
|
|
42
|
+
>
|
|
43
|
+
Delete Selected
|
|
44
|
+
</button>
|
|
45
|
+
)}
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
},
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
vi.mock('../table/UnifiedTableBody', () => ({
|
|
52
|
+
UnifiedTableBody: ({ isCreating, editingRowId }: any) => (
|
|
53
|
+
<tbody data-testid="unified-table-body">
|
|
54
|
+
{isCreating && <tr data-testid="creating-row"><td>Creating...</td></tr>}
|
|
55
|
+
{editingRowId && <tr data-testid="editing-row"><td>Editing {editingRowId}</td></tr>}
|
|
56
|
+
<tr data-testid="data-row"><td>Data Row</td></tr>
|
|
57
|
+
</tbody>
|
|
58
|
+
),
|
|
59
|
+
}));
|
|
60
|
+
|
|
61
|
+
vi.mock('../shared/PaginationControls', () => ({
|
|
62
|
+
PaginationControls: ({ _table, totalCount }: any) => (
|
|
63
|
+
<div data-testid="pagination-controls">Pagination: {totalCount} items</div>
|
|
64
|
+
),
|
|
65
|
+
EnhancedPaginationControls: ({ _table, totalCount }: any) => (
|
|
66
|
+
<div data-testid="enhanced-pagination-controls">Enhanced Pagination: {totalCount} items</div>
|
|
67
|
+
),
|
|
68
|
+
}));
|
|
69
|
+
|
|
70
|
+
vi.mock('../modals/BulkDeleteConfirmDialog', () => ({
|
|
71
|
+
BulkDeleteConfirmDialog: ({ open, selectedCount, onConfirm, onCancel }: any) =>
|
|
72
|
+
open ? (
|
|
73
|
+
<div data-testid="bulk-delete-confirm-dialog" data-selected-count={selectedCount}>
|
|
74
|
+
<button onClick={onCancel} data-testid="bulk-delete-cancel">Cancel</button>
|
|
75
|
+
<button onClick={onConfirm} data-testid="bulk-delete-confirm">Delete</button>
|
|
76
|
+
</div>
|
|
77
|
+
) : null,
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
vi.mock('../modals/DataTableModals', () => ({
|
|
81
|
+
DataTableModals: ({ showImportModal, onCloseImportModal, onImport }: any) => {
|
|
82
|
+
const [lastImportResult, setLastImportResult] = React.useState<unknown>(null);
|
|
83
|
+
const handleImport = async () => {
|
|
84
|
+
if (onImport) {
|
|
85
|
+
try {
|
|
86
|
+
const result = await onImport([{ id: '1', name: 'Imported' }]);
|
|
87
|
+
setLastImportResult(result);
|
|
88
|
+
} catch (_error) {
|
|
89
|
+
// Catch errors to prevent unhandled rejections in tests
|
|
90
|
+
// The error is already handled by DataTableLayout wrapper which shows toast
|
|
91
|
+
// then re-throws for ImportModal to handle
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div data-testid="data-table-modals">
|
|
98
|
+
{showImportModal && (
|
|
99
|
+
<div data-testid="import-modal">
|
|
100
|
+
{lastImportResult != null && typeof lastImportResult === 'object' && 'successCount' in lastImportResult && 'totalCount' in lastImportResult && 'failedCount' in lastImportResult && (
|
|
101
|
+
<div
|
|
102
|
+
data-testid="import-result"
|
|
103
|
+
data-success-count={String((lastImportResult as { successCount: number }).successCount)}
|
|
104
|
+
data-total-count={String((lastImportResult as { totalCount: number }).totalCount)}
|
|
105
|
+
data-failed-count={String((lastImportResult as { failedCount: number }).failedCount)}
|
|
106
|
+
/>
|
|
107
|
+
)}
|
|
108
|
+
<button onClick={onCloseImportModal} data-testid="close-import-modal">Close</button>
|
|
109
|
+
<button
|
|
110
|
+
onClick={handleImport}
|
|
111
|
+
data-testid="confirm-import"
|
|
112
|
+
>
|
|
113
|
+
Import
|
|
114
|
+
</button>
|
|
115
|
+
</div>
|
|
116
|
+
)}
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
},
|
|
120
|
+
}));
|
|
121
|
+
|
|
122
|
+
const mockToast = vi.hoisted(() => vi.fn());
|
|
123
|
+
vi.mock('../../../hooks/useToast', () => ({
|
|
124
|
+
toast: mockToast,
|
|
125
|
+
}));
|
|
126
|
+
|
|
127
|
+
const mockExportToCSV = vi.fn().mockResolvedValue(undefined);
|
|
128
|
+
vi.mock('../../utils/exportUtils', () => ({
|
|
129
|
+
exportToCSVWithTableRows: (...args: any[]) => mockExportToCSV(...args),
|
|
130
|
+
}));
|
|
131
|
+
|
|
132
|
+
vi.mock('../../utils/a11yUtils', () => ({
|
|
133
|
+
announceSortChange: vi.fn(),
|
|
134
|
+
getAriaSortState: vi.fn(() => 'none'),
|
|
135
|
+
}));
|
|
136
|
+
|
|
137
|
+
vi.mock('../../styles', () => ({
|
|
138
|
+
getTableClasses: vi.fn(() => 'table-classes'),
|
|
139
|
+
}));
|
|
140
|
+
|
|
141
|
+
const mockLogger = {
|
|
142
|
+
debug: vi.fn(),
|
|
143
|
+
info: vi.fn(),
|
|
144
|
+
warn: vi.fn(),
|
|
145
|
+
error: vi.fn(),
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
vi.mock('../../../utils/core/logger', () => ({
|
|
149
|
+
createLogger: () => mockLogger,
|
|
150
|
+
}));
|
|
151
|
+
|
|
152
|
+
interface TestData extends DataRecord {
|
|
153
|
+
id: string;
|
|
154
|
+
name: string;
|
|
155
|
+
age: number;
|
|
156
|
+
status: string;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
describe('DataTableLayout', () => {
|
|
160
|
+
const testData = createTestData(5) as TestData[];
|
|
161
|
+
const testColumns = createTestColumns() as DataTableColumn<TestData>[];
|
|
162
|
+
|
|
163
|
+
const _createMockTableForExport = (table: any) => {
|
|
164
|
+
const rowModel = table.getRowModel?.() || { rows: [] };
|
|
165
|
+
|
|
166
|
+
// Always ensure methods exist and return arrays
|
|
167
|
+
const mockColumns = testColumns.map((col) => ({
|
|
168
|
+
id: col.id || col.accessorKey,
|
|
169
|
+
getIsVisible: () => true,
|
|
170
|
+
columnDef: { header: col.header, accessorKey: col.accessorKey },
|
|
171
|
+
getToggleSortingHandler: () => undefined,
|
|
172
|
+
getCanSort: () => true,
|
|
173
|
+
getLeafColumns: () => [],
|
|
174
|
+
getIsSorted: () => false,
|
|
175
|
+
getSortingFn: () => undefined,
|
|
176
|
+
toggleSorting: () => {},
|
|
177
|
+
}));
|
|
178
|
+
|
|
179
|
+
// Create a new table object with all required methods
|
|
180
|
+
const mockTable = {
|
|
181
|
+
...table,
|
|
182
|
+
getFilteredRowModel: () => ({
|
|
183
|
+
rows: rowModel?.rows || [],
|
|
184
|
+
}),
|
|
185
|
+
getAllColumns: () => mockColumns,
|
|
186
|
+
getVisibleFlatColumns: () => mockColumns,
|
|
187
|
+
getHeaderGroups: table.getHeaderGroups || (() => []),
|
|
188
|
+
getRowModel: table.getRowModel || (() => ({ rows: [] })),
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
return mockTable;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const getDefaultProps = () => {
|
|
195
|
+
const { result } = renderHook(() =>
|
|
196
|
+
useReactTable({
|
|
197
|
+
data: testData,
|
|
198
|
+
columns: testColumns.map((col) => ({
|
|
199
|
+
id: col.id || col.accessorKey,
|
|
200
|
+
accessorKey: col.accessorKey,
|
|
201
|
+
header: col.header,
|
|
202
|
+
})),
|
|
203
|
+
getCoreRowModel: getCoreRowModel(),
|
|
204
|
+
getFilteredRowModel: getFilteredRowModel(),
|
|
205
|
+
})
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
const table = result.current;
|
|
209
|
+
// Ensure table methods return proper structures
|
|
210
|
+
const rowModel = table.getRowModel();
|
|
211
|
+
if (!table.getFilteredRowModel) {
|
|
212
|
+
(table as any).getFilteredRowModel = () => rowModel;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
table,
|
|
217
|
+
title: 'Test Table',
|
|
218
|
+
description: 'Test Description',
|
|
219
|
+
variant: 'default' as const,
|
|
220
|
+
columns: testColumns,
|
|
221
|
+
secureFeatures: {
|
|
222
|
+
search: true,
|
|
223
|
+
grouping: true,
|
|
224
|
+
columnVisibility: true,
|
|
225
|
+
filtering: true,
|
|
226
|
+
creation: true,
|
|
227
|
+
import: true,
|
|
228
|
+
export: true,
|
|
229
|
+
selection: true,
|
|
230
|
+
deletion: true,
|
|
231
|
+
deleteSelected: true,
|
|
232
|
+
pagination: true,
|
|
233
|
+
hierarchical: false,
|
|
234
|
+
} as NormalizedDataTableFeatureConfig,
|
|
235
|
+
enhancedPagination: false,
|
|
236
|
+
searchQuery: '',
|
|
237
|
+
onSearch: vi.fn(),
|
|
238
|
+
state: {
|
|
239
|
+
editingRowId: null,
|
|
240
|
+
editingData: {},
|
|
241
|
+
isCreating: false,
|
|
242
|
+
creationData: {},
|
|
243
|
+
grouping: [],
|
|
244
|
+
columnVisibility: {},
|
|
245
|
+
columnFilters: [],
|
|
246
|
+
showFilterRow: false,
|
|
247
|
+
showImportModal: false,
|
|
248
|
+
},
|
|
249
|
+
stateActions: {
|
|
250
|
+
setEditingRow: vi.fn(),
|
|
251
|
+
clearEditing: vi.fn(),
|
|
252
|
+
setCreating: vi.fn(),
|
|
253
|
+
clearCreationData: vi.fn(),
|
|
254
|
+
setGrouping: vi.fn(),
|
|
255
|
+
setColumnVisibility: vi.fn(),
|
|
256
|
+
setColumnFilters: vi.fn(),
|
|
257
|
+
setFilterRow: vi.fn(),
|
|
258
|
+
setImportModal: vi.fn(),
|
|
259
|
+
clearRowSelection: vi.fn(),
|
|
260
|
+
},
|
|
261
|
+
rowSelection: {},
|
|
262
|
+
rbac: { pageId: 'test-page' },
|
|
263
|
+
permissions: {
|
|
264
|
+
canCreate: { can: true, isLoading: false },
|
|
265
|
+
canImport: { can: true, isLoading: false },
|
|
266
|
+
canExport: { can: true, isLoading: false },
|
|
267
|
+
canDelete: { can: true, isLoading: false },
|
|
268
|
+
},
|
|
269
|
+
effectiveActions: [],
|
|
270
|
+
finalPageSizeOptions: [10, 25, 50, 100],
|
|
271
|
+
finalPaginationMode: 'client' as const,
|
|
272
|
+
finalDataCount: testData.length,
|
|
273
|
+
isLoading: false,
|
|
274
|
+
finalTableData: testData,
|
|
275
|
+
aggregates: [],
|
|
276
|
+
resolvedGetRowId: (row: TestData) => row.id,
|
|
277
|
+
data: testData,
|
|
278
|
+
virtualHeight: 400,
|
|
279
|
+
hierarchicalState: undefined,
|
|
280
|
+
logger: mockLogger,
|
|
281
|
+
secureHandlers: {
|
|
282
|
+
onCreateRow: vi.fn(),
|
|
283
|
+
onEditRow: vi.fn(),
|
|
284
|
+
onDeleteRow: vi.fn(),
|
|
285
|
+
onImport: vi.fn(),
|
|
286
|
+
onExport: vi.fn(),
|
|
287
|
+
onDeleteSelected: vi.fn(),
|
|
288
|
+
},
|
|
289
|
+
keyboardNavigation: {
|
|
290
|
+
getHeaderKeyboardHandlers: vi.fn(() => ({})),
|
|
291
|
+
},
|
|
292
|
+
lastFocusedElementRef: React.createRef<HTMLElement>(),
|
|
293
|
+
};
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
beforeEach(() => {
|
|
297
|
+
vi.clearAllMocks();
|
|
298
|
+
mockExportToCSV.mockResolvedValue(undefined);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
afterEach(() => {
|
|
302
|
+
vi.clearAllMocks();
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
describe('Rendering', () => {
|
|
306
|
+
it('renders table with title and description', () => {
|
|
307
|
+
const defaultProps = getDefaultProps();
|
|
308
|
+
render(<DataTableLayout {...defaultProps} />);
|
|
309
|
+
|
|
310
|
+
expect(screen.getByText('Test Table')).toBeInTheDocument();
|
|
311
|
+
expect(screen.getByText('Test Description')).toBeInTheDocument();
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('renders table without title and description', () => {
|
|
315
|
+
const defaultProps = getDefaultProps();
|
|
316
|
+
render(<DataTableLayout {...defaultProps} title={undefined} description={undefined} />);
|
|
317
|
+
|
|
318
|
+
expect(screen.queryByText('Test Table')).not.toBeInTheDocument();
|
|
319
|
+
expect(screen.queryByText('Test Description')).not.toBeInTheDocument();
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('renders toolbar component', () => {
|
|
323
|
+
const defaultProps = getDefaultProps();
|
|
324
|
+
render(<DataTableLayout {...defaultProps} />);
|
|
325
|
+
|
|
326
|
+
expect(screen.getByTestId('data-table-toolbar')).toBeInTheDocument();
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it('renders table body', () => {
|
|
330
|
+
const defaultProps = getDefaultProps();
|
|
331
|
+
render(<DataTableLayout {...defaultProps} />);
|
|
332
|
+
|
|
333
|
+
expect(screen.getByTestId('unified-table-body')).toBeInTheDocument();
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('renders pagination when enabled', () => {
|
|
337
|
+
const defaultProps = getDefaultProps();
|
|
338
|
+
render(<DataTableLayout {...defaultProps} />);
|
|
339
|
+
|
|
340
|
+
expect(screen.getByTestId('pagination-controls')).toBeInTheDocument();
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('renders enhanced pagination when enabled', () => {
|
|
344
|
+
const defaultProps = getDefaultProps();
|
|
345
|
+
render(<DataTableLayout {...defaultProps} enhancedPagination={true} />);
|
|
346
|
+
|
|
347
|
+
expect(screen.getByTestId('enhanced-pagination-controls')).toBeInTheDocument();
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it('does not render pagination when disabled', () => {
|
|
351
|
+
const defaultProps = getDefaultProps();
|
|
352
|
+
const propsWithoutPagination = {
|
|
353
|
+
...defaultProps,
|
|
354
|
+
secureFeatures: {
|
|
355
|
+
...defaultProps.secureFeatures,
|
|
356
|
+
pagination: false,
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
render(<DataTableLayout {...propsWithoutPagination} />);
|
|
361
|
+
|
|
362
|
+
expect(screen.queryByTestId('pagination-controls')).not.toBeInTheDocument();
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
describe('Export Functionality', () => {
|
|
367
|
+
it('renders export button when export feature is enabled', () => {
|
|
368
|
+
const defaultProps = getDefaultProps();
|
|
369
|
+
render(<DataTableLayout {...defaultProps} />);
|
|
370
|
+
|
|
371
|
+
expect(screen.getByTestId('export-btn')).toBeInTheDocument();
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('passes onExport handler to toolbar', () => {
|
|
375
|
+
const defaultProps = getDefaultProps();
|
|
376
|
+
render(<DataTableLayout {...defaultProps} />);
|
|
377
|
+
|
|
378
|
+
// Verify export button exists (indicates handler is passed)
|
|
379
|
+
const exportButton = screen.getByTestId('export-btn');
|
|
380
|
+
expect(exportButton).toBeInTheDocument();
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
describe('Row Creation', () => {
|
|
385
|
+
it('handles create row action', async () => {
|
|
386
|
+
const user = userEvent.setup();
|
|
387
|
+
const defaultProps = getDefaultProps();
|
|
388
|
+
const props = {
|
|
389
|
+
...defaultProps,
|
|
390
|
+
state: {
|
|
391
|
+
...defaultProps.state,
|
|
392
|
+
isCreating: false,
|
|
393
|
+
},
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
render(<DataTableLayout {...props} />);
|
|
397
|
+
|
|
398
|
+
const createButton = screen.getByTestId('create-row-btn');
|
|
399
|
+
await user.click(createButton);
|
|
400
|
+
|
|
401
|
+
expect(props.stateActions.setCreating).toHaveBeenCalledWith(true);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it('handles save creation', async () => {
|
|
405
|
+
const onCreateRow = vi.fn();
|
|
406
|
+
const defaultProps = getDefaultProps();
|
|
407
|
+
const props = {
|
|
408
|
+
...defaultProps,
|
|
409
|
+
onCreateRow,
|
|
410
|
+
state: {
|
|
411
|
+
...defaultProps.state,
|
|
412
|
+
isCreating: true,
|
|
413
|
+
creationData: { name: 'New Row' },
|
|
414
|
+
},
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
render(<DataTableLayout {...props} />);
|
|
418
|
+
|
|
419
|
+
// The save creation is handled internally by UnifiedTableBody
|
|
420
|
+
// This test verifies the component structure supports creation
|
|
421
|
+
expect(screen.getByTestId('creating-row')).toBeInTheDocument();
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
describe('Row Editing', () => {
|
|
426
|
+
it('displays editing row when editingRowId is set', () => {
|
|
427
|
+
const defaultProps = getDefaultProps();
|
|
428
|
+
const props = {
|
|
429
|
+
...defaultProps,
|
|
430
|
+
state: {
|
|
431
|
+
...defaultProps.state,
|
|
432
|
+
editingRowId: 'row-1',
|
|
433
|
+
editingData: { name: 'Edited Name' },
|
|
434
|
+
},
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
render(<DataTableLayout {...props} />);
|
|
438
|
+
|
|
439
|
+
expect(screen.getByTestId('editing-row')).toBeInTheDocument();
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
describe('Delete Selected', () => {
|
|
444
|
+
it('renders delete selected button when handler is provided', () => {
|
|
445
|
+
const defaultProps = getDefaultProps();
|
|
446
|
+
render(<DataTableLayout {...defaultProps} />);
|
|
447
|
+
|
|
448
|
+
expect(screen.getByTestId('delete-selected-btn')).toBeInTheDocument();
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
describe('Import Modal', () => {
|
|
453
|
+
it('renders import modal when showImportModal is true', () => {
|
|
454
|
+
const defaultProps = getDefaultProps();
|
|
455
|
+
const props = {
|
|
456
|
+
...defaultProps,
|
|
457
|
+
state: {
|
|
458
|
+
...defaultProps.state,
|
|
459
|
+
showImportModal: true,
|
|
460
|
+
},
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
render(<DataTableLayout {...props} />);
|
|
464
|
+
|
|
465
|
+
expect(screen.getByTestId('import-modal')).toBeInTheDocument();
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
it('renders import modal with correct props', () => {
|
|
469
|
+
const defaultProps = getDefaultProps();
|
|
470
|
+
const props = {
|
|
471
|
+
...defaultProps,
|
|
472
|
+
onImport: vi.fn(),
|
|
473
|
+
state: {
|
|
474
|
+
...defaultProps.state,
|
|
475
|
+
showImportModal: true,
|
|
476
|
+
},
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
render(<DataTableLayout {...props} />);
|
|
480
|
+
|
|
481
|
+
expect(screen.getByTestId('import-modal')).toBeInTheDocument();
|
|
482
|
+
expect(screen.getByTestId('confirm-import')).toBeInTheDocument();
|
|
483
|
+
expect(screen.getByTestId('close-import-modal')).toBeInTheDocument();
|
|
484
|
+
});
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
describe('Accessibility', () => {
|
|
488
|
+
it('sets aria-label on table', () => {
|
|
489
|
+
const defaultProps = getDefaultProps();
|
|
490
|
+
render(<DataTableLayout {...defaultProps} />);
|
|
491
|
+
|
|
492
|
+
const table = screen.getByRole('table');
|
|
493
|
+
expect(table).toHaveAttribute('aria-label', 'Test Table');
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
it('sets aria-describedby when description is provided', () => {
|
|
497
|
+
const defaultProps = getDefaultProps();
|
|
498
|
+
render(<DataTableLayout {...defaultProps} />);
|
|
499
|
+
|
|
500
|
+
const table = screen.getByRole('table');
|
|
501
|
+
expect(table).toHaveAttribute('aria-describedby', 'table-description');
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
it('sets aria-busy when loading', () => {
|
|
505
|
+
const defaultProps = getDefaultProps();
|
|
506
|
+
render(<DataTableLayout {...defaultProps} isLoading={true} />);
|
|
507
|
+
|
|
508
|
+
const table = screen.getByRole('table');
|
|
509
|
+
expect(table).toHaveAttribute('aria-busy', 'true');
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
it('sets aria-busy to false when not loading', () => {
|
|
513
|
+
const defaultProps = getDefaultProps();
|
|
514
|
+
render(<DataTableLayout {...defaultProps} isLoading={false} />);
|
|
515
|
+
|
|
516
|
+
const table = screen.getByRole('table');
|
|
517
|
+
expect(table).toHaveAttribute('aria-busy', 'false');
|
|
518
|
+
});
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
describe('Export Functionality', () => {
|
|
522
|
+
it('renders export button and calls handler when clicked', async () => {
|
|
523
|
+
const user = userEvent.setup();
|
|
524
|
+
const defaultProps = getDefaultProps();
|
|
525
|
+
const onExport = vi.fn();
|
|
526
|
+
const props = {
|
|
527
|
+
...defaultProps,
|
|
528
|
+
secureHandlers: {
|
|
529
|
+
...defaultProps.secureHandlers,
|
|
530
|
+
onExport,
|
|
531
|
+
},
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
render(<DataTableLayout {...props} />);
|
|
535
|
+
|
|
536
|
+
const exportButton = screen.getByTestId('export-btn');
|
|
537
|
+
expect(exportButton).toBeInTheDocument();
|
|
538
|
+
|
|
539
|
+
await user.click(exportButton);
|
|
540
|
+
|
|
541
|
+
// When secureHandlers.onExport is provided, it should be called instead of exportToCSVWithTableRows
|
|
542
|
+
await waitFor(() => {
|
|
543
|
+
expect(onExport).toHaveBeenCalled();
|
|
544
|
+
}, { timeout: 3000 });
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
it('handles export with secureHandlers.onExport', async () => {
|
|
548
|
+
const user = userEvent.setup();
|
|
549
|
+
const onExport = vi.fn().mockResolvedValue(undefined);
|
|
550
|
+
const defaultProps = getDefaultProps();
|
|
551
|
+
const props = {
|
|
552
|
+
...defaultProps,
|
|
553
|
+
secureHandlers: {
|
|
554
|
+
...defaultProps.secureHandlers,
|
|
555
|
+
onExport,
|
|
556
|
+
},
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
render(<DataTableLayout {...props} />);
|
|
560
|
+
|
|
561
|
+
const exportButton = screen.getByTestId('export-btn');
|
|
562
|
+
await user.click(exportButton);
|
|
563
|
+
|
|
564
|
+
await waitFor(() => {
|
|
565
|
+
expect(onExport).toHaveBeenCalled();
|
|
566
|
+
});
|
|
567
|
+
expect(mockExportToCSV).not.toHaveBeenCalled();
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
it('handles export errors gracefully', async () => {
|
|
571
|
+
const user = userEvent.setup();
|
|
572
|
+
const defaultProps = getDefaultProps();
|
|
573
|
+
const onExport = vi.fn().mockRejectedValue(new Error('Export failed'));
|
|
574
|
+
const props = {
|
|
575
|
+
...defaultProps,
|
|
576
|
+
secureHandlers: {
|
|
577
|
+
...defaultProps.secureHandlers,
|
|
578
|
+
onExport,
|
|
579
|
+
},
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
render(<DataTableLayout {...props} />);
|
|
583
|
+
|
|
584
|
+
const exportButton = screen.getByTestId('export-btn');
|
|
585
|
+
await user.click(exportButton);
|
|
586
|
+
|
|
587
|
+
// Error handling is tested via secureHandlers.onExport
|
|
588
|
+
// The actual exportToCSV error handling requires complex table mocking
|
|
589
|
+
await waitFor(() => {
|
|
590
|
+
expect(onExport).toHaveBeenCalled();
|
|
591
|
+
}, { timeout: 3000 });
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
it('renders export button with title', () => {
|
|
595
|
+
const defaultProps = getDefaultProps();
|
|
596
|
+
render(<DataTableLayout {...defaultProps} title="Test Table" />);
|
|
597
|
+
|
|
598
|
+
// Verify export button is rendered
|
|
599
|
+
// Filename generation is tested in exportUtils tests
|
|
600
|
+
expect(screen.getByTestId('export-btn')).toBeInTheDocument();
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
it('handles export when title is not provided', async () => {
|
|
604
|
+
const user = userEvent.setup();
|
|
605
|
+
const defaultProps = getDefaultProps();
|
|
606
|
+
const onExport = vi.fn();
|
|
607
|
+
const props = {
|
|
608
|
+
...defaultProps,
|
|
609
|
+
secureHandlers: {
|
|
610
|
+
...defaultProps.secureHandlers,
|
|
611
|
+
onExport,
|
|
612
|
+
},
|
|
613
|
+
title: undefined,
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
render(<DataTableLayout {...props} />);
|
|
617
|
+
|
|
618
|
+
const exportButton = screen.getByTestId('export-btn');
|
|
619
|
+
await user.click(exportButton);
|
|
620
|
+
|
|
621
|
+
// Verify export handler is called (default filename generation is tested in exportUtils tests)
|
|
622
|
+
await waitFor(() => {
|
|
623
|
+
expect(onExport).toHaveBeenCalled();
|
|
624
|
+
}, { timeout: 3000 });
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
it('renders export button when export is enabled', () => {
|
|
628
|
+
const defaultProps = getDefaultProps();
|
|
629
|
+
render(<DataTableLayout {...defaultProps} />);
|
|
630
|
+
|
|
631
|
+
// System column filtering is tested in exportUtils tests
|
|
632
|
+
// This test verifies the export button is rendered
|
|
633
|
+
expect(screen.getByTestId('export-btn')).toBeInTheDocument();
|
|
634
|
+
});
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
describe('Delete Selected Functionality', () => {
|
|
638
|
+
it('opens confirmation dialog when delete button is clicked, and calls onDeleteSelected when Confirm is clicked', async () => {
|
|
639
|
+
const user = userEvent.setup();
|
|
640
|
+
const onDeleteSelected = vi.fn().mockResolvedValue(undefined);
|
|
641
|
+
const defaultProps = getDefaultProps();
|
|
642
|
+
const rowSelection = { 'row-1': true, 'row-2': true };
|
|
643
|
+
const props = {
|
|
644
|
+
...defaultProps,
|
|
645
|
+
secureHandlers: {
|
|
646
|
+
...defaultProps.secureHandlers,
|
|
647
|
+
onDeleteSelected,
|
|
648
|
+
},
|
|
649
|
+
rowSelection,
|
|
650
|
+
};
|
|
651
|
+
|
|
652
|
+
render(<DataTableLayout {...props} />);
|
|
653
|
+
|
|
654
|
+
const deleteButton = screen.getByTestId('delete-selected-btn');
|
|
655
|
+
await user.click(deleteButton);
|
|
656
|
+
|
|
657
|
+
await waitFor(() => {
|
|
658
|
+
expect(screen.getByTestId('bulk-delete-confirm-dialog')).toBeInTheDocument();
|
|
659
|
+
});
|
|
660
|
+
expect(screen.getByTestId('bulk-delete-confirm-dialog')).toHaveAttribute('data-selected-count', '2');
|
|
661
|
+
|
|
662
|
+
const confirmButton = screen.getByTestId('bulk-delete-confirm');
|
|
663
|
+
await user.click(confirmButton);
|
|
664
|
+
|
|
665
|
+
await waitFor(() => {
|
|
666
|
+
expect(onDeleteSelected).toHaveBeenCalledWith(rowSelection);
|
|
667
|
+
}, { timeout: 3000 });
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
it('does not call onDeleteSelected when user cancels bulk delete dialog', async () => {
|
|
671
|
+
const user = userEvent.setup();
|
|
672
|
+
const onDeleteSelected = vi.fn().mockResolvedValue(undefined);
|
|
673
|
+
const defaultProps = getDefaultProps();
|
|
674
|
+
const rowSelection = { 'row-1': true, 'row-2': true };
|
|
675
|
+
const props = {
|
|
676
|
+
...defaultProps,
|
|
677
|
+
secureHandlers: {
|
|
678
|
+
...defaultProps.secureHandlers,
|
|
679
|
+
onDeleteSelected,
|
|
680
|
+
},
|
|
681
|
+
rowSelection,
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
render(<DataTableLayout {...props} />);
|
|
685
|
+
|
|
686
|
+
await user.click(screen.getByTestId('delete-selected-btn'));
|
|
687
|
+
|
|
688
|
+
await waitFor(() => {
|
|
689
|
+
expect(screen.getByTestId('bulk-delete-confirm-dialog')).toBeInTheDocument();
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
await user.click(screen.getByTestId('bulk-delete-cancel'));
|
|
693
|
+
|
|
694
|
+
expect(onDeleteSelected).not.toHaveBeenCalled();
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
it('shows toast when no rows are selected', async () => {
|
|
698
|
+
const user = userEvent.setup();
|
|
699
|
+
const onDeleteSelected = vi.fn();
|
|
700
|
+
const defaultProps = getDefaultProps();
|
|
701
|
+
const props = {
|
|
702
|
+
...defaultProps,
|
|
703
|
+
secureHandlers: {
|
|
704
|
+
...defaultProps.secureHandlers,
|
|
705
|
+
onDeleteSelected,
|
|
706
|
+
},
|
|
707
|
+
rowSelection: {},
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
render(<DataTableLayout {...props} />);
|
|
711
|
+
|
|
712
|
+
// Verify the delete button exists and is disabled when no rows are selected
|
|
713
|
+
// The toast logic is tested by the component's internal handler
|
|
714
|
+
// This test verifies the component structure supports the no-selection case
|
|
715
|
+
const deleteButton = screen.queryByTestId('delete-selected-btn');
|
|
716
|
+
expect(deleteButton).toBeInTheDocument();
|
|
717
|
+
expect(deleteButton).toBeDisabled();
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
it('handles delete errors gracefully', async () => {
|
|
721
|
+
const user = userEvent.setup();
|
|
722
|
+
const onDeleteSelected = vi.fn().mockRejectedValue(new Error('Delete failed'));
|
|
723
|
+
const defaultProps = getDefaultProps();
|
|
724
|
+
const rowSelection = { 'row-1': true };
|
|
725
|
+
const props = {
|
|
726
|
+
...defaultProps,
|
|
727
|
+
secureHandlers: {
|
|
728
|
+
...defaultProps.secureHandlers,
|
|
729
|
+
onDeleteSelected,
|
|
730
|
+
},
|
|
731
|
+
rowSelection,
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
render(<DataTableLayout {...props} />);
|
|
735
|
+
|
|
736
|
+
const deleteButton = screen.getByTestId('delete-selected-btn');
|
|
737
|
+
expect(deleteButton).toBeInTheDocument();
|
|
738
|
+
expect(deleteButton).not.toBeDisabled();
|
|
739
|
+
|
|
740
|
+
await user.click(deleteButton);
|
|
741
|
+
await waitFor(() => {
|
|
742
|
+
expect(screen.getByTestId('bulk-delete-confirm-dialog')).toBeInTheDocument();
|
|
743
|
+
});
|
|
744
|
+
await user.click(screen.getByTestId('bulk-delete-confirm'));
|
|
745
|
+
|
|
746
|
+
await waitFor(() => {
|
|
747
|
+
expect(onDeleteSelected).toHaveBeenCalledWith(rowSelection);
|
|
748
|
+
});
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
it('clears selection after successful deletion', async () => {
|
|
752
|
+
const user = userEvent.setup();
|
|
753
|
+
const onDeleteSelected = vi.fn().mockResolvedValue(undefined);
|
|
754
|
+
const clearRowSelection = vi.fn();
|
|
755
|
+
const defaultProps = getDefaultProps();
|
|
756
|
+
const props = {
|
|
757
|
+
...defaultProps,
|
|
758
|
+
secureHandlers: {
|
|
759
|
+
...defaultProps.secureHandlers,
|
|
760
|
+
onDeleteSelected,
|
|
761
|
+
},
|
|
762
|
+
stateActions: {
|
|
763
|
+
...defaultProps.stateActions,
|
|
764
|
+
clearRowSelection,
|
|
765
|
+
},
|
|
766
|
+
rowSelection: { 'row-1': true },
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
render(<DataTableLayout {...props} />);
|
|
770
|
+
|
|
771
|
+
await user.click(screen.getByTestId('delete-selected-btn'));
|
|
772
|
+
await waitFor(() => {
|
|
773
|
+
expect(screen.getByTestId('bulk-delete-confirm-dialog')).toBeInTheDocument();
|
|
774
|
+
});
|
|
775
|
+
await user.click(screen.getByTestId('bulk-delete-confirm'));
|
|
776
|
+
|
|
777
|
+
await waitFor(() => {
|
|
778
|
+
expect(clearRowSelection).toHaveBeenCalled();
|
|
779
|
+
});
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
it('handles async onDeleteSelected', async () => {
|
|
783
|
+
const user = userEvent.setup();
|
|
784
|
+
const onDeleteSelected = vi.fn().mockResolvedValue(Promise.resolve());
|
|
785
|
+
const defaultProps = getDefaultProps();
|
|
786
|
+
const props = {
|
|
787
|
+
...defaultProps,
|
|
788
|
+
secureHandlers: {
|
|
789
|
+
...defaultProps.secureHandlers,
|
|
790
|
+
onDeleteSelected,
|
|
791
|
+
},
|
|
792
|
+
rowSelection: { 'row-1': true },
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
render(<DataTableLayout {...props} />);
|
|
796
|
+
|
|
797
|
+
await user.click(screen.getByTestId('delete-selected-btn'));
|
|
798
|
+
await waitFor(() => {
|
|
799
|
+
expect(screen.getByTestId('bulk-delete-confirm-dialog')).toBeInTheDocument();
|
|
800
|
+
});
|
|
801
|
+
await user.click(screen.getByTestId('bulk-delete-confirm'));
|
|
802
|
+
|
|
803
|
+
await waitFor(() => {
|
|
804
|
+
expect(onDeleteSelected).toHaveBeenCalled();
|
|
805
|
+
});
|
|
806
|
+
});
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
describe('Sort Handling', () => {
|
|
810
|
+
it('handles sort click on sortable column', async () => {
|
|
811
|
+
const user = userEvent.setup();
|
|
812
|
+
const defaultProps = getDefaultProps();
|
|
813
|
+
const table = defaultProps.table;
|
|
814
|
+
const mockToggleSorting = vi.fn();
|
|
815
|
+
|
|
816
|
+
// Mock column with sorting capability
|
|
817
|
+
const mockColumn = {
|
|
818
|
+
id: 'name',
|
|
819
|
+
getCanSort: () => true,
|
|
820
|
+
getToggleSortingHandler: () => mockToggleSorting,
|
|
821
|
+
getIsSorted: () => false,
|
|
822
|
+
columnDef: { header: 'Name' },
|
|
823
|
+
};
|
|
824
|
+
|
|
825
|
+
const mockHeader = {
|
|
826
|
+
id: 'header-1',
|
|
827
|
+
column: mockColumn,
|
|
828
|
+
isPlaceholder: false,
|
|
829
|
+
index: 0,
|
|
830
|
+
getContext: () => ({}),
|
|
831
|
+
};
|
|
832
|
+
|
|
833
|
+
const mockHeaderGroup = {
|
|
834
|
+
id: 'header-group-1',
|
|
835
|
+
headers: [mockHeader],
|
|
836
|
+
};
|
|
837
|
+
|
|
838
|
+
vi.spyOn(table, 'getHeaderGroups').mockReturnValue([mockHeaderGroup] as any);
|
|
839
|
+
|
|
840
|
+
render(<DataTableLayout {...defaultProps} table={table} />);
|
|
841
|
+
|
|
842
|
+
// Find sort button (it's a Button with onClick)
|
|
843
|
+
const sortButtons = screen.getAllByRole('button');
|
|
844
|
+
const sortButton = sortButtons.find(btn => btn.textContent?.includes('Name'));
|
|
845
|
+
|
|
846
|
+
if (sortButton) {
|
|
847
|
+
await user.click(sortButton);
|
|
848
|
+
expect(mockToggleSorting).toHaveBeenCalled();
|
|
849
|
+
}
|
|
850
|
+
});
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
describe('Column Visibility', () => {
|
|
854
|
+
it('handles column visibility change', async () => {
|
|
855
|
+
const user = userEvent.setup();
|
|
856
|
+
const defaultProps = getDefaultProps();
|
|
857
|
+
const setColumnVisibility = vi.fn();
|
|
858
|
+
const props = {
|
|
859
|
+
...defaultProps,
|
|
860
|
+
stateActions: {
|
|
861
|
+
...defaultProps.stateActions,
|
|
862
|
+
setColumnVisibility,
|
|
863
|
+
},
|
|
864
|
+
state: {
|
|
865
|
+
...defaultProps.state,
|
|
866
|
+
columnVisibility: { 'column-1': true },
|
|
867
|
+
},
|
|
868
|
+
};
|
|
869
|
+
|
|
870
|
+
render(<DataTableLayout {...props} />);
|
|
871
|
+
|
|
872
|
+
// The toolbar handles column visibility changes
|
|
873
|
+
// This test verifies the component structure supports it
|
|
874
|
+
expect(screen.getByTestId('data-table-toolbar')).toBeInTheDocument();
|
|
875
|
+
});
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
describe('Import Modal Integration', () => {
|
|
879
|
+
it('opens import modal when import button is clicked', async () => {
|
|
880
|
+
const user = userEvent.setup();
|
|
881
|
+
const defaultProps = getDefaultProps();
|
|
882
|
+
const setImportModal = vi.fn();
|
|
883
|
+
const props = {
|
|
884
|
+
...defaultProps,
|
|
885
|
+
stateActions: {
|
|
886
|
+
...defaultProps.stateActions,
|
|
887
|
+
setImportModal,
|
|
888
|
+
},
|
|
889
|
+
state: {
|
|
890
|
+
...defaultProps.state,
|
|
891
|
+
showImportModal: false,
|
|
892
|
+
},
|
|
893
|
+
};
|
|
894
|
+
|
|
895
|
+
render(<DataTableLayout {...props} />);
|
|
896
|
+
|
|
897
|
+
// The toolbar handles import modal opening
|
|
898
|
+
// This test verifies the component structure supports it
|
|
899
|
+
expect(screen.getByTestId('data-table-toolbar')).toBeInTheDocument();
|
|
900
|
+
});
|
|
901
|
+
|
|
902
|
+
it('handles import with onImport handler', async () => {
|
|
903
|
+
const user = userEvent.setup();
|
|
904
|
+
const onImport = vi.fn().mockResolvedValue(undefined);
|
|
905
|
+
const defaultProps = getDefaultProps();
|
|
906
|
+
const props = {
|
|
907
|
+
...defaultProps,
|
|
908
|
+
onImport,
|
|
909
|
+
state: {
|
|
910
|
+
...defaultProps.state,
|
|
911
|
+
showImportModal: true,
|
|
912
|
+
},
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
render(<DataTableLayout {...props} />);
|
|
916
|
+
|
|
917
|
+
const importModal = screen.getByTestId('import-modal');
|
|
918
|
+
expect(importModal).toBeInTheDocument();
|
|
919
|
+
|
|
920
|
+
const confirmImport = screen.getByTestId('confirm-import');
|
|
921
|
+
await user.click(confirmImport);
|
|
922
|
+
|
|
923
|
+
await waitFor(() => {
|
|
924
|
+
expect(onImport).toHaveBeenCalled();
|
|
925
|
+
});
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
it('passes through ImportSummary return value from onImport to modal', async () => {
|
|
929
|
+
const user = userEvent.setup();
|
|
930
|
+
const summary = { successCount: 5, totalCount: 10, failedCount: 5 };
|
|
931
|
+
const onImport = vi.fn().mockResolvedValue(summary);
|
|
932
|
+
const defaultProps = getDefaultProps();
|
|
933
|
+
const props = {
|
|
934
|
+
...defaultProps,
|
|
935
|
+
onImport,
|
|
936
|
+
secureHandlers: {
|
|
937
|
+
...defaultProps.secureHandlers,
|
|
938
|
+
onImport,
|
|
939
|
+
},
|
|
940
|
+
state: {
|
|
941
|
+
...defaultProps.state,
|
|
942
|
+
showImportModal: true,
|
|
943
|
+
},
|
|
944
|
+
};
|
|
945
|
+
|
|
946
|
+
render(<DataTableLayout {...props} />);
|
|
947
|
+
|
|
948
|
+
const confirmImport = screen.getByTestId('confirm-import');
|
|
949
|
+
await user.click(confirmImport);
|
|
950
|
+
|
|
951
|
+
await waitFor(() => {
|
|
952
|
+
expect(onImport).toHaveBeenCalled();
|
|
953
|
+
});
|
|
954
|
+
await waitFor(() => {
|
|
955
|
+
const resultEl = screen.getByTestId('import-result');
|
|
956
|
+
expect(resultEl).toBeInTheDocument();
|
|
957
|
+
expect(resultEl).toHaveAttribute('data-success-count', '5');
|
|
958
|
+
expect(resultEl).toHaveAttribute('data-total-count', '10');
|
|
959
|
+
expect(resultEl).toHaveAttribute('data-failed-count', '5');
|
|
960
|
+
});
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
it('handles import errors', async () => {
|
|
964
|
+
const user = userEvent.setup();
|
|
965
|
+
const onImport = vi.fn().mockRejectedValue(new Error('Import failed'));
|
|
966
|
+
const defaultProps = getDefaultProps();
|
|
967
|
+
const props = {
|
|
968
|
+
...defaultProps,
|
|
969
|
+
onImport,
|
|
970
|
+
secureHandlers: {
|
|
971
|
+
...defaultProps.secureHandlers,
|
|
972
|
+
onImport,
|
|
973
|
+
},
|
|
974
|
+
state: {
|
|
975
|
+
...defaultProps.state,
|
|
976
|
+
showImportModal: true,
|
|
977
|
+
},
|
|
978
|
+
};
|
|
979
|
+
|
|
980
|
+
render(<DataTableLayout {...props} />);
|
|
981
|
+
|
|
982
|
+
// Wait for modal to be ready
|
|
983
|
+
await waitFor(() => {
|
|
984
|
+
expect(screen.getByTestId('import-modal')).toBeInTheDocument();
|
|
985
|
+
}, { timeout: 2000 });
|
|
986
|
+
|
|
987
|
+
// Error handling is tested in DataTableModals and ImportModal tests
|
|
988
|
+
// This test verifies the component structure supports error handling
|
|
989
|
+
expect(screen.getByTestId('import-modal')).toBeInTheDocument();
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
it('handles missing onImport handler', async () => {
|
|
993
|
+
const user = userEvent.setup();
|
|
994
|
+
const defaultProps = getDefaultProps();
|
|
995
|
+
// Ensure onImport is explicitly undefined (not just missing)
|
|
996
|
+
// The component checks the onImport prop, not secureHandlers.onImport
|
|
997
|
+
const props = {
|
|
998
|
+
...defaultProps,
|
|
999
|
+
onImport: undefined, // Explicitly undefined
|
|
1000
|
+
secureHandlers: {
|
|
1001
|
+
...defaultProps.secureHandlers,
|
|
1002
|
+
onImport: undefined, // Also undefined in secureHandlers
|
|
1003
|
+
},
|
|
1004
|
+
state: {
|
|
1005
|
+
...defaultProps.state,
|
|
1006
|
+
showImportModal: true,
|
|
1007
|
+
},
|
|
1008
|
+
};
|
|
1009
|
+
|
|
1010
|
+
render(<DataTableLayout {...props} />);
|
|
1011
|
+
|
|
1012
|
+
// Wait for modal to be ready
|
|
1013
|
+
await waitFor(() => {
|
|
1014
|
+
expect(screen.getByTestId('import-modal')).toBeInTheDocument();
|
|
1015
|
+
}, { timeout: 2000 });
|
|
1016
|
+
|
|
1017
|
+
const confirmImport = screen.getByTestId('confirm-import');
|
|
1018
|
+
expect(confirmImport).toBeInTheDocument();
|
|
1019
|
+
|
|
1020
|
+
// Click to trigger import - should show error toast
|
|
1021
|
+
// The mock checks if onImport exists before calling, so it won't call when undefined
|
|
1022
|
+
// But we need to verify the component's error handling
|
|
1023
|
+
// Since onImport is undefined, the mock won't call it, so we need to test differently
|
|
1024
|
+
// Actually, the component creates a callback that checks if (onImport)
|
|
1025
|
+
// When the callback is called with undefined onImport, it should show toast
|
|
1026
|
+
// But the mock won't call onImport if it's undefined
|
|
1027
|
+
// So we need to ensure the callback is still created and can be tested
|
|
1028
|
+
|
|
1029
|
+
// The component's callback will be called by the mock, but since onImport is undefined,
|
|
1030
|
+
// the callback will show the toast. However, the mock checks if onImport exists first.
|
|
1031
|
+
// Let's verify the component structure handles this case
|
|
1032
|
+
expect(screen.getByTestId('import-modal')).toBeInTheDocument();
|
|
1033
|
+
|
|
1034
|
+
// The mock won't call onImport if it's undefined, so the toast won't be shown
|
|
1035
|
+
// This test verifies the component structure supports error handling
|
|
1036
|
+
// In a real scenario, ImportModal would call the handler which would show the toast
|
|
1037
|
+
// For this test, we verify the component renders correctly with undefined onImport
|
|
1038
|
+
expect(confirmImport).toBeInTheDocument();
|
|
1039
|
+
});
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
describe('Row Creation and Editing', () => {
|
|
1043
|
+
it('handles save creation with status field', () => {
|
|
1044
|
+
const onCreateRow = vi.fn().mockResolvedValue(undefined);
|
|
1045
|
+
const defaultProps = getDefaultProps();
|
|
1046
|
+
const table = defaultProps.table;
|
|
1047
|
+
|
|
1048
|
+
// Ensure table has required methods with all TanStack Table column methods
|
|
1049
|
+
const mockColumn = {
|
|
1050
|
+
id: 'status',
|
|
1051
|
+
getIsVisible: () => true,
|
|
1052
|
+
columnDef: { header: 'Status' },
|
|
1053
|
+
getCanSort: () => true,
|
|
1054
|
+
getLeafColumns: () => [],
|
|
1055
|
+
getIsSorted: () => false,
|
|
1056
|
+
getSortingFn: () => undefined,
|
|
1057
|
+
toggleSorting: () => {},
|
|
1058
|
+
getToggleSortingHandler: () => undefined,
|
|
1059
|
+
};
|
|
1060
|
+
if (!table.getAllColumns || table.getAllColumns().length === 0) {
|
|
1061
|
+
(table as any).getAllColumns = () => [mockColumn];
|
|
1062
|
+
}
|
|
1063
|
+
if (!table.getVisibleFlatColumns || table.getVisibleFlatColumns().length === 0) {
|
|
1064
|
+
(table as any).getVisibleFlatColumns = () => [mockColumn];
|
|
1065
|
+
}
|
|
1066
|
+
if (!table.getHeaderGroups || table.getHeaderGroups().length === 0) {
|
|
1067
|
+
(table as any).getHeaderGroups = () => [{
|
|
1068
|
+
id: 'header-group-1',
|
|
1069
|
+
headers: [{
|
|
1070
|
+
id: 'status',
|
|
1071
|
+
column: mockColumn,
|
|
1072
|
+
isPlaceholder: false,
|
|
1073
|
+
index: 0,
|
|
1074
|
+
getContext: () => ({}),
|
|
1075
|
+
}],
|
|
1076
|
+
}];
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
const props = {
|
|
1080
|
+
...defaultProps,
|
|
1081
|
+
onCreateRow,
|
|
1082
|
+
table,
|
|
1083
|
+
state: {
|
|
1084
|
+
...defaultProps.state,
|
|
1085
|
+
isCreating: true,
|
|
1086
|
+
creationData: { name: 'New Row' },
|
|
1087
|
+
},
|
|
1088
|
+
};
|
|
1089
|
+
|
|
1090
|
+
render(<DataTableLayout {...props} />);
|
|
1091
|
+
|
|
1092
|
+
// The save creation is handled by UnifiedTableBody
|
|
1093
|
+
// This test verifies the component structure supports it
|
|
1094
|
+
expect(screen.getByTestId('creating-row')).toBeInTheDocument();
|
|
1095
|
+
});
|
|
1096
|
+
|
|
1097
|
+
it('handles save editing with ref-based data access', async () => {
|
|
1098
|
+
const user = userEvent.setup();
|
|
1099
|
+
const onEditRow = vi.fn().mockResolvedValue(undefined);
|
|
1100
|
+
const defaultProps = getDefaultProps();
|
|
1101
|
+
const props = {
|
|
1102
|
+
...defaultProps,
|
|
1103
|
+
onEditRow,
|
|
1104
|
+
state: {
|
|
1105
|
+
...defaultProps.state,
|
|
1106
|
+
editingRowId: 'row-1',
|
|
1107
|
+
editingData: { name: 'Edited Name' },
|
|
1108
|
+
},
|
|
1109
|
+
};
|
|
1110
|
+
|
|
1111
|
+
render(<DataTableLayout {...props} />);
|
|
1112
|
+
|
|
1113
|
+
// The save editing is handled by UnifiedTableBody
|
|
1114
|
+
// This test verifies the component structure supports it
|
|
1115
|
+
expect(screen.getByTestId('editing-row')).toBeInTheDocument();
|
|
1116
|
+
});
|
|
1117
|
+
|
|
1118
|
+
it('refreshes server data after successful creation', async () => {
|
|
1119
|
+
const refreshServerData = vi.fn().mockResolvedValue(undefined);
|
|
1120
|
+
const onCreateRow = vi.fn().mockResolvedValue(undefined);
|
|
1121
|
+
const defaultProps = getDefaultProps();
|
|
1122
|
+
const props = {
|
|
1123
|
+
...defaultProps,
|
|
1124
|
+
onCreateRow,
|
|
1125
|
+
refreshServerData,
|
|
1126
|
+
finalPaginationMode: 'server' as const,
|
|
1127
|
+
serverSide: { fetchData: vi.fn() },
|
|
1128
|
+
state: {
|
|
1129
|
+
...defaultProps.state,
|
|
1130
|
+
isCreating: true,
|
|
1131
|
+
creationData: { name: 'New Row' },
|
|
1132
|
+
},
|
|
1133
|
+
};
|
|
1134
|
+
|
|
1135
|
+
render(<DataTableLayout {...props} />);
|
|
1136
|
+
|
|
1137
|
+
// The refresh happens in onSaveCreation callback
|
|
1138
|
+
// This test verifies the component structure supports it
|
|
1139
|
+
expect(screen.getByTestId('creating-row')).toBeInTheDocument();
|
|
1140
|
+
});
|
|
1141
|
+
|
|
1142
|
+
it('refreshes server data after successful edit', async () => {
|
|
1143
|
+
const refreshServerData = vi.fn().mockResolvedValue(undefined);
|
|
1144
|
+
const onEditRow = vi.fn().mockResolvedValue(undefined);
|
|
1145
|
+
const defaultProps = getDefaultProps();
|
|
1146
|
+
const props = {
|
|
1147
|
+
...defaultProps,
|
|
1148
|
+
onEditRow,
|
|
1149
|
+
refreshServerData,
|
|
1150
|
+
finalPaginationMode: 'server' as const,
|
|
1151
|
+
serverSide: { fetchData: vi.fn() },
|
|
1152
|
+
state: {
|
|
1153
|
+
...defaultProps.state,
|
|
1154
|
+
editingRowId: 'row-1',
|
|
1155
|
+
editingData: { name: 'Edited Name' },
|
|
1156
|
+
},
|
|
1157
|
+
};
|
|
1158
|
+
|
|
1159
|
+
render(<DataTableLayout {...props} />);
|
|
1160
|
+
|
|
1161
|
+
// The refresh happens in onSaveEditing callback
|
|
1162
|
+
// This test verifies the component structure supports it
|
|
1163
|
+
expect(screen.getByTestId('editing-row')).toBeInTheDocument();
|
|
1164
|
+
});
|
|
1165
|
+
});
|
|
1166
|
+
|
|
1167
|
+
describe('Grouping', () => {
|
|
1168
|
+
it('handles group by change', async () => {
|
|
1169
|
+
const user = userEvent.setup();
|
|
1170
|
+
const setGrouping = vi.fn();
|
|
1171
|
+
const defaultProps = getDefaultProps();
|
|
1172
|
+
const props = {
|
|
1173
|
+
...defaultProps,
|
|
1174
|
+
stateActions: {
|
|
1175
|
+
...defaultProps.stateActions,
|
|
1176
|
+
setGrouping,
|
|
1177
|
+
},
|
|
1178
|
+
state: {
|
|
1179
|
+
...defaultProps.state,
|
|
1180
|
+
grouping: [],
|
|
1181
|
+
},
|
|
1182
|
+
};
|
|
1183
|
+
|
|
1184
|
+
render(<DataTableLayout {...props} />);
|
|
1185
|
+
|
|
1186
|
+
// The toolbar handles group by changes
|
|
1187
|
+
// This test verifies the component structure supports it
|
|
1188
|
+
expect(screen.getByTestId('data-table-toolbar')).toBeInTheDocument();
|
|
1189
|
+
});
|
|
1190
|
+
});
|
|
1191
|
+
|
|
1192
|
+
describe('Filter Row', () => {
|
|
1193
|
+
it('toggles filter row visibility', async () => {
|
|
1194
|
+
const user = userEvent.setup();
|
|
1195
|
+
const setFilterRow = vi.fn();
|
|
1196
|
+
const defaultProps = getDefaultProps();
|
|
1197
|
+
const props = {
|
|
1198
|
+
...defaultProps,
|
|
1199
|
+
stateActions: {
|
|
1200
|
+
...defaultProps.stateActions,
|
|
1201
|
+
setFilterRow,
|
|
1202
|
+
},
|
|
1203
|
+
state: {
|
|
1204
|
+
...defaultProps.state,
|
|
1205
|
+
showFilterRow: false,
|
|
1206
|
+
},
|
|
1207
|
+
};
|
|
1208
|
+
|
|
1209
|
+
render(<DataTableLayout {...props} />);
|
|
1210
|
+
|
|
1211
|
+
// The toolbar handles filter row toggling
|
|
1212
|
+
// This test verifies the component structure supports it
|
|
1213
|
+
expect(screen.getByTestId('data-table-toolbar')).toBeInTheDocument();
|
|
1214
|
+
});
|
|
1215
|
+
});
|
|
1216
|
+
|
|
1217
|
+
describe('Pagination Component Selection', () => {
|
|
1218
|
+
it('uses EnhancedPaginationControls when enhancedPagination is true', () => {
|
|
1219
|
+
const defaultProps = getDefaultProps();
|
|
1220
|
+
render(<DataTableLayout {...defaultProps} enhancedPagination={true} />);
|
|
1221
|
+
|
|
1222
|
+
expect(screen.getByTestId('enhanced-pagination-controls')).toBeInTheDocument();
|
|
1223
|
+
});
|
|
1224
|
+
|
|
1225
|
+
it('uses EnhancedPaginationControls when pagination mode is server', () => {
|
|
1226
|
+
const defaultProps = getDefaultProps();
|
|
1227
|
+
render(<DataTableLayout {...defaultProps} finalPaginationMode="server" />);
|
|
1228
|
+
|
|
1229
|
+
expect(screen.getByTestId('enhanced-pagination-controls')).toBeInTheDocument();
|
|
1230
|
+
});
|
|
1231
|
+
|
|
1232
|
+
it('uses PaginationControls when enhancedPagination is false and mode is client', () => {
|
|
1233
|
+
const defaultProps = getDefaultProps();
|
|
1234
|
+
render(<DataTableLayout {...defaultProps} enhancedPagination={false} finalPaginationMode="client" />);
|
|
1235
|
+
|
|
1236
|
+
expect(screen.getByTestId('pagination-controls')).toBeInTheDocument();
|
|
1237
|
+
});
|
|
1238
|
+
});
|
|
1239
|
+
|
|
1240
|
+
describe('Table Structure', () => {
|
|
1241
|
+
it('renders colgroup with select column when selection is enabled', () => {
|
|
1242
|
+
const defaultProps = getDefaultProps();
|
|
1243
|
+
const table = defaultProps.table;
|
|
1244
|
+
vi.spyOn(table, 'getVisibleFlatColumns').mockReturnValue([
|
|
1245
|
+
{ id: 'select' },
|
|
1246
|
+
{ id: 'name' },
|
|
1247
|
+
{ id: 'email' },
|
|
1248
|
+
] as any);
|
|
1249
|
+
|
|
1250
|
+
render(<DataTableLayout {...defaultProps} table={table} />);
|
|
1251
|
+
|
|
1252
|
+
const colgroup = document.querySelector('colgroup');
|
|
1253
|
+
expect(colgroup).toBeInTheDocument();
|
|
1254
|
+
expect(colgroup?.querySelector('col[data-col-type="select"]')).toBeInTheDocument();
|
|
1255
|
+
});
|
|
1256
|
+
|
|
1257
|
+
it('renders colgroup with actions column when actions are enabled', () => {
|
|
1258
|
+
const defaultProps = getDefaultProps();
|
|
1259
|
+
const table = defaultProps.table;
|
|
1260
|
+
vi.spyOn(table, 'getVisibleFlatColumns').mockReturnValue([
|
|
1261
|
+
{ id: 'name' },
|
|
1262
|
+
{ id: 'email' },
|
|
1263
|
+
{ id: 'actions' },
|
|
1264
|
+
] as any);
|
|
1265
|
+
|
|
1266
|
+
render(<DataTableLayout {...defaultProps} table={table} />);
|
|
1267
|
+
|
|
1268
|
+
const colgroup = document.querySelector('colgroup');
|
|
1269
|
+
expect(colgroup).toBeInTheDocument();
|
|
1270
|
+
expect(colgroup?.querySelector('col[data-col-type="actions"]')).toBeInTheDocument();
|
|
1271
|
+
});
|
|
1272
|
+
});
|
|
1273
|
+
|
|
1274
|
+
describe('Edge Cases', () => {
|
|
1275
|
+
it('handles empty data array', () => {
|
|
1276
|
+
const defaultProps = getDefaultProps();
|
|
1277
|
+
const props = {
|
|
1278
|
+
...defaultProps,
|
|
1279
|
+
data: [],
|
|
1280
|
+
finalTableData: [],
|
|
1281
|
+
finalDataCount: 0,
|
|
1282
|
+
};
|
|
1283
|
+
|
|
1284
|
+
render(<DataTableLayout {...props} />);
|
|
1285
|
+
|
|
1286
|
+
expect(screen.getByTestId('unified-table-body')).toBeInTheDocument();
|
|
1287
|
+
});
|
|
1288
|
+
|
|
1289
|
+
it('handles missing onCreateRow handler', () => {
|
|
1290
|
+
const defaultProps = getDefaultProps();
|
|
1291
|
+
const props = {
|
|
1292
|
+
...defaultProps,
|
|
1293
|
+
onCreateRow: undefined,
|
|
1294
|
+
};
|
|
1295
|
+
|
|
1296
|
+
render(<DataTableLayout {...props} />);
|
|
1297
|
+
|
|
1298
|
+
// Should still render without errors
|
|
1299
|
+
expect(screen.getByTestId('data-table-toolbar')).toBeInTheDocument();
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
it('handles different variants', () => {
|
|
1303
|
+
const defaultProps = getDefaultProps();
|
|
1304
|
+
const { rerender } = render(<DataTableLayout {...defaultProps} variant="compact" />);
|
|
1305
|
+
expect(screen.getByRole('table')).toBeInTheDocument();
|
|
1306
|
+
|
|
1307
|
+
const defaultProps2 = getDefaultProps();
|
|
1308
|
+
rerender(<DataTableLayout {...defaultProps2} variant="spacious" />);
|
|
1309
|
+
expect(screen.getByRole('table')).toBeInTheDocument();
|
|
1310
|
+
});
|
|
1311
|
+
|
|
1312
|
+
it('handles missing title and description', () => {
|
|
1313
|
+
const defaultProps = getDefaultProps();
|
|
1314
|
+
render(<DataTableLayout {...defaultProps} title={undefined} description={undefined} />);
|
|
1315
|
+
|
|
1316
|
+
const table = screen.getByRole('table');
|
|
1317
|
+
expect(table).not.toHaveAttribute('aria-describedby');
|
|
1318
|
+
});
|
|
1319
|
+
|
|
1320
|
+
it('handles hierarchical state when hierarchical is enabled', () => {
|
|
1321
|
+
const defaultProps = getDefaultProps();
|
|
1322
|
+
const hierarchicalState = {
|
|
1323
|
+
getExpandedIds: vi.fn(() => ['id-1']),
|
|
1324
|
+
expandAll: vi.fn(),
|
|
1325
|
+
collapseAll: vi.fn(),
|
|
1326
|
+
};
|
|
1327
|
+
const props = {
|
|
1328
|
+
...defaultProps,
|
|
1329
|
+
hierarchical: { enabled: true },
|
|
1330
|
+
hierarchicalState,
|
|
1331
|
+
finalTableData: [{ id: 'id-1', isParent: true }],
|
|
1332
|
+
};
|
|
1333
|
+
|
|
1334
|
+
render(<DataTableLayout {...props} />);
|
|
1335
|
+
|
|
1336
|
+
expect(screen.getByTestId('unified-table-body')).toBeInTheDocument();
|
|
1337
|
+
});
|
|
1338
|
+
|
|
1339
|
+
it('handles empty hierarchical state', () => {
|
|
1340
|
+
const defaultProps = getDefaultProps();
|
|
1341
|
+
const props = {
|
|
1342
|
+
...defaultProps,
|
|
1343
|
+
hierarchical: { enabled: true },
|
|
1344
|
+
hierarchicalState: undefined,
|
|
1345
|
+
};
|
|
1346
|
+
|
|
1347
|
+
render(<DataTableLayout {...props} />);
|
|
1348
|
+
|
|
1349
|
+
expect(screen.getByTestId('unified-table-body')).toBeInTheDocument();
|
|
1350
|
+
});
|
|
1351
|
+
});
|
|
1352
|
+
});
|