@freighttech/ui 0.13.18
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/LICENSE +21 -0
- package/README.md +31 -0
- package/dist/backend/AppShell.js +1289 -0
- package/dist/backend/AppShell.js.map +7 -0
- package/dist/backend/BackendChromeProvider.js +83 -0
- package/dist/backend/BackendChromeProvider.js.map +7 -0
- package/dist/backend/BackendPageSurface.js +19 -0
- package/dist/backend/BackendPageSurface.js.map +7 -0
- package/dist/backend/CollapsibleNavSection.js +149 -0
- package/dist/backend/CollapsibleNavSection.js.map +7 -0
- package/dist/backend/ContextHelp.js +33 -0
- package/dist/backend/ContextHelp.js.map +7 -0
- package/dist/backend/CrudForm.js +3163 -0
- package/dist/backend/CrudForm.js.map +7 -0
- package/dist/backend/DataTable.js +2091 -0
- package/dist/backend/DataTable.js.map +7 -0
- package/dist/backend/EmptyState.js +52 -0
- package/dist/backend/EmptyState.js.map +7 -0
- package/dist/backend/FilterBar.js +147 -0
- package/dist/backend/FilterBar.js.map +7 -0
- package/dist/backend/FilterOverlay.js +337 -0
- package/dist/backend/FilterOverlay.js.map +7 -0
- package/dist/backend/FlashMessages.js +158 -0
- package/dist/backend/FlashMessages.js.map +7 -0
- package/dist/backend/HtmlRichTextEditor.js +129 -0
- package/dist/backend/HtmlRichTextEditor.js.map +7 -0
- package/dist/backend/IntegrationsButton.js +25 -0
- package/dist/backend/IntegrationsButton.js.map +7 -0
- package/dist/backend/JsonBuilder.js +323 -0
- package/dist/backend/JsonBuilder.js.map +7 -0
- package/dist/backend/JsonDisplay.js +207 -0
- package/dist/backend/JsonDisplay.js.map +7 -0
- package/dist/backend/NextStepCallout.js +79 -0
- package/dist/backend/NextStepCallout.js.map +7 -0
- package/dist/backend/Page.js +27 -0
- package/dist/backend/Page.js.map +7 -0
- package/dist/backend/PerspectiveSidebar.js +456 -0
- package/dist/backend/PerspectiveSidebar.js.map +7 -0
- package/dist/backend/ProfileDropdown.js +341 -0
- package/dist/backend/ProfileDropdown.js.map +7 -0
- package/dist/backend/RowActions.js +155 -0
- package/dist/backend/RowActions.js.map +7 -0
- package/dist/backend/SectionHeader.js +74 -0
- package/dist/backend/SectionHeader.js.map +7 -0
- package/dist/backend/SettingsButton.js +25 -0
- package/dist/backend/SettingsButton.js.map +7 -0
- package/dist/backend/Sidebar.js +510 -0
- package/dist/backend/Sidebar.js.map +7 -0
- package/dist/backend/Sidebar.stories.js +166 -0
- package/dist/backend/Sidebar.stories.js.map +7 -0
- package/dist/backend/TruncatedCell.js +92 -0
- package/dist/backend/TruncatedCell.js.map +7 -0
- package/dist/backend/UserMenu.js +146 -0
- package/dist/backend/UserMenu.js.map +7 -0
- package/dist/backend/ValueIcons.js +34 -0
- package/dist/backend/ValueIcons.js.map +7 -0
- package/dist/backend/WebhookSetupGuide.js +79 -0
- package/dist/backend/WebhookSetupGuide.js.map +7 -0
- package/dist/backend/charts/BarChart.js +154 -0
- package/dist/backend/charts/BarChart.js.map +7 -0
- package/dist/backend/charts/ChartUtils.js +141 -0
- package/dist/backend/charts/ChartUtils.js.map +7 -0
- package/dist/backend/charts/ComboChart.js +261 -0
- package/dist/backend/charts/ComboChart.js.map +7 -0
- package/dist/backend/charts/ComboChart.stories.js +88 -0
- package/dist/backend/charts/ComboChart.stories.js.map +7 -0
- package/dist/backend/charts/KpiCard.js +96 -0
- package/dist/backend/charts/KpiCard.js.map +7 -0
- package/dist/backend/charts/LineChart.js +165 -0
- package/dist/backend/charts/LineChart.js.map +7 -0
- package/dist/backend/charts/PieChart.js +121 -0
- package/dist/backend/charts/PieChart.js.map +7 -0
- package/dist/backend/charts/ScatterChart.js +320 -0
- package/dist/backend/charts/ScatterChart.js.map +7 -0
- package/dist/backend/charts/TopNTable.js +91 -0
- package/dist/backend/charts/TopNTable.js.map +7 -0
- package/dist/backend/charts/WaterfallChart.js +136 -0
- package/dist/backend/charts/WaterfallChart.js.map +7 -0
- package/dist/backend/charts/index.js +30 -0
- package/dist/backend/charts/index.js.map +7 -0
- package/dist/backend/columns/ColumnChooserPanel.js +244 -0
- package/dist/backend/columns/ColumnChooserPanel.js.map +7 -0
- package/dist/backend/confirm-dialog/ConfirmDialog.js +214 -0
- package/dist/backend/confirm-dialog/ConfirmDialog.js.map +7 -0
- package/dist/backend/confirm-dialog/index.js +7 -0
- package/dist/backend/confirm-dialog/index.js.map +7 -0
- package/dist/backend/confirm-dialog/useConfirmDialog.js +113 -0
- package/dist/backend/confirm-dialog/useConfirmDialog.js.map +7 -0
- package/dist/backend/custom-fields/FieldDefinitionsEditor.js +1352 -0
- package/dist/backend/custom-fields/FieldDefinitionsEditor.js.map +7 -0
- package/dist/backend/custom-fields/FieldDefinitionsManager.js +332 -0
- package/dist/backend/custom-fields/FieldDefinitionsManager.js.map +7 -0
- package/dist/backend/dashboard/DashboardScreen.js +598 -0
- package/dist/backend/dashboard/DashboardScreen.js.map +7 -0
- package/dist/backend/dashboard/index.js +5 -0
- package/dist/backend/dashboard/index.js.map +7 -0
- package/dist/backend/dashboard/widgetRegistry.js +55 -0
- package/dist/backend/dashboard/widgetRegistry.js.map +7 -0
- package/dist/backend/date-range/DateRangeSelect.js +39 -0
- package/dist/backend/date-range/DateRangeSelect.js.map +7 -0
- package/dist/backend/date-range/InlineDateRangeSelect.js +42 -0
- package/dist/backend/date-range/InlineDateRangeSelect.js.map +7 -0
- package/dist/backend/date-range/InlineGranularitySelect.js +40 -0
- package/dist/backend/date-range/InlineGranularitySelect.js.map +7 -0
- package/dist/backend/date-range/dateRanges.js +176 -0
- package/dist/backend/date-range/dateRanges.js.map +7 -0
- package/dist/backend/date-range/index.js +25 -0
- package/dist/backend/date-range/index.js.map +7 -0
- package/dist/backend/detail/AccessDeniedMessage.js +28 -0
- package/dist/backend/detail/AccessDeniedMessage.js.map +7 -0
- package/dist/backend/detail/ActivitiesSection.js +947 -0
- package/dist/backend/detail/ActivitiesSection.js.map +7 -0
- package/dist/backend/detail/AddressEditor.js +417 -0
- package/dist/backend/detail/AddressEditor.js.map +7 -0
- package/dist/backend/detail/AddressTiles.js +441 -0
- package/dist/backend/detail/AddressTiles.js.map +7 -0
- package/dist/backend/detail/AddressesSection.js +274 -0
- package/dist/backend/detail/AddressesSection.js.map +7 -0
- package/dist/backend/detail/AttachmentDeleteDialog.js +41 -0
- package/dist/backend/detail/AttachmentDeleteDialog.js.map +7 -0
- package/dist/backend/detail/AttachmentMetadataDialog.js +519 -0
- package/dist/backend/detail/AttachmentMetadataDialog.js.map +7 -0
- package/dist/backend/detail/AttachmentVisualPreview.js +132 -0
- package/dist/backend/detail/AttachmentVisualPreview.js.map +7 -0
- package/dist/backend/detail/AttachmentsSection.js +281 -0
- package/dist/backend/detail/AttachmentsSection.js.map +7 -0
- package/dist/backend/detail/CustomDataSection.js +575 -0
- package/dist/backend/detail/CustomDataSection.js.map +7 -0
- package/dist/backend/detail/DetailFieldsSection.js +85 -0
- package/dist/backend/detail/DetailFieldsSection.js.map +7 -0
- package/dist/backend/detail/ErrorMessage.js +28 -0
- package/dist/backend/detail/ErrorMessage.js.map +7 -0
- package/dist/backend/detail/InlineEditors.js +697 -0
- package/dist/backend/detail/InlineEditors.js.map +7 -0
- package/dist/backend/detail/LoadingMessage.js +14 -0
- package/dist/backend/detail/LoadingMessage.js.map +7 -0
- package/dist/backend/detail/NotesSection.js +1022 -0
- package/dist/backend/detail/NotesSection.js.map +7 -0
- package/dist/backend/detail/TabEmptyState.js +25 -0
- package/dist/backend/detail/TabEmptyState.js.map +7 -0
- package/dist/backend/detail/TagsSection.js +375 -0
- package/dist/backend/detail/TagsSection.js.map +7 -0
- package/dist/backend/detail/addressFormat.js +77 -0
- package/dist/backend/detail/addressFormat.js.map +7 -0
- package/dist/backend/detail/index.js +36 -0
- package/dist/backend/detail/index.js.map +7 -0
- package/dist/backend/devtools/UmesDevToolsPanel.js +109 -0
- package/dist/backend/devtools/UmesDevToolsPanel.js.map +7 -0
- package/dist/backend/devtools/components/ConflictWarnings.js +32 -0
- package/dist/backend/devtools/components/ConflictWarnings.js.map +7 -0
- package/dist/backend/devtools/components/EnricherTiming.js +46 -0
- package/dist/backend/devtools/components/EnricherTiming.js.map +7 -0
- package/dist/backend/devtools/components/EventFlow.js +34 -0
- package/dist/backend/devtools/components/EventFlow.js.map +7 -0
- package/dist/backend/devtools/components/ExtensionPointList.js +58 -0
- package/dist/backend/devtools/components/ExtensionPointList.js.map +7 -0
- package/dist/backend/devtools/components/InterceptorActivity.js +36 -0
- package/dist/backend/devtools/components/InterceptorActivity.js.map +7 -0
- package/dist/backend/devtools/index.js +7 -0
- package/dist/backend/devtools/index.js.map +7 -0
- package/dist/backend/devtools/useUmesDevTools.js +125 -0
- package/dist/backend/devtools/useUmesDevTools.js.map +7 -0
- package/dist/backend/document-viewer/RegionOverlay.js +92 -0
- package/dist/backend/document-viewer/RegionOverlay.js.map +7 -0
- package/dist/backend/document-viewer/index.js +6 -0
- package/dist/backend/document-viewer/index.js.map +7 -0
- package/dist/backend/dynamic-grid/DynamicGrid.js +130 -0
- package/dist/backend/dynamic-grid/DynamicGrid.js.map +7 -0
- package/dist/backend/dynamic-grid/adapters.js +34 -0
- package/dist/backend/dynamic-grid/adapters.js.map +7 -0
- package/dist/backend/dynamic-grid/components/GridCell.js +149 -0
- package/dist/backend/dynamic-grid/components/GridCell.js.map +7 -0
- package/dist/backend/dynamic-grid/components/GridRow.js +32 -0
- package/dist/backend/dynamic-grid/components/GridRow.js.map +7 -0
- package/dist/backend/dynamic-grid/hooks/useGridKeyboardNav.js +115 -0
- package/dist/backend/dynamic-grid/hooks/useGridKeyboardNav.js.map +7 -0
- package/dist/backend/dynamic-grid/index.js +9 -0
- package/dist/backend/dynamic-grid/index.js.map +7 -0
- package/dist/backend/dynamic-grid/types.js +1 -0
- package/dist/backend/dynamic-grid/types.js.map +7 -0
- package/dist/backend/dynamic-table/DynamicTable.js +2607 -0
- package/dist/backend/dynamic-table/DynamicTable.js.map +7 -0
- package/dist/backend/dynamic-table/DynamicTable.stories.js +233 -0
- package/dist/backend/dynamic-table/DynamicTable.stories.js.map +7 -0
- package/dist/backend/dynamic-table/components/ActiveFilterChips.js +60 -0
- package/dist/backend/dynamic-table/components/ActiveFilterChips.js.map +7 -0
- package/dist/backend/dynamic-table/components/BulkActionsBar.js +58 -0
- package/dist/backend/dynamic-table/components/BulkActionsBar.js.map +7 -0
- package/dist/backend/dynamic-table/components/Cell.js +155 -0
- package/dist/backend/dynamic-table/components/Cell.js.map +7 -0
- package/dist/backend/dynamic-table/components/CellCommentDialog.js +726 -0
- package/dist/backend/dynamic-table/components/CellCommentDialog.js.map +7 -0
- package/dist/backend/dynamic-table/components/CellCommentHoverPopup.js +77 -0
- package/dist/backend/dynamic-table/components/CellCommentHoverPopup.js.map +7 -0
- package/dist/backend/dynamic-table/components/ColumnFilterPopover.js +485 -0
- package/dist/backend/dynamic-table/components/ColumnFilterPopover.js.map +7 -0
- package/dist/backend/dynamic-table/components/ColumnHeaderMenu.js +173 -0
- package/dist/backend/dynamic-table/components/ColumnHeaderMenu.js.map +7 -0
- package/dist/backend/dynamic-table/components/ColumnHeaders.js +528 -0
- package/dist/backend/dynamic-table/components/ColumnHeaders.js.map +7 -0
- package/dist/backend/dynamic-table/components/CompactLabelCell.js +64 -0
- package/dist/backend/dynamic-table/components/CompactLabelCell.js.map +7 -0
- package/dist/backend/dynamic-table/components/ConfigureViewFields.js +139 -0
- package/dist/backend/dynamic-table/components/ConfigureViewFields.js.map +7 -0
- package/dist/backend/dynamic-table/components/ConfigureViewFilters.js +305 -0
- package/dist/backend/dynamic-table/components/ConfigureViewFilters.js.map +7 -0
- package/dist/backend/dynamic-table/components/ConfigureViewFormatting.js +169 -0
- package/dist/backend/dynamic-table/components/ConfigureViewFormatting.js.map +7 -0
- package/dist/backend/dynamic-table/components/ConfigureViewFormulas.js +298 -0
- package/dist/backend/dynamic-table/components/ConfigureViewFormulas.js.map +7 -0
- package/dist/backend/dynamic-table/components/ConfigureViewGrouping.js +224 -0
- package/dist/backend/dynamic-table/components/ConfigureViewGrouping.js.map +7 -0
- package/dist/backend/dynamic-table/components/ConfigureViewLinkedColumns.js +167 -0
- package/dist/backend/dynamic-table/components/ConfigureViewLinkedColumns.js.map +7 -0
- package/dist/backend/dynamic-table/components/ConfigureViewPanel.js +592 -0
- package/dist/backend/dynamic-table/components/ConfigureViewPanel.js.map +7 -0
- package/dist/backend/dynamic-table/components/ConfigureViewRollups.js +269 -0
- package/dist/backend/dynamic-table/components/ConfigureViewRollups.js.map +7 -0
- package/dist/backend/dynamic-table/components/ConfigureViewSorting.js +107 -0
- package/dist/backend/dynamic-table/components/ConfigureViewSorting.js.map +7 -0
- package/dist/backend/dynamic-table/components/ContextMenu.js +91 -0
- package/dist/backend/dynamic-table/components/ContextMenu.js.map +7 -0
- package/dist/backend/dynamic-table/components/DateCell.js +80 -0
- package/dist/backend/dynamic-table/components/DateCell.js.map +7 -0
- package/dist/backend/dynamic-table/components/Debugger.js +311 -0
- package/dist/backend/dynamic-table/components/Debugger.js.map +7 -0
- package/dist/backend/dynamic-table/components/DensityControl.js +289 -0
- package/dist/backend/dynamic-table/components/DensityControl.js.map +7 -0
- package/dist/backend/dynamic-table/components/EmptyState.js +42 -0
- package/dist/backend/dynamic-table/components/EmptyState.js.map +7 -0
- package/dist/backend/dynamic-table/components/EntitySearchEditor.js +369 -0
- package/dist/backend/dynamic-table/components/EntitySearchEditor.js.map +7 -0
- package/dist/backend/dynamic-table/components/ExportMenu.js +63 -0
- package/dist/backend/dynamic-table/components/ExportMenu.js.map +7 -0
- package/dist/backend/dynamic-table/components/FilterDatePicker.js +165 -0
- package/dist/backend/dynamic-table/components/FilterDatePicker.js.map +7 -0
- package/dist/backend/dynamic-table/components/FilterValueInput.js +200 -0
- package/dist/backend/dynamic-table/components/FilterValueInput.js.map +7 -0
- package/dist/backend/dynamic-table/components/FooterTotalsRow.js +165 -0
- package/dist/backend/dynamic-table/components/FooterTotalsRow.js.map +7 -0
- package/dist/backend/dynamic-table/components/FullscreenOverlay.js +51 -0
- package/dist/backend/dynamic-table/components/FullscreenOverlay.js.map +7 -0
- package/dist/backend/dynamic-table/components/GroupHeaderRow.js +108 -0
- package/dist/backend/dynamic-table/components/GroupHeaderRow.js.map +7 -0
- package/dist/backend/dynamic-table/components/GroupSummaryRow.js +134 -0
- package/dist/backend/dynamic-table/components/GroupSummaryRow.js.map +7 -0
- package/dist/backend/dynamic-table/components/ImportPanel.js +341 -0
- package/dist/backend/dynamic-table/components/ImportPanel.js.map +7 -0
- package/dist/backend/dynamic-table/components/ImportPreviewGrid.js +114 -0
- package/dist/backend/dynamic-table/components/ImportPreviewGrid.js.map +7 -0
- package/dist/backend/dynamic-table/components/LoadErrorState.js +23 -0
- package/dist/backend/dynamic-table/components/LoadErrorState.js.map +7 -0
- package/dist/backend/dynamic-table/components/MentionPopup.js +149 -0
- package/dist/backend/dynamic-table/components/MentionPopup.js.map +7 -0
- package/dist/backend/dynamic-table/components/PerspectiveDeleteDialog.js +57 -0
- package/dist/backend/dynamic-table/components/PerspectiveDeleteDialog.js.map +7 -0
- package/dist/backend/dynamic-table/components/PerspectivePublishDialog.js +100 -0
- package/dist/backend/dynamic-table/components/PerspectivePublishDialog.js.map +7 -0
- package/dist/backend/dynamic-table/components/PerspectiveTabMenu.js +159 -0
- package/dist/backend/dynamic-table/components/PerspectiveTabMenu.js.map +7 -0
- package/dist/backend/dynamic-table/components/PerspectiveTabs.js +319 -0
- package/dist/backend/dynamic-table/components/PerspectiveTabs.js.map +7 -0
- package/dist/backend/dynamic-table/components/PerspectiveTemplateMenu.js +90 -0
- package/dist/backend/dynamic-table/components/PerspectiveTemplateMenu.js.map +7 -0
- package/dist/backend/dynamic-table/components/RowHeaderCell.js +58 -0
- package/dist/backend/dynamic-table/components/RowHeaderCell.js.map +7 -0
- package/dist/backend/dynamic-table/components/SearchBar.js +221 -0
- package/dist/backend/dynamic-table/components/SearchBar.js.map +7 -0
- package/dist/backend/dynamic-table/components/SelectMenu.js +186 -0
- package/dist/backend/dynamic-table/components/SelectMenu.js.map +7 -0
- package/dist/backend/dynamic-table/components/StaleDataBar.js +26 -0
- package/dist/backend/dynamic-table/components/StaleDataBar.js.map +7 -0
- package/dist/backend/dynamic-table/components/TableDeleteDialog.js +48 -0
- package/dist/backend/dynamic-table/components/TableDeleteDialog.js.map +7 -0
- package/dist/backend/dynamic-table/components/TablePagination.js +69 -0
- package/dist/backend/dynamic-table/components/TablePagination.js.map +7 -0
- package/dist/backend/dynamic-table/components/TableSkeleton.js +43 -0
- package/dist/backend/dynamic-table/components/TableSkeleton.js.map +7 -0
- package/dist/backend/dynamic-table/components/ToolbarOverflow.js +204 -0
- package/dist/backend/dynamic-table/components/ToolbarOverflow.js.map +7 -0
- package/dist/backend/dynamic-table/components/ViewModeSwitch.js +50 -0
- package/dist/backend/dynamic-table/components/ViewModeSwitch.js.map +7 -0
- package/dist/backend/dynamic-table/components/VirtualRow.js +214 -0
- package/dist/backend/dynamic-table/components/VirtualRow.js.map +7 -0
- package/dist/backend/dynamic-table/components/editors.js +1483 -0
- package/dist/backend/dynamic-table/components/editors.js.map +7 -0
- package/dist/backend/dynamic-table/components/renderers.js +131 -0
- package/dist/backend/dynamic-table/components/renderers.js.map +7 -0
- package/dist/backend/dynamic-table/events/events.js +73 -0
- package/dist/backend/dynamic-table/events/events.js.map +7 -0
- package/dist/backend/dynamic-table/formula/check.js +243 -0
- package/dist/backend/dynamic-table/formula/check.js.map +7 -0
- package/dist/backend/dynamic-table/formula/evaluate.js +302 -0
- package/dist/backend/dynamic-table/formula/evaluate.js.map +7 -0
- package/dist/backend/dynamic-table/formula/functions.js +602 -0
- package/dist/backend/dynamic-table/formula/functions.js.map +7 -0
- package/dist/backend/dynamic-table/formula/index.js +47 -0
- package/dist/backend/dynamic-table/formula/index.js.map +7 -0
- package/dist/backend/dynamic-table/formula/parse.js +293 -0
- package/dist/backend/dynamic-table/formula/parse.js.map +7 -0
- package/dist/backend/dynamic-table/formula/types.js +24 -0
- package/dist/backend/dynamic-table/formula/types.js.map +7 -0
- package/dist/backend/dynamic-table/handlers/cellWrites.js +143 -0
- package/dist/backend/dynamic-table/handlers/cellWrites.js.map +7 -0
- package/dist/backend/dynamic-table/handlers/index.js +504 -0
- package/dist/backend/dynamic-table/handlers/index.js.map +7 -0
- package/dist/backend/dynamic-table/handlers/perspectiveHandlers.js +377 -0
- package/dist/backend/dynamic-table/handlers/perspectiveHandlers.js.map +7 -0
- package/dist/backend/dynamic-table/hooks/index.js +524 -0
- package/dist/backend/dynamic-table/hooks/index.js.map +7 -0
- package/dist/backend/dynamic-table/hooks/useAbbreviations.js +19 -0
- package/dist/backend/dynamic-table/hooks/useAbbreviations.js.map +7 -0
- package/dist/backend/dynamic-table/hooks/useAnnotations.js +115 -0
- package/dist/backend/dynamic-table/hooks/useAnnotations.js.map +7 -0
- package/dist/backend/dynamic-table/hooks/useColumnVirtualizer.js +155 -0
- package/dist/backend/dynamic-table/hooks/useColumnVirtualizer.js.map +7 -0
- package/dist/backend/dynamic-table/hooks/useColumnWidthPersistence.js +135 -0
- package/dist/backend/dynamic-table/hooks/useColumnWidthPersistence.js.map +7 -0
- package/dist/backend/dynamic-table/hooks/useDensityPreference.js +203 -0
- package/dist/backend/dynamic-table/hooks/useDensityPreference.js.map +7 -0
- package/dist/backend/dynamic-table/hooks/useDynamicTablePage.js +1005 -0
- package/dist/backend/dynamic-table/hooks/useDynamicTablePage.js.map +7 -0
- package/dist/backend/dynamic-table/hooks/useEscapeLayer.js +32 -0
- package/dist/backend/dynamic-table/hooks/useEscapeLayer.js.map +7 -0
- package/dist/backend/dynamic-table/hooks/useGrouping.js +256 -0
- package/dist/backend/dynamic-table/hooks/useGrouping.js.map +7 -0
- package/dist/backend/dynamic-table/hooks/useSuggestionFetch.js +63 -0
- package/dist/backend/dynamic-table/hooks/useSuggestionFetch.js.map +7 -0
- package/dist/backend/dynamic-table/hooks/useTableDateFormat.js +11 -0
- package/dist/backend/dynamic-table/hooks/useTableDateFormat.js.map +7 -0
- package/dist/backend/dynamic-table/index.js +476 -0
- package/dist/backend/dynamic-table/index.js.map +7 -0
- package/dist/backend/dynamic-table/registry/ContentRegistryContext.js +126 -0
- package/dist/backend/dynamic-table/registry/ContentRegistryContext.js.map +7 -0
- package/dist/backend/dynamic-table/registry/TableRegistryContext.js +43 -0
- package/dist/backend/dynamic-table/registry/TableRegistryContext.js.map +7 -0
- package/dist/backend/dynamic-table/registry/filterAccessibleTables.js +13 -0
- package/dist/backend/dynamic-table/registry/filterAccessibleTables.js.map +7 -0
- package/dist/backend/dynamic-table/registry/index.js +44 -0
- package/dist/backend/dynamic-table/registry/index.js.map +7 -0
- package/dist/backend/dynamic-table/registry/lazyTable.js +55 -0
- package/dist/backend/dynamic-table/registry/lazyTable.js.map +7 -0
- package/dist/backend/dynamic-table/registry/types.js +1 -0
- package/dist/backend/dynamic-table/registry/types.js.map +7 -0
- package/dist/backend/dynamic-table/registry/widgetCatalog.js +96 -0
- package/dist/backend/dynamic-table/registry/widgetCatalog.js.map +7 -0
- package/dist/backend/dynamic-table/server/aggregate.js +261 -0
- package/dist/backend/dynamic-table/server/aggregate.js.map +7 -0
- package/dist/backend/dynamic-table/server/dateWindows.js +105 -0
- package/dist/backend/dynamic-table/server/dateWindows.js.map +7 -0
- package/dist/backend/dynamic-table/server/filterParser.js +97 -0
- package/dist/backend/dynamic-table/server/filterParser.js.map +7 -0
- package/dist/backend/dynamic-table/server/filterSuggestions.js +70 -0
- package/dist/backend/dynamic-table/server/filterSuggestions.js.map +7 -0
- package/dist/backend/dynamic-table/server/index.js +47 -0
- package/dist/backend/dynamic-table/server/index.js.map +7 -0
- package/dist/backend/dynamic-table/server/makeDynamicTableRoute.js +270 -0
- package/dist/backend/dynamic-table/server/makeDynamicTableRoute.js.map +7 -0
- package/dist/backend/dynamic-table/split-view/ContentPicker.js +171 -0
- package/dist/backend/dynamic-table/split-view/ContentPicker.js.map +7 -0
- package/dist/backend/dynamic-table/split-view/EmptySlot.js +47 -0
- package/dist/backend/dynamic-table/split-view/EmptySlot.js.map +7 -0
- package/dist/backend/dynamic-table/split-view/SplitViewHost.js +863 -0
- package/dist/backend/dynamic-table/split-view/SplitViewHost.js.map +7 -0
- package/dist/backend/dynamic-table/split-view/WidgetPane.js +280 -0
- package/dist/backend/dynamic-table/split-view/WidgetPane.js.map +7 -0
- package/dist/backend/dynamic-table/split-view/WorkspaceFilterBar.js +524 -0
- package/dist/backend/dynamic-table/split-view/WorkspaceFilterBar.js.map +7 -0
- package/dist/backend/dynamic-table/split-view/index.js +61 -0
- package/dist/backend/dynamic-table/split-view/index.js.map +7 -0
- package/dist/backend/dynamic-table/split-view/layoutPersistence.js +91 -0
- package/dist/backend/dynamic-table/split-view/layoutPersistence.js.map +7 -0
- package/dist/backend/dynamic-table/split-view/sharedCriteria.js +62 -0
- package/dist/backend/dynamic-table/split-view/sharedCriteria.js.map +7 -0
- package/dist/backend/dynamic-table/split-view/types.js +426 -0
- package/dist/backend/dynamic-table/split-view/types.js.map +7 -0
- package/dist/backend/dynamic-table/split-view/useSplitViewLayouts.js +79 -0
- package/dist/backend/dynamic-table/split-view/useSplitViewLayouts.js.map +7 -0
- package/dist/backend/dynamic-table/store/index.js +611 -0
- package/dist/backend/dynamic-table/store/index.js.map +7 -0
- package/dist/backend/dynamic-table/styles/ContextMenu.css.js +2 -0
- package/dist/backend/dynamic-table/styles/DynamicTable.css.js +2 -0
- package/dist/backend/dynamic-table/styles/DynamicTable.v2.css.js +2 -0
- package/dist/backend/dynamic-table/styles/SearchBar.css.js +2 -0
- package/dist/backend/dynamic-table/styles/dateCell.css.js +2 -0
- package/dist/backend/dynamic-table/styles/density.css.js +2 -0
- package/dist/backend/dynamic-table/types/density.js +29 -0
- package/dist/backend/dynamic-table/types/density.js.map +7 -0
- package/dist/backend/dynamic-table/types/filters.js +124 -0
- package/dist/backend/dynamic-table/types/filters.js.map +7 -0
- package/dist/backend/dynamic-table/types/grouping.js +34 -0
- package/dist/backend/dynamic-table/types/grouping.js.map +7 -0
- package/dist/backend/dynamic-table/types/import.js +7 -0
- package/dist/backend/dynamic-table/types/import.js.map +7 -0
- package/dist/backend/dynamic-table/types/index.js +46 -0
- package/dist/backend/dynamic-table/types/index.js.map +7 -0
- package/dist/backend/dynamic-table/types/perspective.js +79 -0
- package/dist/backend/dynamic-table/types/perspective.js.map +7 -0
- package/dist/backend/dynamic-table/types/rollup.js +13 -0
- package/dist/backend/dynamic-table/types/rollup.js.map +7 -0
- package/dist/backend/dynamic-table/utils/abbreviations.js +228 -0
- package/dist/backend/dynamic-table/utils/abbreviations.js.map +7 -0
- package/dist/backend/dynamic-table/utils/anchoredPosition.js +21 -0
- package/dist/backend/dynamic-table/utils/anchoredPosition.js.map +7 -0
- package/dist/backend/dynamic-table/utils/badgeAutoFit.js +35 -0
- package/dist/backend/dynamic-table/utils/badgeAutoFit.js.map +7 -0
- package/dist/backend/dynamic-table/utils/clipboard.js +172 -0
- package/dist/backend/dynamic-table/utils/clipboard.js.map +7 -0
- package/dist/backend/dynamic-table/utils/coerceCellValue.js +52 -0
- package/dist/backend/dynamic-table/utils/coerceCellValue.js.map +7 -0
- package/dist/backend/dynamic-table/utils/colorAdjacency.js +27 -0
- package/dist/backend/dynamic-table/utils/colorAdjacency.js.map +7 -0
- package/dist/backend/dynamic-table/utils/columnFill.js +36 -0
- package/dist/backend/dynamic-table/utils/columnFill.js.map +7 -0
- package/dist/backend/dynamic-table/utils/columnSpacerStyle.js +21 -0
- package/dist/backend/dynamic-table/utils/columnSpacerStyle.js.map +7 -0
- package/dist/backend/dynamic-table/utils/columnWindow.js +293 -0
- package/dist/backend/dynamic-table/utils/columnWindow.js.map +7 -0
- package/dist/backend/dynamic-table/utils/conditionalFormat.js +188 -0
- package/dist/backend/dynamic-table/utils/conditionalFormat.js.map +7 -0
- package/dist/backend/dynamic-table/utils/exportTable.js +82 -0
- package/dist/backend/dynamic-table/utils/exportTable.js.map +7 -0
- package/dist/backend/dynamic-table/utils/fillGeometry.js +92 -0
- package/dist/backend/dynamic-table/utils/fillGeometry.js.map +7 -0
- package/dist/backend/dynamic-table/utils/fillPatterns.js +241 -0
- package/dist/backend/dynamic-table/utils/fillPatterns.js.map +7 -0
- package/dist/backend/dynamic-table/utils/formatAggregate.js +82 -0
- package/dist/backend/dynamic-table/utils/formatAggregate.js.map +7 -0
- package/dist/backend/dynamic-table/utils/formatDateCompact.js +508 -0
- package/dist/backend/dynamic-table/utils/formatDateCompact.js.map +7 -0
- package/dist/backend/dynamic-table/utils/formatLabelCompact.js +312 -0
- package/dist/backend/dynamic-table/utils/formatLabelCompact.js.map +7 -0
- package/dist/backend/dynamic-table/utils/formulaColumns.js +158 -0
- package/dist/backend/dynamic-table/utils/formulaColumns.js.map +7 -0
- package/dist/backend/dynamic-table/utils/importMapping.js +118 -0
- package/dist/backend/dynamic-table/utils/importMapping.js.map +7 -0
- package/dist/backend/dynamic-table/utils/importParse.js +121 -0
- package/dist/backend/dynamic-table/utils/importParse.js.map +7 -0
- package/dist/backend/dynamic-table/utils/lookupColumns.js +28 -0
- package/dist/backend/dynamic-table/utils/lookupColumns.js.map +7 -0
- package/dist/backend/dynamic-table/utils/perspectiveTransforms.js +191 -0
- package/dist/backend/dynamic-table/utils/perspectiveTransforms.js.map +7 -0
- package/dist/backend/dynamic-table/utils/rollupColumns.js +81 -0
- package/dist/backend/dynamic-table/utils/rollupColumns.js.map +7 -0
- package/dist/backend/dynamic-table/validators/index.js +36 -0
- package/dist/backend/dynamic-table/validators/index.js.map +7 -0
- package/dist/backend/fields/registry.generated.js +8 -0
- package/dist/backend/fields/registry.generated.js.map +7 -0
- package/dist/backend/fields/registry.js +29 -0
- package/dist/backend/fields/registry.js.map +7 -0
- package/dist/backend/filters/AdvancedFilterBuilder.js +280 -0
- package/dist/backend/filters/AdvancedFilterBuilder.js.map +7 -0
- package/dist/backend/forms/ActionsDropdown.js +160 -0
- package/dist/backend/forms/ActionsDropdown.js.map +7 -0
- package/dist/backend/forms/FormActionButtons.js +59 -0
- package/dist/backend/forms/FormActionButtons.js.map +7 -0
- package/dist/backend/forms/FormFooter.js +18 -0
- package/dist/backend/forms/FormFooter.js.map +7 -0
- package/dist/backend/forms/FormHeader.js +125 -0
- package/dist/backend/forms/FormHeader.js.map +7 -0
- package/dist/backend/forms/index.js +11 -0
- package/dist/backend/forms/index.js.map +7 -0
- package/dist/backend/hooks/useAdvancedFilter.js +64 -0
- package/dist/backend/hooks/useAdvancedFilter.js.map +7 -0
- package/dist/backend/indexes/PartialIndexBanner.js +74 -0
- package/dist/backend/indexes/PartialIndexBanner.js.map +7 -0
- package/dist/backend/indexes/store.js +62 -0
- package/dist/backend/indexes/store.js.map +7 -0
- package/dist/backend/injection/ComponentOverrideProvider.js +55 -0
- package/dist/backend/injection/ComponentOverrideProvider.js.map +7 -0
- package/dist/backend/injection/InjectedField.js +166 -0
- package/dist/backend/injection/InjectedField.js.map +7 -0
- package/dist/backend/injection/InjectionSpot.js +323 -0
- package/dist/backend/injection/InjectionSpot.js.map +7 -0
- package/dist/backend/injection/InjectionWizard.js +256 -0
- package/dist/backend/injection/InjectionWizard.js.map +7 -0
- package/dist/backend/injection/PageInjectionBoundary.js +26 -0
- package/dist/backend/injection/PageInjectionBoundary.js.map +7 -0
- package/dist/backend/injection/SseEventIndicator.js +8 -0
- package/dist/backend/injection/SseEventIndicator.js.map +7 -0
- package/dist/backend/injection/StatusBadgeInjectionSpot.js +34 -0
- package/dist/backend/injection/StatusBadgeInjectionSpot.js.map +7 -0
- package/dist/backend/injection/StatusBadgeRenderer.js +58 -0
- package/dist/backend/injection/StatusBadgeRenderer.js.map +7 -0
- package/dist/backend/injection/WidgetSharedState.js +49 -0
- package/dist/backend/injection/WidgetSharedState.js.map +7 -0
- package/dist/backend/injection/WizardInjectionSpot.js +30 -0
- package/dist/backend/injection/WizardInjectionSpot.js.map +7 -0
- package/dist/backend/injection/eventBridge.js +129 -0
- package/dist/backend/injection/eventBridge.js.map +7 -0
- package/dist/backend/injection/helpers.js +26 -0
- package/dist/backend/injection/helpers.js.map +7 -0
- package/dist/backend/injection/mergeMenuItems.js +49 -0
- package/dist/backend/injection/mergeMenuItems.js.map +7 -0
- package/dist/backend/injection/mutationEvents.js +20 -0
- package/dist/backend/injection/mutationEvents.js.map +7 -0
- package/dist/backend/injection/resolveInjectedIcon.js +23 -0
- package/dist/backend/injection/resolveInjectedIcon.js.map +7 -0
- package/dist/backend/injection/spotIds.js +72 -0
- package/dist/backend/injection/spotIds.js.map +7 -0
- package/dist/backend/injection/useAppEvent.js +35 -0
- package/dist/backend/injection/useAppEvent.js.map +7 -0
- package/dist/backend/injection/useGuardedMutation.js +71 -0
- package/dist/backend/injection/useGuardedMutation.js.map +7 -0
- package/dist/backend/injection/useInjectedMenuItems.js +49 -0
- package/dist/backend/injection/useInjectedMenuItems.js.map +7 -0
- package/dist/backend/injection/useInjectionDataWidgets.js +36 -0
- package/dist/backend/injection/useInjectionDataWidgets.js.map +7 -0
- package/dist/backend/injection/useOperationProgress.js +64 -0
- package/dist/backend/injection/useOperationProgress.js.map +7 -0
- package/dist/backend/injection/useRegisteredComponent.js +89 -0
- package/dist/backend/injection/useRegisteredComponent.js.map +7 -0
- package/dist/backend/injection/useWidgetSharedState.js +26 -0
- package/dist/backend/injection/useWidgetSharedState.js.map +7 -0
- package/dist/backend/injection/visibility-utils.js +29 -0
- package/dist/backend/injection/visibility-utils.js.map +7 -0
- package/dist/backend/injection/widgetRegistry.js +55 -0
- package/dist/backend/injection/widgetRegistry.js.map +7 -0
- package/dist/backend/inputs/ComboboxInput.js +228 -0
- package/dist/backend/inputs/ComboboxInput.js.map +7 -0
- package/dist/backend/inputs/DatePicker.js +148 -0
- package/dist/backend/inputs/DatePicker.js.map +7 -0
- package/dist/backend/inputs/DateTimePicker.js +188 -0
- package/dist/backend/inputs/DateTimePicker.js.map +7 -0
- package/dist/backend/inputs/EventPatternInput.js +38 -0
- package/dist/backend/inputs/EventPatternInput.js.map +7 -0
- package/dist/backend/inputs/EventSelect.js +115 -0
- package/dist/backend/inputs/EventSelect.js.map +7 -0
- package/dist/backend/inputs/LookupSelect.js +197 -0
- package/dist/backend/inputs/LookupSelect.js.map +7 -0
- package/dist/backend/inputs/PhoneNumberField.js +133 -0
- package/dist/backend/inputs/PhoneNumberField.js.map +7 -0
- package/dist/backend/inputs/SwitchableMarkdownInput.js +73 -0
- package/dist/backend/inputs/SwitchableMarkdownInput.js.map +7 -0
- package/dist/backend/inputs/TagsInput.js +258 -0
- package/dist/backend/inputs/TagsInput.js.map +7 -0
- package/dist/backend/inputs/TimeInput.js +132 -0
- package/dist/backend/inputs/TimeInput.js.map +7 -0
- package/dist/backend/inputs/TimePicker.js +105 -0
- package/dist/backend/inputs/TimePicker.js.map +7 -0
- package/dist/backend/inputs/index.js +12 -0
- package/dist/backend/inputs/index.js.map +7 -0
- package/dist/backend/markdown/MarkdownContent.js +20 -0
- package/dist/backend/markdown/MarkdownContent.js.map +7 -0
- package/dist/backend/markdown/index.js +3 -0
- package/dist/backend/markdown/index.js.map +7 -0
- package/dist/backend/markdown/useMarkdownRemarkPlugins.js +33 -0
- package/dist/backend/markdown/useMarkdownRemarkPlugins.js.map +7 -0
- package/dist/backend/messages/MessageComposer.js +158 -0
- package/dist/backend/messages/MessageComposer.js.map +7 -0
- package/dist/backend/messages/MessageObjectDetail.js +81 -0
- package/dist/backend/messages/MessageObjectDetail.js.map +7 -0
- package/dist/backend/messages/MessageObjectPreview.js +48 -0
- package/dist/backend/messages/MessageObjectPreview.js.map +7 -0
- package/dist/backend/messages/MessageObjectRecordPicker.js +89 -0
- package/dist/backend/messages/MessageObjectRecordPicker.js.map +7 -0
- package/dist/backend/messages/MessagePrioritySelector.js +74 -0
- package/dist/backend/messages/MessagePrioritySelector.js.map +7 -0
- package/dist/backend/messages/MessagesIcon.js +22 -0
- package/dist/backend/messages/MessagesIcon.js.map +7 -0
- package/dist/backend/messages/SendObjectMessageDialog.js +65 -0
- package/dist/backend/messages/SendObjectMessageDialog.js.map +7 -0
- package/dist/backend/messages/index.js +17 -0
- package/dist/backend/messages/index.js.map +7 -0
- package/dist/backend/messages/message-compose-form-groups.js +333 -0
- package/dist/backend/messages/message-compose-form-groups.js.map +7 -0
- package/dist/backend/messages/message-composer.types.js +1 -0
- package/dist/backend/messages/message-composer.types.js.map +7 -0
- package/dist/backend/messages/message-priority.js +40 -0
- package/dist/backend/messages/message-priority.js.map +7 -0
- package/dist/backend/messages/useMessageCompose.js +488 -0
- package/dist/backend/messages/useMessageCompose.js.map +7 -0
- package/dist/backend/messages/useMessageComposeOperations.js +163 -0
- package/dist/backend/messages/useMessageComposeOperations.js.map +7 -0
- package/dist/backend/messages/useMessagesPoll.js +79 -0
- package/dist/backend/messages/useMessagesPoll.js.map +7 -0
- package/dist/backend/notifications/NotificationBell.js +83 -0
- package/dist/backend/notifications/NotificationBell.js.map +7 -0
- package/dist/backend/notifications/NotificationCountBadge.js +9 -0
- package/dist/backend/notifications/NotificationCountBadge.js.map +7 -0
- package/dist/backend/notifications/NotificationDispatcher.js +165 -0
- package/dist/backend/notifications/NotificationDispatcher.js.map +7 -0
- package/dist/backend/notifications/NotificationItem.js +187 -0
- package/dist/backend/notifications/NotificationItem.js.map +7 -0
- package/dist/backend/notifications/NotificationPanel.js +146 -0
- package/dist/backend/notifications/NotificationPanel.js.map +7 -0
- package/dist/backend/notifications/index.js +19 -0
- package/dist/backend/notifications/index.js.map +7 -0
- package/dist/backend/notifications/useNotificationActions.js +124 -0
- package/dist/backend/notifications/useNotificationActions.js.map +7 -0
- package/dist/backend/notifications/useNotificationEffect.js +13 -0
- package/dist/backend/notifications/useNotificationEffect.js.map +7 -0
- package/dist/backend/notifications/useNotifications.js +11 -0
- package/dist/backend/notifications/useNotifications.js.map +7 -0
- package/dist/backend/notifications/useNotificationsPoll.js +135 -0
- package/dist/backend/notifications/useNotificationsPoll.js.map +7 -0
- package/dist/backend/notifications/useNotificationsSse.js +173 -0
- package/dist/backend/notifications/useNotificationsSse.js.map +7 -0
- package/dist/backend/operations/LastOperationBanner.js +80 -0
- package/dist/backend/operations/LastOperationBanner.js.map +7 -0
- package/dist/backend/operations/store.js +183 -0
- package/dist/backend/operations/store.js.map +7 -0
- package/dist/backend/progress/ProgressTopBar.js +129 -0
- package/dist/backend/progress/ProgressTopBar.js.map +7 -0
- package/dist/backend/progress/useProgress.js +11 -0
- package/dist/backend/progress/useProgress.js.map +7 -0
- package/dist/backend/progress/useProgressPoll.js +69 -0
- package/dist/backend/progress/useProgressPoll.js.map +7 -0
- package/dist/backend/progress/useProgressSse.js +104 -0
- package/dist/backend/progress/useProgressSse.js.map +7 -0
- package/dist/backend/schedule/ScheduleAgenda.js +107 -0
- package/dist/backend/schedule/ScheduleAgenda.js.map +7 -0
- package/dist/backend/schedule/ScheduleGrid.js +107 -0
- package/dist/backend/schedule/ScheduleGrid.js.map +7 -0
- package/dist/backend/schedule/ScheduleToolbar.js +166 -0
- package/dist/backend/schedule/ScheduleToolbar.js.map +7 -0
- package/dist/backend/schedule/ScheduleView.js +168 -0
- package/dist/backend/schedule/ScheduleView.js.map +7 -0
- package/dist/backend/schedule/index.js +6 -0
- package/dist/backend/schedule/index.js.map +7 -0
- package/dist/backend/schedule/recurrence.js +83 -0
- package/dist/backend/schedule/recurrence.js.map +7 -0
- package/dist/backend/schedule/types.js +1 -0
- package/dist/backend/schedule/types.js.map +7 -0
- package/dist/backend/section-page/SectionNav.js +104 -0
- package/dist/backend/section-page/SectionNav.js.map +7 -0
- package/dist/backend/section-page/SectionPage.js +33 -0
- package/dist/backend/section-page/SectionPage.js.map +7 -0
- package/dist/backend/section-page/index.js +7 -0
- package/dist/backend/section-page/index.js.map +7 -0
- package/dist/backend/section-page/types.js +1 -0
- package/dist/backend/section-page/types.js.map +7 -0
- package/dist/backend/settings/SettingsNavigation.js +43 -0
- package/dist/backend/settings/SettingsNavigation.js.map +7 -0
- package/dist/backend/settings/SettingsPageWrapper.js +66 -0
- package/dist/backend/settings/SettingsPageWrapper.js.map +7 -0
- package/dist/backend/settings/index.js +6 -0
- package/dist/backend/settings/index.js.map +7 -0
- package/dist/backend/upgrades/UpgradeActionBanner.js +91 -0
- package/dist/backend/upgrades/UpgradeActionBanner.js.map +7 -0
- package/dist/backend/utils/api.js +178 -0
- package/dist/backend/utils/api.js.map +7 -0
- package/dist/backend/utils/apiCall.js +68 -0
- package/dist/backend/utils/apiCall.js.map +7 -0
- package/dist/backend/utils/crud.js +126 -0
- package/dist/backend/utils/crud.js.map +7 -0
- package/dist/backend/utils/customFieldColumns.js +100 -0
- package/dist/backend/utils/customFieldColumns.js.map +7 -0
- package/dist/backend/utils/customFieldDefs.js +143 -0
- package/dist/backend/utils/customFieldDefs.js.map +7 -0
- package/dist/backend/utils/customFieldFilters.js +151 -0
- package/dist/backend/utils/customFieldFilters.js.map +7 -0
- package/dist/backend/utils/customFieldForms.js +169 -0
- package/dist/backend/utils/customFieldForms.js.map +7 -0
- package/dist/backend/utils/customFieldRelationDisplay.js +239 -0
- package/dist/backend/utils/customFieldRelationDisplay.js.map +7 -0
- package/dist/backend/utils/customFieldValues.js +26 -0
- package/dist/backend/utils/customFieldValues.js.map +7 -0
- package/dist/backend/utils/flash.js +16 -0
- package/dist/backend/utils/flash.js.map +7 -0
- package/dist/backend/utils/nav.js +280 -0
- package/dist/backend/utils/nav.js.map +7 -0
- package/dist/backend/utils/richTextSanitizer.js +11 -0
- package/dist/backend/utils/richTextSanitizer.js.map +7 -0
- package/dist/backend/utils/scopedHeaderStack.js +44 -0
- package/dist/backend/utils/scopedHeaderStack.js.map +7 -0
- package/dist/backend/utils/serverErrors.js +237 -0
- package/dist/backend/utils/serverErrors.js.map +7 -0
- package/dist/backend/utils/useAutoDiscoveredFields.js +96 -0
- package/dist/backend/utils/useAutoDiscoveredFields.js.map +7 -0
- package/dist/backend/version-history/VersionHistoryAction.js +67 -0
- package/dist/backend/version-history/VersionHistoryAction.js.map +7 -0
- package/dist/backend/version-history/VersionHistoryDetail.js +57 -0
- package/dist/backend/version-history/VersionHistoryDetail.js.map +7 -0
- package/dist/backend/version-history/VersionHistoryPanel.js +270 -0
- package/dist/backend/version-history/VersionHistoryPanel.js.map +7 -0
- package/dist/backend/version-history/index.js +13 -0
- package/dist/backend/version-history/index.js.map +7 -0
- package/dist/backend/version-history/labels.js +52 -0
- package/dist/backend/version-history/labels.js.map +7 -0
- package/dist/backend/version-history/types.js +1 -0
- package/dist/backend/version-history/types.js.map +7 -0
- package/dist/backend/version-history/useAuditPermissions.js +72 -0
- package/dist/backend/version-history/useAuditPermissions.js.map +7 -0
- package/dist/backend/version-history/useVersionHistory.js +141 -0
- package/dist/backend/version-history/useVersionHistory.js.map +7 -0
- package/dist/backend/views/NewViewForm.js +56 -0
- package/dist/backend/views/NewViewForm.js.map +7 -0
- package/dist/backend/views/ShareForm.js +49 -0
- package/dist/backend/views/ShareForm.js.map +7 -0
- package/dist/backend/views/ViewChip.js +112 -0
- package/dist/backend/views/ViewChip.js.map +7 -0
- package/dist/backend/views/ViewChipMenu.js +89 -0
- package/dist/backend/views/ViewChipMenu.js.map +7 -0
- package/dist/backend/views/types.js +1 -0
- package/dist/backend/views/types.js.map +7 -0
- package/dist/frontend/AuthFooter.js +23 -0
- package/dist/frontend/AuthFooter.js.map +7 -0
- package/dist/frontend/LanguageSwitcher.js +57 -0
- package/dist/frontend/LanguageSwitcher.js.map +7 -0
- package/dist/frontend/Layout.js +14 -0
- package/dist/frontend/Layout.js.map +7 -0
- package/dist/hooks/useIsMobile.js +21 -0
- package/dist/hooks/useIsMobile.js.map +7 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +7 -0
- package/dist/portal/PortalContext.js +113 -0
- package/dist/portal/PortalContext.js.map +7 -0
- package/dist/portal/PortalLayoutProvider.js +5 -0
- package/dist/portal/PortalLayoutProvider.js.map +7 -0
- package/dist/portal/PortalLayoutShell.js +44 -0
- package/dist/portal/PortalLayoutShell.js.map +7 -0
- package/dist/portal/PortalShell.js +229 -0
- package/dist/portal/PortalShell.js.map +7 -0
- package/dist/portal/components/PortalCard.js +31 -0
- package/dist/portal/components/PortalCard.js.map +7 -0
- package/dist/portal/components/PortalEmptyState.js +16 -0
- package/dist/portal/components/PortalEmptyState.js.map +7 -0
- package/dist/portal/components/PortalFeatureCard.js +21 -0
- package/dist/portal/components/PortalFeatureCard.js.map +7 -0
- package/dist/portal/components/PortalNotificationBell.js +67 -0
- package/dist/portal/components/PortalNotificationBell.js.map +7 -0
- package/dist/portal/components/PortalNotificationPanel.js +158 -0
- package/dist/portal/components/PortalNotificationPanel.js.map +7 -0
- package/dist/portal/components/PortalPageHeader.js +16 -0
- package/dist/portal/components/PortalPageHeader.js.map +7 -0
- package/dist/portal/components/index.js +18 -0
- package/dist/portal/components/index.js.map +7 -0
- package/dist/portal/hooks/useCustomerAuth.js +69 -0
- package/dist/portal/hooks/useCustomerAuth.js.map +7 -0
- package/dist/portal/hooks/usePortalAppEvent.js +26 -0
- package/dist/portal/hooks/usePortalAppEvent.js.map +7 -0
- package/dist/portal/hooks/usePortalDashboardWidgets.js +37 -0
- package/dist/portal/hooks/usePortalDashboardWidgets.js.map +7 -0
- package/dist/portal/hooks/usePortalEventBridge.js +129 -0
- package/dist/portal/hooks/usePortalEventBridge.js.map +7 -0
- package/dist/portal/hooks/usePortalInjectedMenuItems.js +88 -0
- package/dist/portal/hooks/usePortalInjectedMenuItems.js.map +7 -0
- package/dist/portal/hooks/usePortalNotifications.js +85 -0
- package/dist/portal/hooks/usePortalNotifications.js.map +7 -0
- package/dist/portal/hooks/useTenantContext.js +53 -0
- package/dist/portal/hooks/useTenantContext.js.map +7 -0
- package/dist/primitives/DataLoader.js +68 -0
- package/dist/primitives/DataLoader.js.map +7 -0
- package/dist/primitives/ErrorNotice.js +14 -0
- package/dist/primitives/ErrorNotice.js.map +7 -0
- package/dist/primitives/Notice.js +55 -0
- package/dist/primitives/Notice.js.map +7 -0
- package/dist/primitives/alert.js +38 -0
- package/dist/primitives/alert.js.map +7 -0
- package/dist/primitives/badge.js +33 -0
- package/dist/primitives/badge.js.map +7 -0
- package/dist/primitives/button.js +45 -0
- package/dist/primitives/button.js.map +7 -0
- package/dist/primitives/calendar.js +62 -0
- package/dist/primitives/calendar.js.map +7 -0
- package/dist/primitives/card.js +91 -0
- package/dist/primitives/card.js.map +7 -0
- package/dist/primitives/checkbox.js +28 -0
- package/dist/primitives/checkbox.js.map +7 -0
- package/dist/primitives/dialog.js +101 -0
- package/dist/primitives/dialog.js.map +7 -0
- package/dist/primitives/form-field.js +78 -0
- package/dist/primitives/form-field.js.map +7 -0
- package/dist/primitives/icon-button.js +40 -0
- package/dist/primitives/icon-button.js.map +7 -0
- package/dist/primitives/input.js +22 -0
- package/dist/primitives/input.js.map +7 -0
- package/dist/primitives/label.js +21 -0
- package/dist/primitives/label.js.map +7 -0
- package/dist/primitives/popover.js +36 -0
- package/dist/primitives/popover.js.map +7 -0
- package/dist/primitives/primitives.stories.js +187 -0
- package/dist/primitives/primitives.stories.js.map +7 -0
- package/dist/primitives/progress.js +28 -0
- package/dist/primitives/progress.js.map +7 -0
- package/dist/primitives/separator.js +9 -0
- package/dist/primitives/separator.js.map +7 -0
- package/dist/primitives/sheet.js +103 -0
- package/dist/primitives/sheet.js.map +7 -0
- package/dist/primitives/spinner.js +24 -0
- package/dist/primitives/spinner.js.map +7 -0
- package/dist/primitives/status-badge.js +38 -0
- package/dist/primitives/status-badge.js.map +7 -0
- package/dist/primitives/switch.js +80 -0
- package/dist/primitives/switch.js.map +7 -0
- package/dist/primitives/table.js +29 -0
- package/dist/primitives/table.js.map +7 -0
- package/dist/primitives/tabs.js +90 -0
- package/dist/primitives/tabs.js.map +7 -0
- package/dist/primitives/textarea.js +21 -0
- package/dist/primitives/textarea.js.map +7 -0
- package/dist/primitives/tooltip.js +60 -0
- package/dist/primitives/tooltip.js.map +7 -0
- package/dist/primitives-v2/Alert.js +69 -0
- package/dist/primitives-v2/Alert.js.map +7 -0
- package/dist/primitives-v2/Alert.stories.js +42 -0
- package/dist/primitives-v2/Alert.stories.js.map +7 -0
- package/dist/primitives-v2/Avatar.js +93 -0
- package/dist/primitives-v2/Avatar.js.map +7 -0
- package/dist/primitives-v2/Avatar.stories.js +57 -0
- package/dist/primitives-v2/Avatar.stories.js.map +7 -0
- package/dist/primitives-v2/Badge.js +50 -0
- package/dist/primitives-v2/Badge.js.map +7 -0
- package/dist/primitives-v2/Badge.stories.js +66 -0
- package/dist/primitives-v2/Badge.stories.js.map +7 -0
- package/dist/primitives-v2/Breadcrumb.js +53 -0
- package/dist/primitives-v2/Breadcrumb.js.map +7 -0
- package/dist/primitives-v2/Breadcrumb.stories.js +70 -0
- package/dist/primitives-v2/Breadcrumb.stories.js.map +7 -0
- package/dist/primitives-v2/Button.js +85 -0
- package/dist/primitives-v2/Button.js.map +7 -0
- package/dist/primitives-v2/Button.stories.js +48 -0
- package/dist/primitives-v2/Button.stories.js.map +7 -0
- package/dist/primitives-v2/Checkbox.js +65 -0
- package/dist/primitives-v2/Checkbox.js.map +7 -0
- package/dist/primitives-v2/Checkbox.stories.js +31 -0
- package/dist/primitives-v2/Checkbox.stories.js.map +7 -0
- package/dist/primitives-v2/IconButton.js +13 -0
- package/dist/primitives-v2/IconButton.js.map +7 -0
- package/dist/primitives-v2/IconButton.stories.js +37 -0
- package/dist/primitives-v2/IconButton.stories.js.map +7 -0
- package/dist/primitives-v2/Input.js +74 -0
- package/dist/primitives-v2/Input.js.map +7 -0
- package/dist/primitives-v2/Input.stories.js +56 -0
- package/dist/primitives-v2/Input.stories.js.map +7 -0
- package/dist/primitives-v2/NavGroup.js +54 -0
- package/dist/primitives-v2/NavGroup.js.map +7 -0
- package/dist/primitives-v2/NavGroup.stories.js +39 -0
- package/dist/primitives-v2/NavGroup.stories.js.map +7 -0
- package/dist/primitives-v2/NavItem.js +61 -0
- package/dist/primitives-v2/NavItem.js.map +7 -0
- package/dist/primitives-v2/NavItem.stories.js +56 -0
- package/dist/primitives-v2/NavItem.stories.js.map +7 -0
- package/dist/primitives-v2/SearchInput.js +93 -0
- package/dist/primitives-v2/SearchInput.js.map +7 -0
- package/dist/primitives-v2/SearchInput.stories.js +41 -0
- package/dist/primitives-v2/SearchInput.stories.js.map +7 -0
- package/dist/primitives-v2/Sidebar.js +48 -0
- package/dist/primitives-v2/Sidebar.js.map +7 -0
- package/dist/primitives-v2/Sidebar.stories.js +76 -0
- package/dist/primitives-v2/Sidebar.stories.js.map +7 -0
- package/dist/primitives-v2/Tab.js +78 -0
- package/dist/primitives-v2/Tab.js.map +7 -0
- package/dist/primitives-v2/Tab.stories.js +51 -0
- package/dist/primitives-v2/Tab.stories.js.map +7 -0
- package/dist/primitives-v2/Tag.js +61 -0
- package/dist/primitives-v2/Tag.js.map +7 -0
- package/dist/primitives-v2/Tag.stories.js +121 -0
- package/dist/primitives-v2/Tag.stories.js.map +7 -0
- package/dist/primitives-v2/Toggle.js +72 -0
- package/dist/primitives-v2/Toggle.js.map +7 -0
- package/dist/primitives-v2/Toggle.stories.js +34 -0
- package/dist/primitives-v2/Toggle.stories.js.map +7 -0
- package/dist/primitives-v2/Topbar.js +24 -0
- package/dist/primitives-v2/Topbar.js.map +7 -0
- package/dist/primitives-v2/Topbar.stories.js +58 -0
- package/dist/primitives-v2/Topbar.stories.js.map +7 -0
- package/dist/primitives-v2/index.js +41 -0
- package/dist/primitives-v2/index.js.map +7 -0
- package/dist/primitives-v2/utils.js +7 -0
- package/dist/primitives-v2/utils.js.map +7 -0
- package/dist/theme/BrandThemeProvider.js +49 -0
- package/dist/theme/BrandThemeProvider.js.map +7 -0
- package/dist/theme/QueryProvider.js +44 -0
- package/dist/theme/QueryProvider.js.map +7 -0
- package/dist/theme/ThemeProvider.js +96 -0
- package/dist/theme/ThemeProvider.js.map +7 -0
- package/dist/theme/ThemeToggle.js +88 -0
- package/dist/theme/ThemeToggle.js.map +7 -0
- package/dist/theme/index.js +12 -0
- package/dist/theme/index.js.map +7 -0
- package/dist/theme/m3.css.js +2 -0
- package/dist/theme/tokens.v2.css.js +2 -0
- package/dist/theme/typography.css.js +2 -0
- package/dist/theme/typography.stories.js +123 -0
- package/dist/theme/typography.stories.js.map +7 -0
- package/dist/theme/typography.tokens.js +281 -0
- package/dist/theme/typography.tokens.js.map +7 -0
- package/dist/types/react-big-calendar.d.js +1 -0
- package/dist/types/react-big-calendar.d.js.map +7 -0
- package/package.json +411 -0
- package/src/backend/AppShell.tsx +1606 -0
- package/src/backend/BackendChromeProvider.tsx +99 -0
- package/src/backend/BackendPageSurface.tsx +57 -0
- package/src/backend/CollapsibleNavSection.tsx +201 -0
- package/src/backend/ContextHelp.tsx +40 -0
- package/src/backend/CrudForm.tsx +3796 -0
- package/src/backend/DataTable.tsx +2584 -0
- package/src/backend/EmptyState.tsx +65 -0
- package/src/backend/FilterBar.tsx +171 -0
- package/src/backend/FilterOverlay.tsx +388 -0
- package/src/backend/FlashMessages.tsx +189 -0
- package/src/backend/HtmlRichTextEditor.tsx +124 -0
- package/src/backend/IntegrationsButton.tsx +29 -0
- package/src/backend/JsonBuilder.tsx +359 -0
- package/src/backend/JsonDisplay.tsx +258 -0
- package/src/backend/NextStepCallout.tsx +134 -0
- package/src/backend/Page.tsx +30 -0
- package/src/backend/PerspectiveSidebar.tsx +506 -0
- package/src/backend/ProfileDropdown.tsx +369 -0
- package/src/backend/RowActions.tsx +158 -0
- package/src/backend/SectionHeader.tsx +119 -0
- package/src/backend/SettingsButton.tsx +28 -0
- package/src/backend/Sidebar.stories.tsx +183 -0
- package/src/backend/Sidebar.tsx +737 -0
- package/src/backend/TruncatedCell.tsx +133 -0
- package/src/backend/UserMenu.tsx +153 -0
- package/src/backend/ValueIcons.tsx +48 -0
- package/src/backend/WebhookSetupGuide.tsx +127 -0
- package/src/backend/__tests__/AppShell.test.tsx +392 -0
- package/src/backend/__tests__/CrudForm.custom-fields.test.tsx +180 -0
- package/src/backend/__tests__/CrudForm.datetime.test.tsx +95 -0
- package/src/backend/__tests__/CrudForm.defaults.test.tsx +195 -0
- package/src/backend/__tests__/CrudForm.navigation.test.tsx +156 -0
- package/src/backend/__tests__/CrudForm.render.test.tsx +146 -0
- package/src/backend/__tests__/CrudForm.transformData.test.tsx +231 -0
- package/src/backend/__tests__/CrudForm.unsavedNavigation.test.tsx +145 -0
- package/src/backend/__tests__/CrudForm.validation.test.tsx +178 -0
- package/src/backend/__tests__/CustomDataSection.test.tsx +465 -0
- package/src/backend/__tests__/DataTable.extensions.test.tsx +339 -0
- package/src/backend/__tests__/DataTable.namespaces.test.ts +32 -0
- package/src/backend/__tests__/DataTable.render.test.tsx +48 -0
- package/src/backend/__tests__/FieldDefinitionsEditor.test.tsx +64 -0
- package/src/backend/__tests__/FlashMessages.test.tsx +105 -0
- package/src/backend/__tests__/FormHeader.test.tsx +81 -0
- package/src/backend/__tests__/NotificationDispatcher.test.ts +172 -0
- package/src/backend/__tests__/VersionHistoryPanel.test.tsx +109 -0
- package/src/backend/__tests__/component-replacement.test.tsx +270 -0
- package/src/backend/__tests__/custom-field-filters.test.ts +72 -0
- package/src/backend/__tests__/custom-field-forms.test.ts +54 -0
- package/src/backend/__tests__/nav-utils.test.ts +175 -0
- package/src/backend/__tests__/serverErrors.test.ts +83 -0
- package/src/backend/__tests__/useAuditPermissions.test.tsx +51 -0
- package/src/backend/__tests__/useNotifications.strategy.test.ts +58 -0
- package/src/backend/__tests__/useProgress.strategy.test.ts +62 -0
- package/src/backend/charts/BarChart.tsx +208 -0
- package/src/backend/charts/ChartUtils.tsx +196 -0
- package/src/backend/charts/ComboChart.stories.tsx +103 -0
- package/src/backend/charts/ComboChart.tsx +350 -0
- package/src/backend/charts/KpiCard.tsx +155 -0
- package/src/backend/charts/LineChart.tsx +207 -0
- package/src/backend/charts/PieChart.tsx +157 -0
- package/src/backend/charts/ScatterChart.tsx +498 -0
- package/src/backend/charts/TopNTable.tsx +141 -0
- package/src/backend/charts/WaterfallChart.tsx +213 -0
- package/src/backend/charts/index.ts +21 -0
- package/src/backend/columns/ColumnChooserPanel.tsx +293 -0
- package/src/backend/confirm-dialog/ConfirmDialog.tsx +275 -0
- package/src/backend/confirm-dialog/index.ts +7 -0
- package/src/backend/confirm-dialog/useConfirmDialog.tsx +146 -0
- package/src/backend/custom-fields/FieldDefinitionsEditor.tsx +1413 -0
- package/src/backend/custom-fields/FieldDefinitionsManager.tsx +381 -0
- package/src/backend/dashboard/DashboardScreen.tsx +706 -0
- package/src/backend/dashboard/__tests__/DashboardScreen.test.tsx +114 -0
- package/src/backend/dashboard/index.ts +1 -0
- package/src/backend/dashboard/widgetRegistry.ts +68 -0
- package/src/backend/date-range/DateRangeSelect.tsx +50 -0
- package/src/backend/date-range/InlineDateRangeSelect.tsx +52 -0
- package/src/backend/date-range/InlineGranularitySelect.tsx +63 -0
- package/src/backend/date-range/__tests__/dateRanges.test.ts +224 -0
- package/src/backend/date-range/dateRanges.ts +218 -0
- package/src/backend/date-range/index.ts +15 -0
- package/src/backend/detail/AccessDeniedMessage.tsx +32 -0
- package/src/backend/detail/ActivitiesSection.tsx +1261 -0
- package/src/backend/detail/AddressEditor.tsx +479 -0
- package/src/backend/detail/AddressTiles.tsx +591 -0
- package/src/backend/detail/AddressesSection.tsx +362 -0
- package/src/backend/detail/AttachmentDeleteDialog.tsx +56 -0
- package/src/backend/detail/AttachmentMetadataDialog.tsx +674 -0
- package/src/backend/detail/AttachmentVisualPreview.tsx +155 -0
- package/src/backend/detail/AttachmentsSection.tsx +327 -0
- package/src/backend/detail/CustomDataSection.tsx +700 -0
- package/src/backend/detail/DetailFieldsSection.tsx +163 -0
- package/src/backend/detail/ErrorMessage.tsx +32 -0
- package/src/backend/detail/InlineEditors.tsx +894 -0
- package/src/backend/detail/LoadingMessage.tsx +14 -0
- package/src/backend/detail/NotesSection.tsx +1266 -0
- package/src/backend/detail/TabEmptyState.tsx +48 -0
- package/src/backend/detail/TagsSection.tsx +470 -0
- package/src/backend/detail/__tests__/InlineEditors.test.tsx +59 -0
- package/src/backend/detail/addressFormat.tsx +121 -0
- package/src/backend/detail/index.ts +46 -0
- package/src/backend/devtools/UmesDevToolsPanel.tsx +122 -0
- package/src/backend/devtools/components/ConflictWarnings.tsx +36 -0
- package/src/backend/devtools/components/EnricherTiming.tsx +50 -0
- package/src/backend/devtools/components/EventFlow.tsx +45 -0
- package/src/backend/devtools/components/ExtensionPointList.tsx +59 -0
- package/src/backend/devtools/components/InterceptorActivity.tsx +48 -0
- package/src/backend/devtools/index.ts +2 -0
- package/src/backend/devtools/useUmesDevTools.ts +188 -0
- package/src/backend/document-viewer/RegionOverlay.tsx +150 -0
- package/src/backend/document-viewer/index.ts +2 -0
- package/src/backend/dynamic-grid/DynamicGrid.tsx +161 -0
- package/src/backend/dynamic-grid/adapters.ts +57 -0
- package/src/backend/dynamic-grid/components/GridCell.tsx +194 -0
- package/src/backend/dynamic-grid/components/GridRow.tsx +40 -0
- package/src/backend/dynamic-grid/hooks/useGridKeyboardNav.ts +134 -0
- package/src/backend/dynamic-grid/index.ts +4 -0
- package/src/backend/dynamic-grid/types.ts +55 -0
- package/src/backend/dynamic-table/DynamicTable.stories.tsx +274 -0
- package/src/backend/dynamic-table/DynamicTable.tsx +4419 -0
- package/src/backend/dynamic-table/README.md +1199 -0
- package/src/backend/dynamic-table/URL_FILTERING.md +217 -0
- package/src/backend/dynamic-table/__e2e__/GridHarness.ts +1155 -0
- package/src/backend/dynamic-table/__e2e__/SplitViewHarness.ts +366 -0
- package/src/backend/dynamic-table/__tests__/BulkActionsBar.test.tsx +143 -0
- package/src/backend/dynamic-table/__tests__/ColumnQuickFilter.test.tsx +607 -0
- package/src/backend/dynamic-table/__tests__/ConfigureViewFilters.suggestions.test.tsx +177 -0
- package/src/backend/dynamic-table/__tests__/ConfigureViewFormatting.test.tsx +125 -0
- package/src/backend/dynamic-table/__tests__/ConfigureViewFormulas.test.tsx +228 -0
- package/src/backend/dynamic-table/__tests__/ConfigureViewGrouping.aggregations.test.tsx +131 -0
- package/src/backend/dynamic-table/__tests__/ConfigureViewPanel.baseView.test.tsx +162 -0
- package/src/backend/dynamic-table/__tests__/ConfigureViewPanel.focus.test.tsx +54 -0
- package/src/backend/dynamic-table/__tests__/ConfigureViewPanel.sections.test.tsx +142 -0
- package/src/backend/dynamic-table/__tests__/ConfigureViewRollups.test.tsx +228 -0
- package/src/backend/dynamic-table/__tests__/ContextMenu.test.tsx +89 -0
- package/src/backend/dynamic-table/__tests__/DynamicTable.density.test.tsx +299 -0
- package/src/backend/dynamic-table/__tests__/DynamicTable.exportTruncation.test.tsx +145 -0
- package/src/backend/dynamic-table/__tests__/DynamicTable.loadError.test.tsx +173 -0
- package/src/backend/dynamic-table/__tests__/DynamicTable.wiring.test.tsx +357 -0
- package/src/backend/dynamic-table/__tests__/EmptyState.test.tsx +70 -0
- package/src/backend/dynamic-table/__tests__/ExportMenu.test.tsx +82 -0
- package/src/backend/dynamic-table/__tests__/FooterTotalsRow.test.tsx +154 -0
- package/src/backend/dynamic-table/__tests__/GroupSummaryRow.render.test.tsx +131 -0
- package/src/backend/dynamic-table/__tests__/LoadErrorState.test.tsx +74 -0
- package/src/backend/dynamic-table/__tests__/MentionPopup.search.test.tsx +127 -0
- package/src/backend/dynamic-table/__tests__/PerspectiveTabs.test.tsx +170 -0
- package/src/backend/dynamic-table/__tests__/RowHeaderCell.selection.test.tsx +80 -0
- package/src/backend/dynamic-table/__tests__/abbreviationsSubscription.test.ts +171 -0
- package/src/backend/dynamic-table/__tests__/aggregate.spec.test.ts +224 -0
- package/src/backend/dynamic-table/__tests__/aggregateDimension.test.tsx +191 -0
- package/src/backend/dynamic-table/__tests__/anchoredPosition.test.ts +88 -0
- package/src/backend/dynamic-table/__tests__/badgeAutoFit.test.ts +121 -0
- package/src/backend/dynamic-table/__tests__/cellHandlers.save.test.ts +282 -0
- package/src/backend/dynamic-table/__tests__/cellWrites.test.ts +274 -0
- package/src/backend/dynamic-table/__tests__/clipboard.test.ts +230 -0
- package/src/backend/dynamic-table/__tests__/coerceCellValue.test.ts +117 -0
- package/src/backend/dynamic-table/__tests__/colorAdjacency.test.ts +151 -0
- package/src/backend/dynamic-table/__tests__/columnFill.test.ts +66 -0
- package/src/backend/dynamic-table/__tests__/columnHeaderHandlers.sort.test.ts +100 -0
- package/src/backend/dynamic-table/__tests__/columnHeaderReorder.test.tsx +209 -0
- package/src/backend/dynamic-table/__tests__/columnReorderPersistence.test.tsx +127 -0
- package/src/backend/dynamic-table/__tests__/columnVirtualization.render.test.tsx +420 -0
- package/src/backend/dynamic-table/__tests__/columnVisibilityPersistence.test.tsx +136 -0
- package/src/backend/dynamic-table/__tests__/columnWindow.test.ts +643 -0
- package/src/backend/dynamic-table/__tests__/commentAudience.test.tsx +137 -0
- package/src/backend/dynamic-table/__tests__/commentScope.test.tsx +214 -0
- package/src/backend/dynamic-table/__tests__/conditionalFormat.test.ts +175 -0
- package/src/backend/dynamic-table/__tests__/contentRegistry.test.ts +267 -0
- package/src/backend/dynamic-table/__tests__/dateWindows.test.ts +149 -0
- package/src/backend/dynamic-table/__tests__/density.test.ts +204 -0
- package/src/backend/dynamic-table/__tests__/exportTable.test.ts +146 -0
- package/src/backend/dynamic-table/__tests__/fillGeometry.test.ts +201 -0
- package/src/backend/dynamic-table/__tests__/fillPatterns.test.ts +272 -0
- package/src/backend/dynamic-table/__tests__/filterParser.presets.test.ts +64 -0
- package/src/backend/dynamic-table/__tests__/filterParser.relative.test.ts +109 -0
- package/src/backend/dynamic-table/__tests__/filterSuggestions.test.ts +152 -0
- package/src/backend/dynamic-table/__tests__/formatAggregate.test.ts +99 -0
- package/src/backend/dynamic-table/__tests__/formatDateCompact.test.ts +489 -0
- package/src/backend/dynamic-table/__tests__/formatLabelCompact.test.ts +717 -0
- package/src/backend/dynamic-table/__tests__/formula.check.test.ts +257 -0
- package/src/backend/dynamic-table/__tests__/formula.evaluate.test.ts +274 -0
- package/src/backend/dynamic-table/__tests__/formula.functions.test.ts +226 -0
- package/src/backend/dynamic-table/__tests__/formula.noEval.test.ts +92 -0
- package/src/backend/dynamic-table/__tests__/formula.parse.test.ts +233 -0
- package/src/backend/dynamic-table/__tests__/formulaColumns.test.tsx +213 -0
- package/src/backend/dynamic-table/__tests__/handlerIdentity.test.ts +77 -0
- package/src/backend/dynamic-table/__tests__/importMapping.test.ts +174 -0
- package/src/backend/dynamic-table/__tests__/importParse.test.ts +130 -0
- package/src/backend/dynamic-table/__tests__/importServerOnly.test.ts +37 -0
- package/src/backend/dynamic-table/__tests__/layoutPersistence.test.ts +372 -0
- package/src/backend/dynamic-table/__tests__/mouseHandlers.doubleClick.test.ts +155 -0
- package/src/backend/dynamic-table/__tests__/perspectivePersonalization.test.ts +188 -0
- package/src/backend/dynamic-table/__tests__/perspectiveResolver.test.ts +133 -0
- package/src/backend/dynamic-table/__tests__/perspectiveState.defaultGrouping.test.ts +306 -0
- package/src/backend/dynamic-table/__tests__/perspectiveState.defaults.test.ts +107 -0
- package/src/backend/dynamic-table/__tests__/perspectiveTemplates.test.tsx +269 -0
- package/src/backend/dynamic-table/__tests__/perspectiveTransforms.aggregations.test.ts +105 -0
- package/src/backend/dynamic-table/__tests__/perspectiveTransforms.conditionalFormats.test.ts +107 -0
- package/src/backend/dynamic-table/__tests__/perspectiveTransforms.dateFormat.test.ts +61 -0
- package/src/backend/dynamic-table/__tests__/perspectiveTransforms.formulas.test.ts +85 -0
- package/src/backend/dynamic-table/__tests__/perspectiveTransforms.frozenColumns.test.ts +124 -0
- package/src/backend/dynamic-table/__tests__/rollupColumns.test.ts +149 -0
- package/src/backend/dynamic-table/__tests__/sharedCriteria.test.ts +255 -0
- package/src/backend/dynamic-table/__tests__/splitViewTree.test.ts +324 -0
- package/src/backend/dynamic-table/__tests__/store.clipboardMarker.test.ts +143 -0
- package/src/backend/dynamic-table/__tests__/store.fillPreview.test.ts +168 -0
- package/src/backend/dynamic-table/__tests__/store.mutations.test.ts +345 -0
- package/src/backend/dynamic-table/__tests__/store.selection.test.ts +157 -0
- package/src/backend/dynamic-table/__tests__/store.undo.test.ts +225 -0
- package/src/backend/dynamic-table/__tests__/store.valueSource.test.tsx +204 -0
- package/src/backend/dynamic-table/__tests__/useAnnotations.legacyOptions.test.tsx +58 -0
- package/src/backend/dynamic-table/__tests__/useColumnVirtualizer.test.ts +290 -0
- package/src/backend/dynamic-table/__tests__/useDensityPreference.test.ts +435 -0
- package/src/backend/dynamic-table/__tests__/useDynamicTablePage.duplicate.test.tsx +99 -0
- package/src/backend/dynamic-table/__tests__/useDynamicTablePage.export.test.tsx +281 -0
- package/src/backend/dynamic-table/__tests__/useDynamicTablePage.personalization.test.tsx +313 -0
- package/src/backend/dynamic-table/__tests__/useDynamicTablePage.perspectiveRestore.test.tsx +126 -0
- package/src/backend/dynamic-table/__tests__/useGrouping.aggregations.test.ts +158 -0
- package/src/backend/dynamic-table/__tests__/useGrouping.groupKey.test.ts +58 -0
- package/src/backend/dynamic-table/__tests__/useKeyboardNavigation.test.ts +594 -0
- package/src/backend/dynamic-table/__tests__/useStickyOffsets.memo.test.tsx +75 -0
- package/src/backend/dynamic-table/__tests__/useStickyOffsets.test.ts +106 -0
- package/src/backend/dynamic-table/__tests__/widgetPane.test.tsx +338 -0
- package/src/backend/dynamic-table/components/ActiveFilterChips.tsx +109 -0
- package/src/backend/dynamic-table/components/BulkActionsBar.tsx +89 -0
- package/src/backend/dynamic-table/components/Cell.tsx +292 -0
- package/src/backend/dynamic-table/components/CellCommentDialog.tsx +887 -0
- package/src/backend/dynamic-table/components/CellCommentHoverPopup.tsx +94 -0
- package/src/backend/dynamic-table/components/ColumnFilterPopover.tsx +775 -0
- package/src/backend/dynamic-table/components/ColumnHeaderMenu.tsx +189 -0
- package/src/backend/dynamic-table/components/ColumnHeaders.tsx +867 -0
- package/src/backend/dynamic-table/components/CompactLabelCell.tsx +293 -0
- package/src/backend/dynamic-table/components/ConfigureViewFields.tsx +173 -0
- package/src/backend/dynamic-table/components/ConfigureViewFilters.tsx +346 -0
- package/src/backend/dynamic-table/components/ConfigureViewFormatting.tsx +205 -0
- package/src/backend/dynamic-table/components/ConfigureViewFormulas.tsx +423 -0
- package/src/backend/dynamic-table/components/ConfigureViewGrouping.tsx +264 -0
- package/src/backend/dynamic-table/components/ConfigureViewLinkedColumns.tsx +249 -0
- package/src/backend/dynamic-table/components/ConfigureViewPanel.tsx +857 -0
- package/src/backend/dynamic-table/components/ConfigureViewRollups.tsx +364 -0
- package/src/backend/dynamic-table/components/ConfigureViewSorting.tsx +121 -0
- package/src/backend/dynamic-table/components/ContextMenu.tsx +140 -0
- package/src/backend/dynamic-table/components/DateCell.tsx +205 -0
- package/src/backend/dynamic-table/components/Debugger.tsx +340 -0
- package/src/backend/dynamic-table/components/DensityControl.tsx +441 -0
- package/src/backend/dynamic-table/components/EmptyState.tsx +103 -0
- package/src/backend/dynamic-table/components/EntitySearchEditor.tsx +528 -0
- package/src/backend/dynamic-table/components/ExportMenu.tsx +90 -0
- package/src/backend/dynamic-table/components/FilterDatePicker.tsx +194 -0
- package/src/backend/dynamic-table/components/FilterValueInput.tsx +294 -0
- package/src/backend/dynamic-table/components/FooterTotalsRow.tsx +291 -0
- package/src/backend/dynamic-table/components/FullscreenOverlay.tsx +66 -0
- package/src/backend/dynamic-table/components/GroupHeaderRow.tsx +136 -0
- package/src/backend/dynamic-table/components/GroupSummaryRow.tsx +184 -0
- package/src/backend/dynamic-table/components/ImportPanel.tsx +445 -0
- package/src/backend/dynamic-table/components/ImportPreviewGrid.tsx +155 -0
- package/src/backend/dynamic-table/components/LoadErrorState.tsx +70 -0
- package/src/backend/dynamic-table/components/MentionPopup.tsx +224 -0
- package/src/backend/dynamic-table/components/PerspectiveDeleteDialog.tsx +96 -0
- package/src/backend/dynamic-table/components/PerspectivePublishDialog.tsx +152 -0
- package/src/backend/dynamic-table/components/PerspectiveTabMenu.tsx +215 -0
- package/src/backend/dynamic-table/components/PerspectiveTabs.tsx +426 -0
- package/src/backend/dynamic-table/components/PerspectiveTemplateMenu.tsx +135 -0
- package/src/backend/dynamic-table/components/RowHeaderCell.tsx +73 -0
- package/src/backend/dynamic-table/components/SearchBar.tsx +309 -0
- package/src/backend/dynamic-table/components/SelectMenu.tsx +237 -0
- package/src/backend/dynamic-table/components/StaleDataBar.tsx +74 -0
- package/src/backend/dynamic-table/components/TableDeleteDialog.tsx +74 -0
- package/src/backend/dynamic-table/components/TablePagination.tsx +96 -0
- package/src/backend/dynamic-table/components/TableSkeleton.tsx +123 -0
- package/src/backend/dynamic-table/components/ToolbarOverflow.tsx +284 -0
- package/src/backend/dynamic-table/components/ViewModeSwitch.tsx +92 -0
- package/src/backend/dynamic-table/components/VirtualRow.tsx +346 -0
- package/src/backend/dynamic-table/components/editors.tsx +1727 -0
- package/src/backend/dynamic-table/components/renderers.tsx +162 -0
- package/src/backend/dynamic-table/events/events.ts +107 -0
- package/src/backend/dynamic-table/formula/check.ts +359 -0
- package/src/backend/dynamic-table/formula/evaluate.ts +473 -0
- package/src/backend/dynamic-table/formula/functions.ts +737 -0
- package/src/backend/dynamic-table/formula/index.ts +57 -0
- package/src/backend/dynamic-table/formula/parse.ts +414 -0
- package/src/backend/dynamic-table/formula/types.ts +151 -0
- package/src/backend/dynamic-table/handlers/cellWrites.ts +362 -0
- package/src/backend/dynamic-table/handlers/index.ts +789 -0
- package/src/backend/dynamic-table/handlers/perspectiveHandlers.ts +684 -0
- package/src/backend/dynamic-table/hooks/index.ts +888 -0
- package/src/backend/dynamic-table/hooks/useAbbreviations.ts +67 -0
- package/src/backend/dynamic-table/hooks/useAnnotations.ts +215 -0
- package/src/backend/dynamic-table/hooks/useColumnVirtualizer.ts +303 -0
- package/src/backend/dynamic-table/hooks/useColumnWidthPersistence.ts +214 -0
- package/src/backend/dynamic-table/hooks/useDensityPreference.ts +451 -0
- package/src/backend/dynamic-table/hooks/useDynamicTablePage.tsx +1681 -0
- package/src/backend/dynamic-table/hooks/useEscapeLayer.ts +72 -0
- package/src/backend/dynamic-table/hooks/useGrouping.ts +428 -0
- package/src/backend/dynamic-table/hooks/useSuggestionFetch.ts +109 -0
- package/src/backend/dynamic-table/hooks/useTableDateFormat.tsx +19 -0
- package/src/backend/dynamic-table/index.ts +497 -0
- package/src/backend/dynamic-table/registry/ContentRegistryContext.tsx +244 -0
- package/src/backend/dynamic-table/registry/TableRegistryContext.tsx +84 -0
- package/src/backend/dynamic-table/registry/filterAccessibleTables.ts +36 -0
- package/src/backend/dynamic-table/registry/index.ts +38 -0
- package/src/backend/dynamic-table/registry/lazyTable.tsx +80 -0
- package/src/backend/dynamic-table/registry/types.ts +181 -0
- package/src/backend/dynamic-table/registry/widgetCatalog.ts +226 -0
- package/src/backend/dynamic-table/server/aggregate.ts +474 -0
- package/src/backend/dynamic-table/server/dateWindows.ts +201 -0
- package/src/backend/dynamic-table/server/filterParser.ts +132 -0
- package/src/backend/dynamic-table/server/filterSuggestions.ts +150 -0
- package/src/backend/dynamic-table/server/index.ts +47 -0
- package/src/backend/dynamic-table/server/makeDynamicTableRoute.ts +426 -0
- package/src/backend/dynamic-table/split-view/ContentPicker.tsx +251 -0
- package/src/backend/dynamic-table/split-view/EmptySlot.tsx +69 -0
- package/src/backend/dynamic-table/split-view/SplitViewHost.tsx +1336 -0
- package/src/backend/dynamic-table/split-view/WidgetPane.tsx +390 -0
- package/src/backend/dynamic-table/split-view/WorkspaceFilterBar.tsx +728 -0
- package/src/backend/dynamic-table/split-view/index.ts +48 -0
- package/src/backend/dynamic-table/split-view/layoutPersistence.ts +185 -0
- package/src/backend/dynamic-table/split-view/sharedCriteria.ts +196 -0
- package/src/backend/dynamic-table/split-view/types.ts +778 -0
- package/src/backend/dynamic-table/split-view/useSplitViewLayouts.ts +97 -0
- package/src/backend/dynamic-table/store/index.ts +1011 -0
- package/src/backend/dynamic-table/styles/ContextMenu.css +387 -0
- package/src/backend/dynamic-table/styles/DynamicTable.css +3659 -0
- package/src/backend/dynamic-table/styles/DynamicTable.v2.css +2651 -0
- package/src/backend/dynamic-table/styles/SearchBar.css +426 -0
- package/src/backend/dynamic-table/styles/dateCell.css +146 -0
- package/src/backend/dynamic-table/styles/density.css +690 -0
- package/src/backend/dynamic-table/types/density.ts +179 -0
- package/src/backend/dynamic-table/types/filters.ts +156 -0
- package/src/backend/dynamic-table/types/grouping.ts +189 -0
- package/src/backend/dynamic-table/types/import.ts +132 -0
- package/src/backend/dynamic-table/types/index.ts +1198 -0
- package/src/backend/dynamic-table/types/perspective.ts +436 -0
- package/src/backend/dynamic-table/types/rollup.ts +111 -0
- package/src/backend/dynamic-table/utils/abbreviations.ts +556 -0
- package/src/backend/dynamic-table/utils/anchoredPosition.ts +106 -0
- package/src/backend/dynamic-table/utils/badgeAutoFit.ts +108 -0
- package/src/backend/dynamic-table/utils/clipboard.ts +294 -0
- package/src/backend/dynamic-table/utils/coerceCellValue.ts +96 -0
- package/src/backend/dynamic-table/utils/colorAdjacency.ts +87 -0
- package/src/backend/dynamic-table/utils/columnFill.ts +93 -0
- package/src/backend/dynamic-table/utils/columnSpacerStyle.ts +53 -0
- package/src/backend/dynamic-table/utils/columnWindow.ts +668 -0
- package/src/backend/dynamic-table/utils/conditionalFormat.ts +300 -0
- package/src/backend/dynamic-table/utils/exportTable.ts +140 -0
- package/src/backend/dynamic-table/utils/fillGeometry.ts +187 -0
- package/src/backend/dynamic-table/utils/fillPatterns.ts +422 -0
- package/src/backend/dynamic-table/utils/formatAggregate.ts +145 -0
- package/src/backend/dynamic-table/utils/formatDateCompact.ts +879 -0
- package/src/backend/dynamic-table/utils/formatLabelCompact.ts +640 -0
- package/src/backend/dynamic-table/utils/formulaColumns.tsx +237 -0
- package/src/backend/dynamic-table/utils/importMapping.ts +202 -0
- package/src/backend/dynamic-table/utils/importParse.ts +220 -0
- package/src/backend/dynamic-table/utils/lookupColumns.tsx +47 -0
- package/src/backend/dynamic-table/utils/perspectiveTransforms.ts +368 -0
- package/src/backend/dynamic-table/utils/rollupColumns.ts +162 -0
- package/src/backend/dynamic-table/validators/index.ts +40 -0
- package/src/backend/fields/registry.generated.ts +8 -0
- package/src/backend/fields/registry.ts +38 -0
- package/src/backend/filters/AdvancedFilterBuilder.tsx +339 -0
- package/src/backend/forms/ActionsDropdown.tsx +198 -0
- package/src/backend/forms/FormActionButtons.tsx +98 -0
- package/src/backend/forms/FormFooter.tsx +27 -0
- package/src/backend/forms/FormHeader.tsx +204 -0
- package/src/backend/forms/index.ts +4 -0
- package/src/backend/hooks/useAdvancedFilter.ts +77 -0
- package/src/backend/indexes/PartialIndexBanner.tsx +86 -0
- package/src/backend/indexes/store.ts +88 -0
- package/src/backend/injection/ComponentOverrideProvider.tsx +66 -0
- package/src/backend/injection/InjectedField.tsx +196 -0
- package/src/backend/injection/InjectionSpot.tsx +457 -0
- package/src/backend/injection/InjectionWizard.tsx +335 -0
- package/src/backend/injection/PageInjectionBoundary.tsx +31 -0
- package/src/backend/injection/SseEventIndicator.tsx +11 -0
- package/src/backend/injection/StatusBadgeInjectionSpot.tsx +50 -0
- package/src/backend/injection/StatusBadgeRenderer.tsx +100 -0
- package/src/backend/injection/WidgetSharedState.ts +58 -0
- package/src/backend/injection/WizardInjectionSpot.tsx +46 -0
- package/src/backend/injection/eventBridge.ts +158 -0
- package/src/backend/injection/helpers.ts +35 -0
- package/src/backend/injection/mergeMenuItems.ts +80 -0
- package/src/backend/injection/mutationEvents.ts +22 -0
- package/src/backend/injection/resolveInjectedIcon.tsx +30 -0
- package/src/backend/injection/spotIds.ts +64 -0
- package/src/backend/injection/useAppEvent.ts +76 -0
- package/src/backend/injection/useGuardedMutation.ts +101 -0
- package/src/backend/injection/useInjectedMenuItems.ts +67 -0
- package/src/backend/injection/useInjectionDataWidgets.ts +41 -0
- package/src/backend/injection/useOperationProgress.ts +105 -0
- package/src/backend/injection/useRegisteredComponent.tsx +106 -0
- package/src/backend/injection/useWidgetSharedState.ts +28 -0
- package/src/backend/injection/visibility-utils.ts +31 -0
- package/src/backend/injection/widgetRegistry.ts +68 -0
- package/src/backend/inputs/ComboboxInput.tsx +272 -0
- package/src/backend/inputs/DatePicker.tsx +162 -0
- package/src/backend/inputs/DateTimePicker.tsx +199 -0
- package/src/backend/inputs/EventPatternInput.tsx +46 -0
- package/src/backend/inputs/EventSelect.tsx +183 -0
- package/src/backend/inputs/LookupSelect.tsx +253 -0
- package/src/backend/inputs/PhoneNumberField.tsx +166 -0
- package/src/backend/inputs/SwitchableMarkdownInput.tsx +106 -0
- package/src/backend/inputs/TagsInput.tsx +297 -0
- package/src/backend/inputs/TimeInput.tsx +146 -0
- package/src/backend/inputs/TimePicker.tsx +120 -0
- package/src/backend/inputs/__tests__/DatePicker.test.tsx +76 -0
- package/src/backend/inputs/__tests__/DateTimePicker.logic.test.ts +126 -0
- package/src/backend/inputs/__tests__/PhoneNumberField.test.tsx +73 -0
- package/src/backend/inputs/__tests__/TagsInput.test.tsx +78 -0
- package/src/backend/inputs/__tests__/TimeInput.test.tsx +134 -0
- package/src/backend/inputs/__tests__/TimePicker.test.tsx +50 -0
- package/src/backend/inputs/index.ts +11 -0
- package/src/backend/markdown/MarkdownContent.tsx +33 -0
- package/src/backend/markdown/index.ts +2 -0
- package/src/backend/markdown/useMarkdownRemarkPlugins.ts +39 -0
- package/src/backend/messages/MessageComposer.tsx +197 -0
- package/src/backend/messages/MessageObjectDetail.tsx +95 -0
- package/src/backend/messages/MessageObjectPreview.tsx +64 -0
- package/src/backend/messages/MessageObjectRecordPicker.tsx +149 -0
- package/src/backend/messages/MessagePrioritySelector.tsx +78 -0
- package/src/backend/messages/MessagesIcon.tsx +32 -0
- package/src/backend/messages/SendObjectMessageDialog.tsx +74 -0
- package/src/backend/messages/__tests__/MessageComposer.test.tsx +350 -0
- package/src/backend/messages/__tests__/SendObjectMessageDialog.test.tsx +77 -0
- package/src/backend/messages/index.ts +22 -0
- package/src/backend/messages/message-compose-form-groups.tsx +383 -0
- package/src/backend/messages/message-composer.types.ts +82 -0
- package/src/backend/messages/message-priority.ts +53 -0
- package/src/backend/messages/useMessageCompose.ts +666 -0
- package/src/backend/messages/useMessageComposeOperations.ts +259 -0
- package/src/backend/messages/useMessagesPoll.ts +118 -0
- package/src/backend/notifications/NotificationBell.tsx +91 -0
- package/src/backend/notifications/NotificationCountBadge.tsx +14 -0
- package/src/backend/notifications/NotificationDispatcher.ts +213 -0
- package/src/backend/notifications/NotificationItem.tsx +262 -0
- package/src/backend/notifications/NotificationPanel.tsx +225 -0
- package/src/backend/notifications/__tests__/NotificationCountBadge.test.tsx +34 -0
- package/src/backend/notifications/index.ts +20 -0
- package/src/backend/notifications/useNotificationActions.ts +161 -0
- package/src/backend/notifications/useNotificationEffect.ts +17 -0
- package/src/backend/notifications/useNotifications.ts +12 -0
- package/src/backend/notifications/useNotificationsPoll.ts +167 -0
- package/src/backend/notifications/useNotificationsSse.ts +220 -0
- package/src/backend/operations/LastOperationBanner.tsx +85 -0
- package/src/backend/operations/__tests__/LastOperationBanner.test.tsx +101 -0
- package/src/backend/operations/store.ts +230 -0
- package/src/backend/progress/ProgressTopBar.tsx +189 -0
- package/src/backend/progress/useProgress.ts +12 -0
- package/src/backend/progress/useProgressPoll.ts +100 -0
- package/src/backend/progress/useProgressSse.ts +118 -0
- package/src/backend/schedule/ScheduleAgenda.tsx +136 -0
- package/src/backend/schedule/ScheduleGrid.tsx +136 -0
- package/src/backend/schedule/ScheduleToolbar.tsx +178 -0
- package/src/backend/schedule/ScheduleView.tsx +201 -0
- package/src/backend/schedule/index.ts +5 -0
- package/src/backend/schedule/recurrence.ts +99 -0
- package/src/backend/schedule/types.ts +26 -0
- package/src/backend/section-page/SectionNav.tsx +140 -0
- package/src/backend/section-page/SectionPage.tsx +34 -0
- package/src/backend/section-page/index.ts +3 -0
- package/src/backend/section-page/types.ts +30 -0
- package/src/backend/settings/SettingsNavigation.tsx +74 -0
- package/src/backend/settings/SettingsPageWrapper.tsx +78 -0
- package/src/backend/settings/index.ts +3 -0
- package/src/backend/upgrades/UpgradeActionBanner.tsx +128 -0
- package/src/backend/utils/__tests__/api.scoped-headers.test.ts +58 -0
- package/src/backend/utils/__tests__/api.test.ts +148 -0
- package/src/backend/utils/__tests__/apiCall.scoped-request-headers.test.ts +58 -0
- package/src/backend/utils/__tests__/apiCall.test.ts +109 -0
- package/src/backend/utils/__tests__/crud.test.ts +87 -0
- package/src/backend/utils/__tests__/customFieldDefs.test.ts +25 -0
- package/src/backend/utils/__tests__/customFieldValues.test.ts +35 -0
- package/src/backend/utils/__tests__/richTextSanitizer.test.ts +57 -0
- package/src/backend/utils/api.ts +214 -0
- package/src/backend/utils/apiCall.ts +123 -0
- package/src/backend/utils/crud.ts +174 -0
- package/src/backend/utils/customFieldColumns.ts +118 -0
- package/src/backend/utils/customFieldDefs.ts +247 -0
- package/src/backend/utils/customFieldFilters.ts +174 -0
- package/src/backend/utils/customFieldForms.ts +203 -0
- package/src/backend/utils/customFieldRelationDisplay.ts +302 -0
- package/src/backend/utils/customFieldValues.ts +41 -0
- package/src/backend/utils/flash.ts +17 -0
- package/src/backend/utils/nav.ts +402 -0
- package/src/backend/utils/richTextSanitizer.ts +7 -0
- package/src/backend/utils/scopedHeaderStack.ts +49 -0
- package/src/backend/utils/serverErrors.ts +313 -0
- package/src/backend/utils/useAutoDiscoveredFields.ts +138 -0
- package/src/backend/version-history/VersionHistoryAction.tsx +76 -0
- package/src/backend/version-history/VersionHistoryDetail.tsx +87 -0
- package/src/backend/version-history/VersionHistoryPanel.tsx +331 -0
- package/src/backend/version-history/index.ts +9 -0
- package/src/backend/version-history/labels.ts +56 -0
- package/src/backend/version-history/types.ts +27 -0
- package/src/backend/version-history/useAuditPermissions.ts +94 -0
- package/src/backend/version-history/useVersionHistory.ts +173 -0
- package/src/backend/views/NewViewForm.tsx +54 -0
- package/src/backend/views/ShareForm.tsx +62 -0
- package/src/backend/views/ViewChip.tsx +123 -0
- package/src/backend/views/ViewChipMenu.tsx +94 -0
- package/src/backend/views/types.ts +4 -0
- package/src/frontend/AuthFooter.tsx +29 -0
- package/src/frontend/LanguageSwitcher.tsx +66 -0
- package/src/frontend/Layout.tsx +13 -0
- package/src/hooks/useIsMobile.ts +27 -0
- package/src/index.ts +40 -0
- package/src/portal/PortalContext.tsx +180 -0
- package/src/portal/PortalLayoutProvider.tsx +5 -0
- package/src/portal/PortalLayoutShell.tsx +64 -0
- package/src/portal/PortalShell.tsx +370 -0
- package/src/portal/components/PortalCard.tsx +77 -0
- package/src/portal/components/PortalEmptyState.tsx +32 -0
- package/src/portal/components/PortalFeatureCard.tsx +41 -0
- package/src/portal/components/PortalNotificationBell.tsx +81 -0
- package/src/portal/components/PortalNotificationPanel.tsx +230 -0
- package/src/portal/components/PortalPageHeader.tsx +33 -0
- package/src/portal/components/index.ts +6 -0
- package/src/portal/hooks/useCustomerAuth.ts +97 -0
- package/src/portal/hooks/usePortalAppEvent.ts +56 -0
- package/src/portal/hooks/usePortalDashboardWidgets.ts +50 -0
- package/src/portal/hooks/usePortalEventBridge.ts +162 -0
- package/src/portal/hooks/usePortalInjectedMenuItems.ts +130 -0
- package/src/portal/hooks/usePortalNotifications.ts +116 -0
- package/src/portal/hooks/useTenantContext.ts +83 -0
- package/src/primitives/DataLoader.tsx +93 -0
- package/src/primitives/ErrorNotice.tsx +17 -0
- package/src/primitives/Notice.tsx +83 -0
- package/src/primitives/alert.tsx +59 -0
- package/src/primitives/badge.tsx +43 -0
- package/src/primitives/button.tsx +60 -0
- package/src/primitives/calendar.tsx +69 -0
- package/src/primitives/card.tsx +92 -0
- package/src/primitives/checkbox.tsx +34 -0
- package/src/primitives/dialog.tsx +122 -0
- package/src/primitives/form-field.tsx +106 -0
- package/src/primitives/icon-button.tsx +48 -0
- package/src/primitives/input.tsx +26 -0
- package/src/primitives/label.tsx +18 -0
- package/src/primitives/popover.tsx +37 -0
- package/src/primitives/primitives.stories.tsx +223 -0
- package/src/primitives/progress.tsx +28 -0
- package/src/primitives/separator.tsx +7 -0
- package/src/primitives/sheet.tsx +137 -0
- package/src/primitives/spinner.tsx +27 -0
- package/src/primitives/status-badge.tsx +62 -0
- package/src/primitives/switch.tsx +92 -0
- package/src/primitives/table.tsx +27 -0
- package/src/primitives/tabs.tsx +131 -0
- package/src/primitives/textarea.tsx +20 -0
- package/src/primitives/tooltip.tsx +85 -0
- package/src/primitives-v2/AGENTS.md +66 -0
- package/src/primitives-v2/Alert.stories.tsx +56 -0
- package/src/primitives-v2/Alert.tsx +108 -0
- package/src/primitives-v2/Avatar.stories.tsx +60 -0
- package/src/primitives-v2/Avatar.tsx +139 -0
- package/src/primitives-v2/Badge.stories.tsx +80 -0
- package/src/primitives-v2/Badge.tsx +99 -0
- package/src/primitives-v2/Breadcrumb.stories.tsx +61 -0
- package/src/primitives-v2/Breadcrumb.tsx +96 -0
- package/src/primitives-v2/Button.stories.tsx +72 -0
- package/src/primitives-v2/Button.tsx +178 -0
- package/src/primitives-v2/Checkbox.stories.tsx +34 -0
- package/src/primitives-v2/Checkbox.tsx +103 -0
- package/src/primitives-v2/IconButton.stories.tsx +40 -0
- package/src/primitives-v2/IconButton.tsx +40 -0
- package/src/primitives-v2/Input.stories.tsx +60 -0
- package/src/primitives-v2/Input.tsx +113 -0
- package/src/primitives-v2/NavGroup.stories.tsx +50 -0
- package/src/primitives-v2/NavGroup.tsx +76 -0
- package/src/primitives-v2/NavItem.stories.tsx +68 -0
- package/src/primitives-v2/NavItem.tsx +105 -0
- package/src/primitives-v2/SearchInput.stories.tsx +44 -0
- package/src/primitives-v2/SearchInput.tsx +123 -0
- package/src/primitives-v2/Sidebar.stories.tsx +91 -0
- package/src/primitives-v2/Sidebar.tsx +92 -0
- package/src/primitives-v2/Tab.stories.tsx +62 -0
- package/src/primitives-v2/Tab.tsx +148 -0
- package/src/primitives-v2/Tag.stories.tsx +123 -0
- package/src/primitives-v2/Tag.tsx +115 -0
- package/src/primitives-v2/Toggle.stories.tsx +37 -0
- package/src/primitives-v2/Toggle.tsx +93 -0
- package/src/primitives-v2/Topbar.stories.tsx +65 -0
- package/src/primitives-v2/Topbar.tsx +37 -0
- package/src/primitives-v2/index.ts +31 -0
- package/src/primitives-v2/utils.ts +9 -0
- package/src/theme/BrandThemeProvider.tsx +122 -0
- package/src/theme/QueryProvider.tsx +46 -0
- package/src/theme/ThemeProvider.tsx +120 -0
- package/src/theme/ThemeToggle.tsx +88 -0
- package/src/theme/index.ts +4 -0
- package/src/theme/m3.css +622 -0
- package/src/theme/tokens.v2.css +559 -0
- package/src/theme/typography.css +163 -0
- package/src/theme/typography.stories.tsx +157 -0
- package/src/theme/typography.tokens.ts +326 -0
- package/src/types/react-big-calendar.d.ts +16 -0
|
@@ -0,0 +1,1155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GridHarness — a user-perspective driver for DynamicTable.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS
|
|
5
|
+
* The grid's value is keyboard-and-clipboard flow: land on a cell, tab across,
|
|
6
|
+
* type, watch it save, drag-fill, undo, switch view. Existing specs reach into
|
|
7
|
+
* the DOM (`td[data-row="0"][data-col="4"]`) which couples every test to
|
|
8
|
+
* positional indices and to markup, and reads nothing about whether the *user*
|
|
9
|
+
* would have believed the interaction worked.
|
|
10
|
+
*
|
|
11
|
+
* This harness speaks in user intentions instead — `editCell`, `tabForward`,
|
|
12
|
+
* `expectSaved`, `copySelection`, `switchToView` — and resolves columns by their
|
|
13
|
+
* visible header text so specs never hard-code an index. It is deliberately
|
|
14
|
+
* assertion-light: it reports what the user can see and lets each spec assert.
|
|
15
|
+
*
|
|
16
|
+
* WHERE IT LIVES
|
|
17
|
+
* Ships next to the component so the two evolve together. `build.mjs` ignores
|
|
18
|
+
* every `__e2e__` directory so this never reaches the published bundle, but
|
|
19
|
+
* tsconfig still covers it, so it stays type-checked. Import it from any
|
|
20
|
+
* module's `__integration__` spec by relative path.
|
|
21
|
+
*
|
|
22
|
+
* DOM CONTRACT (as of @freighttech/ui 0.12.0 — update together with the component)
|
|
23
|
+
* .hot-container outer shell, carries data-density / data-striped / …
|
|
24
|
+
* .hot-virtual-container the scroller; tabIndex=0, receives all key events
|
|
25
|
+
* th[data-col] column header
|
|
26
|
+
* td[data-row][data-col] a cell; data-cell-selected / data-in-range /
|
|
27
|
+
* data-range-{top,bottom,left,right} / data-save-state
|
|
28
|
+
* tr[data-row] a row; data-is-new / data-row-checked / data-row-highlighted
|
|
29
|
+
* td[data-row-header=true] the row-header (checkbox) cell
|
|
30
|
+
* .fill-handle the drag-to-fill nub on a single active cell
|
|
31
|
+
* [role=tab][aria-selected] perspective (saved view) tabs
|
|
32
|
+
* .hot-bulk-bar grouped-actions bar, shown when ≥1 row is checked
|
|
33
|
+
* .hot-empty-message empty state
|
|
34
|
+
* th .hot-col-kebab per-column "Column options" trigger (modern layout)
|
|
35
|
+
* .hot-col-menu that menu, PORTALLED to document.body (outside .hot-container)
|
|
36
|
+
*/
|
|
37
|
+
import { mkdirSync, writeFileSync } from 'node:fs'
|
|
38
|
+
import { dirname, resolve } from 'node:path'
|
|
39
|
+
import { expect, type Locator, type Page, type Request } from '@playwright/test'
|
|
40
|
+
|
|
41
|
+
/** A resolved column: its visible header and the `data-col` index behind it. */
|
|
42
|
+
export interface GridColumn {
|
|
43
|
+
/** `data-col` attribute value — a string because that is how the DOM stores it. */
|
|
44
|
+
index: string
|
|
45
|
+
/** Header text as the user reads it. */
|
|
46
|
+
header: string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type SaveState = 'saving' | 'success' | 'error' | null
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The DOM event `applyCellWrites` emits once per multi-cell write.
|
|
53
|
+
* Re-declared here rather than imported so the harness keeps no runtime
|
|
54
|
+
* dependency on the component (it must be loadable from a Playwright process
|
|
55
|
+
* that never renders React). Kept in sync with
|
|
56
|
+
* `handlers/cellWrites.ts` → `CELL_BATCH_SAVE_RESULT_EVENT`.
|
|
57
|
+
*/
|
|
58
|
+
const BATCH_RESULT_EVENT = 'table:cell:batch:save:result'
|
|
59
|
+
|
|
60
|
+
/** Why a cell was refused — mirrors `CellWriteRejectReason`. */
|
|
61
|
+
export type BatchRejectReason =
|
|
62
|
+
| 'readOnly'
|
|
63
|
+
| 'notCoercible'
|
|
64
|
+
| 'notInSource'
|
|
65
|
+
| 'required'
|
|
66
|
+
| 'outOfRange'
|
|
67
|
+
|
|
68
|
+
/** One multi-cell write's outcome, as the grid reports it. */
|
|
69
|
+
export interface BatchWriteReport {
|
|
70
|
+
/** Cells whose value actually changed and were saved. */
|
|
71
|
+
applied: number
|
|
72
|
+
/** One entry per refused cell — never a silent drop. */
|
|
73
|
+
rejected: Array<{ rowIndex: number; colIndex: number; reason: BatchRejectReason }>
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** A write the grid issued to the server, captured for request-count assertions. */
|
|
77
|
+
export interface RecordedWrite {
|
|
78
|
+
method: string
|
|
79
|
+
url: string
|
|
80
|
+
/** Path only, query stripped — what you usually want to group by. */
|
|
81
|
+
path: string
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface GridHarnessOptions {
|
|
85
|
+
/**
|
|
86
|
+
* Scope the harness to one grid when a page renders several (drawers with
|
|
87
|
+
* chained sub-tables). Defaults to the first `.hot-container` on the page.
|
|
88
|
+
*/
|
|
89
|
+
root?: Locator
|
|
90
|
+
/** Search-box placeholder. The component passes 'Search...' regardless of locale. */
|
|
91
|
+
searchPlaceholder?: string
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const SAVE_SETTLE_MS = 2_000 // component clears a success state after 2s
|
|
95
|
+
|
|
96
|
+
export class GridHarness {
|
|
97
|
+
readonly page: Page
|
|
98
|
+
readonly root: Locator
|
|
99
|
+
/** The focusable scroller — every keyboard interaction must target this. */
|
|
100
|
+
readonly grid: Locator
|
|
101
|
+
private readonly searchPlaceholder: string
|
|
102
|
+
private writes: RecordedWrite[] = []
|
|
103
|
+
private recording = false
|
|
104
|
+
|
|
105
|
+
constructor(page: Page, options: GridHarnessOptions = {}) {
|
|
106
|
+
this.page = page
|
|
107
|
+
this.root = options.root ?? page.locator('.hot-container').first()
|
|
108
|
+
this.grid = this.root.locator('.hot-virtual-container')
|
|
109
|
+
this.searchPlaceholder = options.searchPlaceholder ?? 'Search...'
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Navigate to a list page and wait until its grid is interactive. */
|
|
113
|
+
static async open(page: Page, url: string, options: GridHarnessOptions = {}): Promise<GridHarness> {
|
|
114
|
+
await page.goto(url)
|
|
115
|
+
const harness = new GridHarness(page, options)
|
|
116
|
+
await harness.waitReady()
|
|
117
|
+
return harness
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Ready = the grid shell exists and it has either rendered rows or an explicit
|
|
122
|
+
* empty state. Without the empty-state branch this hangs on legitimately empty
|
|
123
|
+
* lists instead of failing usefully.
|
|
124
|
+
*/
|
|
125
|
+
async waitReady(timeout = 20_000): Promise<void> {
|
|
126
|
+
await expect(this.grid).toBeVisible({ timeout })
|
|
127
|
+
await expect
|
|
128
|
+
.poll(async () => (await this.rowCount()) > 0 || (await this.isEmpty()), { timeout })
|
|
129
|
+
.toBe(true)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Ready AND populated. Use this whenever the spec is about to interact with
|
|
134
|
+
* cells — `waitReady` deliberately accepts an empty grid, which then fails much
|
|
135
|
+
* later inside `clickCell` with an unreadable selector error. Measured on a real
|
|
136
|
+
* list that reported 66 records via its API but rendered no cells, where the
|
|
137
|
+
* failure surfaced four steps downstream from the actual problem.
|
|
138
|
+
*/
|
|
139
|
+
async waitForRows(minimum = 1, timeout = 20_000): Promise<number> {
|
|
140
|
+
await expect(this.grid).toBeVisible({ timeout })
|
|
141
|
+
try {
|
|
142
|
+
await expect.poll(async () => this.rowCount(), { timeout }).toBeGreaterThanOrEqual(minimum)
|
|
143
|
+
} catch {
|
|
144
|
+
const empty = await this.isEmpty()
|
|
145
|
+
throw new Error(
|
|
146
|
+
`GridHarness: expected at least ${minimum} row(s) at ${this.page.url()} but the grid rendered ` +
|
|
147
|
+
`${await this.rowCount()}${empty ? ' and is showing its empty state' : ''}. ` +
|
|
148
|
+
`Check the list has data for the active organization scope, and that any search filter matches.`,
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
return this.rowCount()
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ── What the user sees ────────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
async isEmpty(): Promise<boolean> {
|
|
157
|
+
return (await this.root.locator('.hot-empty-message').count()) > 0
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Rows currently rendered. Virtualised — this is what is on screen, not the dataset. */
|
|
161
|
+
async rowCount(): Promise<number> {
|
|
162
|
+
return this.grid.locator('tr[data-row]').count()
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Resolve a column by its visible header. Accepts a regex so a spec can match
|
|
167
|
+
* both locales at once (`/Nr faktury|Invoice no/i`) — the demo session's
|
|
168
|
+
* language is not fixed, and hard-coding one locale makes tests flaky by design.
|
|
169
|
+
*/
|
|
170
|
+
async column(header: RegExp | string): Promise<GridColumn> {
|
|
171
|
+
const re = typeof header === 'string' ? new RegExp(escapeRegExp(header), 'i') : header
|
|
172
|
+
const headers = this.root.locator('th[data-col]')
|
|
173
|
+
const n = await headers.count()
|
|
174
|
+
const seen: string[] = []
|
|
175
|
+
for (let i = 0; i < n; i++) {
|
|
176
|
+
const th = headers.nth(i)
|
|
177
|
+
const text = ((await th.textContent()) ?? '').trim()
|
|
178
|
+
seen.push(text)
|
|
179
|
+
if (re.test(text)) {
|
|
180
|
+
const index = await th.getAttribute('data-col')
|
|
181
|
+
if (index != null) return { index, header: text }
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
throw new Error(`GridHarness: no column header matched ${re}. Saw: ${seen.map((s) => JSON.stringify(s)).join(', ')}`)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
cell(row: number, col: GridColumn | string): Locator {
|
|
188
|
+
return this.grid.locator(`td[data-row="${row}"][data-col="${colIndexOf(col)}"]`)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
row(row: number): Locator {
|
|
192
|
+
return this.grid.locator(`tr[data-row="${row}"]`)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** The text a user reads in a cell — renderer output included, trimmed. */
|
|
196
|
+
async readCell(row: number, col: GridColumn | string): Promise<string> {
|
|
197
|
+
return ((await this.cell(row, col).textContent()) ?? '').trim()
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Every rendered value in a column, top to bottom. */
|
|
201
|
+
async readColumn(col: GridColumn | string): Promise<string[]> {
|
|
202
|
+
const cells = this.grid.locator(`td[data-col="${colIndexOf(col)}"]`)
|
|
203
|
+
const n = await cells.count()
|
|
204
|
+
const out: string[] = []
|
|
205
|
+
for (let i = 0; i < n; i++) out.push(((await cells.nth(i).textContent()) ?? '').trim())
|
|
206
|
+
return out
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** A row keyed by header text — how a user would describe what they see. */
|
|
210
|
+
async readRowByHeader(row: number): Promise<Record<string, string>> {
|
|
211
|
+
const headers = this.root.locator('th[data-col]')
|
|
212
|
+
const n = await headers.count()
|
|
213
|
+
const out: Record<string, string> = {}
|
|
214
|
+
for (let i = 0; i < n; i++) {
|
|
215
|
+
const th = headers.nth(i)
|
|
216
|
+
const index = await th.getAttribute('data-col')
|
|
217
|
+
if (index == null) continue
|
|
218
|
+
const label = ((await th.textContent()) ?? '').trim()
|
|
219
|
+
if (!label) continue
|
|
220
|
+
out[label] = await this.readCell(row, index)
|
|
221
|
+
}
|
|
222
|
+
return out
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ── Navigating, the way a user does ───────────────────────────────────
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* True when the host passed `onRowClick`, i.e. clicking a cell NAVIGATES to the
|
|
229
|
+
* record instead of selecting the cell. Measured on a real contrahents list,
|
|
230
|
+
* where `clickCell` silently left the list for `/backend/contrahents/<id>` and
|
|
231
|
+
* every later assertion failed for an unrelated-looking reason.
|
|
232
|
+
*/
|
|
233
|
+
async hasClickableRows(): Promise<boolean> {
|
|
234
|
+
return (await this.root.getAttribute('data-clickable-rows')) === 'true'
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Click a cell to select it. Refuses on clickable-row lists, where a click
|
|
239
|
+
* navigates — use {@link focusCell} there instead.
|
|
240
|
+
*/
|
|
241
|
+
async clickCell(row: number, col: GridColumn | string): Promise<void> {
|
|
242
|
+
this.note(`clicked cell row ${row}, column ${describeCol(col)}`)
|
|
243
|
+
const target = this.cell(row, col)
|
|
244
|
+
if ((await target.count()) === 0) {
|
|
245
|
+
throw new Error(
|
|
246
|
+
`GridHarness: no cell at row ${row}, column ${describeCol(col)} — the grid has ` +
|
|
247
|
+
`${await this.rowCount()} rendered row(s) at ${this.page.url()}. ` +
|
|
248
|
+
`Call waitForRows() before interacting with cells.`,
|
|
249
|
+
)
|
|
250
|
+
}
|
|
251
|
+
if (await this.hasClickableRows()) {
|
|
252
|
+
throw new Error(
|
|
253
|
+
`GridHarness: this list has clickable rows (onRowClick), so clicking a cell navigates to the ` +
|
|
254
|
+
`record instead of selecting it. Use focusCell(${row}, ${describeCol(col)}) — keyboard ` +
|
|
255
|
+
`selection — or call the navigation deliberately with cell(...).click().`,
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
await target.click()
|
|
259
|
+
await expect(target).toHaveAttribute('data-in-range', 'true')
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Select a cell using the keyboard only — safe on every list, including those
|
|
264
|
+
* whose rows navigate on click. Seeds a selection by focusing the grid and
|
|
265
|
+
* pressing an arrow (the component selects the first cell on directional entry),
|
|
266
|
+
* then walks to the target.
|
|
267
|
+
*/
|
|
268
|
+
async focusCell(row: number, col: GridColumn | string): Promise<void> {
|
|
269
|
+
this.note(`keyboard-selected row ${row}, column ${describeCol(col)}`)
|
|
270
|
+
const targetCol = Number(colIndexOf(col))
|
|
271
|
+
if (Number.isNaN(targetCol)) {
|
|
272
|
+
throw new Error(`GridHarness: focusCell needs a numeric column index, got ${colIndexOf(col)}`)
|
|
273
|
+
}
|
|
274
|
+
await this.grid.focus()
|
|
275
|
+
// Directional entry seeds the selection at the first cell.
|
|
276
|
+
await this.press('ArrowDown')
|
|
277
|
+
const seeded = await this.selectedRange()
|
|
278
|
+
if (!seeded) {
|
|
279
|
+
throw new Error(
|
|
280
|
+
`GridHarness: the grid did not take keyboard focus at ${this.page.url()} — ` +
|
|
281
|
+
`it may not be interactive yet (call waitForRows() first).`,
|
|
282
|
+
)
|
|
283
|
+
}
|
|
284
|
+
let currentRow = Math.min(...seeded.rows)
|
|
285
|
+
let currentCol = Math.min(...seeded.cols.map(Number))
|
|
286
|
+
if (row > currentRow) await this.arrow('Down', row - currentRow)
|
|
287
|
+
else if (row < currentRow) await this.arrow('Up', currentRow - row)
|
|
288
|
+
if (targetCol > currentCol) await this.arrow('Right', targetCol - currentCol)
|
|
289
|
+
else if (targetCol < currentCol) await this.arrow('Left', currentCol - targetCol)
|
|
290
|
+
await expect(
|
|
291
|
+
this.cell(row, col),
|
|
292
|
+
`keyboard navigation should have landed on row ${row}, column ${describeCol(col)}`,
|
|
293
|
+
).toHaveAttribute('data-in-range', 'true')
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** Which cell is selected right now, or null. Reads the component's own markers. */
|
|
297
|
+
async selectedCell(): Promise<{ row: number; col: string } | null> {
|
|
298
|
+
const selected = this.grid.locator('td[data-cell-selected="true"]').first()
|
|
299
|
+
if ((await selected.count()) === 0) return null
|
|
300
|
+
const row = await selected.getAttribute('data-row')
|
|
301
|
+
const col = await selected.getAttribute('data-col')
|
|
302
|
+
return row != null && col != null ? { row: Number(row), col } : null
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** The selected rectangle in cell coordinates, or null when nothing is selected. */
|
|
306
|
+
async selectedRange(): Promise<{ rows: number[]; cols: string[] } | null> {
|
|
307
|
+
const inRange = this.grid.locator('td[data-in-range="true"]')
|
|
308
|
+
const n = await inRange.count()
|
|
309
|
+
if (n === 0) return null
|
|
310
|
+
const rows = new Set<number>()
|
|
311
|
+
const cols = new Set<string>()
|
|
312
|
+
for (let i = 0; i < n; i++) {
|
|
313
|
+
const td = inRange.nth(i)
|
|
314
|
+
const r = await td.getAttribute('data-row')
|
|
315
|
+
const c = await td.getAttribute('data-col')
|
|
316
|
+
if (r != null) rows.add(Number(r))
|
|
317
|
+
if (c != null) cols.add(c)
|
|
318
|
+
}
|
|
319
|
+
return { rows: [...rows].sort((a, b) => a - b), cols: [...cols] }
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Send a key to the grid.
|
|
324
|
+
*
|
|
325
|
+
* CRITICAL: when an editor is open the keystroke must go to the FOCUSED element,
|
|
326
|
+
* not the container. `locator.press()` focuses its target first, so pressing on
|
|
327
|
+
* the container blurs the open editor — the editor's blur handler then saves and
|
|
328
|
+
* clears the editing state, and the container's keydown handler consequently sees
|
|
329
|
+
* `editing === null` and takes the wrong branch.
|
|
330
|
+
*
|
|
331
|
+
* That bug made Enter-commit look like it never moved down a row, and made the
|
|
332
|
+
* two-step Escape look like a one-step clear. Both were harness artifacts; the
|
|
333
|
+
* grid behaves correctly when the keystroke reaches the editor, as it does for a
|
|
334
|
+
* real user. Verified by hand: Enter at (0,0) → editor at (0,0); Enter again →
|
|
335
|
+
* selection AND editor at (1,0). Escape → editor closes, selection survives;
|
|
336
|
+
* Escape again → selection clears.
|
|
337
|
+
*/
|
|
338
|
+
async press(key: string): Promise<void> {
|
|
339
|
+
if (await this.isAnyEditorOpen()) {
|
|
340
|
+
// Goes to document.activeElement — the editor — without stealing focus.
|
|
341
|
+
await this.page.keyboard.press(key)
|
|
342
|
+
return
|
|
343
|
+
}
|
|
344
|
+
await this.grid.press(key)
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async tabForward(times = 1): Promise<void> {
|
|
348
|
+
for (let i = 0; i < times; i++) await this.press('Tab')
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async tabBackward(times = 1): Promise<void> {
|
|
352
|
+
for (let i = 0; i < times; i++) await this.press('Shift+Tab')
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/** Walk with arrow keys only — real traversal, not a click shortcut. */
|
|
356
|
+
async arrow(direction: 'Up' | 'Down' | 'Left' | 'Right', times = 1): Promise<void> {
|
|
357
|
+
this.note(`pressed Arrow${direction} x${times}`)
|
|
358
|
+
for (let i = 0; i < times; i++) await this.press(`Arrow${direction}`)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Extend the current selection with Shift+Arrow — the spreadsheet way to grow a
|
|
363
|
+
* range from the keyboard.
|
|
364
|
+
*/
|
|
365
|
+
async shiftArrow(direction: 'Up' | 'Down' | 'Left' | 'Right', times = 1): Promise<void> {
|
|
366
|
+
this.note(`pressed Shift+Arrow${direction} x${times}`)
|
|
367
|
+
for (let i = 0; i < times; i++) await this.press(`Shift+Arrow${direction}`)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** Right-click a cell to open its context menu. */
|
|
371
|
+
async rightClickCell(row: number, col: GridColumn | string): Promise<void> {
|
|
372
|
+
this.note(`right-clicked row ${row}, column ${describeCol(col)}`)
|
|
373
|
+
await this.cell(row, col).click({ button: 'right' })
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Labels currently offered by an open context menu. `ContextMenu` portals to the
|
|
378
|
+
* body, so this queries the page rather than the grid container.
|
|
379
|
+
*/
|
|
380
|
+
async contextMenuLabels(): Promise<string[]> {
|
|
381
|
+
const menu = this.page.locator('.hot-context-menu, .context-menu').last()
|
|
382
|
+
if ((await menu.count()) === 0) return []
|
|
383
|
+
const texts = await menu.locator('button, [role="menuitem"], li').allTextContents()
|
|
384
|
+
return texts.map((t) => t.trim()).filter(Boolean)
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Coordinates of the cell whose editor is currently open, or null. Distinct from
|
|
389
|
+
* `selectedCell` — Tab and Enter both move the selection AND (when
|
|
390
|
+
* `autoEditOnTab` is on) open an editor, and those two can legitimately diverge.
|
|
391
|
+
*/
|
|
392
|
+
async editingCellCoords(): Promise<{ row: number; col: string } | null> {
|
|
393
|
+
// Every editor variant renders an in-cell element classed `hot-cell-editor`
|
|
394
|
+
// (text, numeric, date, dropdown, multiselect) — matching on `input` alone
|
|
395
|
+
// misses the variants that render a div/button and their portalled popups.
|
|
396
|
+
const editing = this.grid.locator('td:has(.hot-cell-editor)').first()
|
|
397
|
+
if ((await editing.count()) === 0) return null
|
|
398
|
+
const row = await editing.getAttribute('data-row')
|
|
399
|
+
const col = await editing.getAttribute('data-col')
|
|
400
|
+
return row != null && col != null ? { row: Number(row), col } : null
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/** True while any editor is open — including one whose popup is portalled out. */
|
|
404
|
+
async isAnyEditorOpen(): Promise<boolean> {
|
|
405
|
+
if ((await this.grid.locator('.hot-cell-editor').count()) > 0) return true
|
|
406
|
+
return (await this.page.locator('.hot-editor-popup').count()) > 0
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* `data-col` indices of every read-only column, resolved from the rendered cells.
|
|
411
|
+
* Tab skips these, so a spec that wants to predict where Tab lands must know them
|
|
412
|
+
* rather than assume the next column is editable.
|
|
413
|
+
*/
|
|
414
|
+
async readOnlyColumnIndices(): Promise<string[]> {
|
|
415
|
+
return this.grid.evaluate((gridEl) => {
|
|
416
|
+
const seen = new Map<string, boolean>()
|
|
417
|
+
gridEl.querySelectorAll('td[data-col]').forEach((td) => {
|
|
418
|
+
const col = td.getAttribute('data-col')
|
|
419
|
+
if (col == null || seen.has(col)) return
|
|
420
|
+
seen.set(col, td.classList.contains('read-only'))
|
|
421
|
+
})
|
|
422
|
+
return [...seen.entries()].filter(([, ro]) => ro).map(([col]) => col)
|
|
423
|
+
})
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/** True when this column is declared read-only (cells carry `.read-only`). */
|
|
427
|
+
async isColumnReadOnly(col: GridColumn | string): Promise<boolean> {
|
|
428
|
+
const first = this.grid.locator(`td[data-col="${colIndexOf(col)}"]`).first()
|
|
429
|
+
const cls = (await first.getAttribute('class')) ?? ''
|
|
430
|
+
return cls.includes('read-only')
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// ── Editing ───────────────────────────────────────────────────────────
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Edit a cell the way a user does: double-click to open the editor, replace the
|
|
437
|
+
* text, commit with Enter. Does NOT wait for persistence — call `expectSaved`
|
|
438
|
+
* or assert against the API, so the spec stays explicit about what it trusts.
|
|
439
|
+
*/
|
|
440
|
+
async editCell(row: number, col: GridColumn | string, text: string): Promise<void> {
|
|
441
|
+
this.note(`typed ${JSON.stringify(text)} into row ${row}, column ${describeCol(col)}`)
|
|
442
|
+
const target = this.cell(row, col)
|
|
443
|
+
await target.dblclick()
|
|
444
|
+
const editor = target.locator('input, textarea').first()
|
|
445
|
+
await expect(editor).toBeVisible({ timeout: 5_000 })
|
|
446
|
+
await editor.fill(text)
|
|
447
|
+
await editor.press('Enter')
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/** True while an editor is open in this cell. */
|
|
451
|
+
async isEditing(row: number, col: GridColumn | string): Promise<boolean> {
|
|
452
|
+
return (await this.cell(row, col).locator('input, textarea').count()) > 0
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/** The cell's save indicator: 'saving' → 'success' | 'error', then cleared. */
|
|
456
|
+
async saveState(row: number, col: GridColumn | string): Promise<SaveState> {
|
|
457
|
+
const value = await this.cell(row, col).getAttribute('data-save-state')
|
|
458
|
+
return (value as SaveState) ?? null
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Wait until the grid itself says the cell saved. This is the signal the *user*
|
|
463
|
+
* sees, which is the point — a spec that only checks the API can pass while the
|
|
464
|
+
* user is still staring at a spinner.
|
|
465
|
+
*
|
|
466
|
+
* Note the success state self-clears after 2s, so a slow assertion can miss it;
|
|
467
|
+
* this polls for success-or-cleared and fails loudly on 'error'.
|
|
468
|
+
*/
|
|
469
|
+
async expectSaved(row: number, col: GridColumn | string, timeout = 15_000): Promise<void> {
|
|
470
|
+
await expect
|
|
471
|
+
.poll(async () => this.saveState(row, col), { timeout })
|
|
472
|
+
.not.toBe('saving')
|
|
473
|
+
const final = await this.saveState(row, col)
|
|
474
|
+
if (final === 'error') {
|
|
475
|
+
throw new Error(`GridHarness: cell (${row}, ${colIndexOf(col)}) reported a save error to the user`)
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// ── Selection & clipboard ─────────────────────────────────────────────
|
|
480
|
+
|
|
481
|
+
/** Drag from the cell BODY to select a range (never touches the fill nub). */
|
|
482
|
+
async selectRange(
|
|
483
|
+
from: { row: number; col: GridColumn | string },
|
|
484
|
+
to: { row: number; col: GridColumn | string },
|
|
485
|
+
): Promise<void> {
|
|
486
|
+
this.note(`drag-selected from row ${from.row} to row ${to.row} in column ${describeCol(to.col)}`)
|
|
487
|
+
await this.cell(from.row, from.col).dragTo(this.cell(to.row, to.col))
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/** Tick a row's checkbox via the row header — drives the bulk-actions bar. */
|
|
491
|
+
async checkRow(row: number): Promise<void> {
|
|
492
|
+
this.note(`ticked the checkbox on row ${row}`)
|
|
493
|
+
await this.row(row).locator('td[data-row-header="true"] input[type="checkbox"]').first().check()
|
|
494
|
+
await expect(this.row(row)).toHaveAttribute('data-row-checked', 'true')
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
async checkedRowCount(): Promise<number> {
|
|
498
|
+
return this.grid.locator('tr[data-row-checked="true"]').count()
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
async isBulkBarVisible(): Promise<boolean> {
|
|
502
|
+
return this.root.locator('.hot-bulk-bar-wrap.is-open').count().then((n) => n > 0)
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Copy the current selection with Ctrl/Cmd+C and return what landed on the
|
|
507
|
+
* clipboard. Requires clipboard permissions on the context — see
|
|
508
|
+
* `grantClipboard()`.
|
|
509
|
+
*/
|
|
510
|
+
async copySelection(): Promise<string> {
|
|
511
|
+
this.note('pressed Copy on the selection')
|
|
512
|
+
await this.press(modifier() + '+c')
|
|
513
|
+
return this.readClipboard()
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
async readClipboard(): Promise<string> {
|
|
517
|
+
return this.page.evaluate(() => navigator.clipboard.readText())
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Paste a TSV block at the current selection, simulating a real Excel paste:
|
|
522
|
+
* a `paste` ClipboardEvent carrying both `text/plain` and an HTML table, which
|
|
523
|
+
* is what Excel and Google Sheets actually put on the clipboard.
|
|
524
|
+
*
|
|
525
|
+
* Ships on every DynamicTable page via a document-level handler; the grid
|
|
526
|
+
* reads the HTML flavour first because Excel's `text/plain` mangles any cell
|
|
527
|
+
* containing a tab or a newline.
|
|
528
|
+
*/
|
|
529
|
+
async pasteTsv(tsv: string): Promise<void> {
|
|
530
|
+
this.note(`pasted a ${tsv.split('\n').length}x${(tsv.split('\n')[0] ?? '').split('\t').length} block from the clipboard`)
|
|
531
|
+
const html =
|
|
532
|
+
'<table>' +
|
|
533
|
+
tsv
|
|
534
|
+
.split('\n')
|
|
535
|
+
.map((line) => '<tr>' + line.split('\t').map((c) => `<td>${escapeHtml(c)}</td>`).join('') + '</tr>')
|
|
536
|
+
.join('') +
|
|
537
|
+
'</table>'
|
|
538
|
+
await this.grid.evaluate(
|
|
539
|
+
(el, payload) => {
|
|
540
|
+
const dt = new DataTransfer()
|
|
541
|
+
dt.setData('text/plain', payload.tsv)
|
|
542
|
+
dt.setData('text/html', payload.html)
|
|
543
|
+
el.dispatchEvent(new ClipboardEvent('paste', { clipboardData: dt, bubbles: true, cancelable: true }))
|
|
544
|
+
},
|
|
545
|
+
{ tsv, html },
|
|
546
|
+
)
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* `Ctrl/⌘+X` on the current selection: copy to the clipboard, then clear the
|
|
551
|
+
* source — ONE undo entry.
|
|
552
|
+
*
|
|
553
|
+
* Dispatches a real `cut` ClipboardEvent rather than pressing the key
|
|
554
|
+
* combination, for the same reason `pasteTsv` does: Chromium only turns
|
|
555
|
+
* `Ctrl+X` into a clipboard event when the keystroke comes from the OS, and a
|
|
556
|
+
* synthesised one never reaches the renderer's editing commands. The grid
|
|
557
|
+
* listens for the DOM event, so this exercises the real handler.
|
|
558
|
+
*
|
|
559
|
+
* Returns what the grid put on the clipboard, so a test can assert that a cut
|
|
560
|
+
* and a copy place byte-identical text.
|
|
561
|
+
*/
|
|
562
|
+
async cut(): Promise<string> {
|
|
563
|
+
this.note('pressed Cut (Ctrl/⌘+X)')
|
|
564
|
+
return this.grid.evaluate((el) => {
|
|
565
|
+
const dt = new DataTransfer()
|
|
566
|
+
el.dispatchEvent(new ClipboardEvent('cut', { clipboardData: dt, bubbles: true, cancelable: true }))
|
|
567
|
+
return dt.getData('text/plain')
|
|
568
|
+
})
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* `Delete` on the current selection: clear cell CONTENTS. The row is never
|
|
573
|
+
* deleted — that stays on `Ctrl/⌘+D` and the grouped-actions bar.
|
|
574
|
+
*/
|
|
575
|
+
async clearSelectionContents(key: 'Delete' | 'Backspace' = 'Delete'): Promise<void> {
|
|
576
|
+
this.note(`pressed ${key} to clear the selected cells`)
|
|
577
|
+
await this.press(key)
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
async undo(): Promise<void> {
|
|
581
|
+
this.note('pressed Undo')
|
|
582
|
+
await this.press(modifier() + '+z')
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
async redo(): Promise<void> {
|
|
586
|
+
this.note('pressed Redo')
|
|
587
|
+
await this.press(modifier() + '+Shift+z')
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// ── Fill handle ───────────────────────────────────────────────────────
|
|
591
|
+
|
|
592
|
+
/** Is the drag-to-fill nub offered on the currently selected cell? */
|
|
593
|
+
async hasFillHandle(): Promise<boolean> {
|
|
594
|
+
return (await this.root.locator('.fill-handle').count()) > 0
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/** Select the source cell, then drag its nub to `toRow` (up or down). */
|
|
598
|
+
async fillFrom(fromRow: number, toRow: number, col: GridColumn | string): Promise<void> {
|
|
599
|
+
this.note(`dragged the fill nub from row ${fromRow} to row ${toRow} in column ${describeCol(col)}`)
|
|
600
|
+
await this.clickCell(fromRow, col)
|
|
601
|
+
const nub = this.root.locator('.fill-handle')
|
|
602
|
+
await expect(nub, 'fill nub should be offered on an editable single-cell selection').toBeVisible({
|
|
603
|
+
timeout: 5_000,
|
|
604
|
+
})
|
|
605
|
+
await nub.dragTo(this.cell(toRow, col))
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Drag the nub to an arbitrary CELL — the rectangle model, so `to` may be in a
|
|
610
|
+
* different column, above, or back inside the source (which clears).
|
|
611
|
+
*
|
|
612
|
+
* `steps` matters: a single-jump drag can miss the intermediate `mousemove`
|
|
613
|
+
* the preview is computed on, so the release would commit a stale rectangle.
|
|
614
|
+
*/
|
|
615
|
+
async fillTo(
|
|
616
|
+
to: { row: number; col: GridColumn | string },
|
|
617
|
+
options: { modifier?: boolean } = {},
|
|
618
|
+
): Promise<void> {
|
|
619
|
+
this.note(`dragged the fill nub to row ${to.row}, column ${describeCol(to.col)}`)
|
|
620
|
+
const nub = this.root.locator('.fill-handle')
|
|
621
|
+
await expect(nub, 'fill nub should be offered on a fillable selection').toBeVisible({ timeout: 5_000 })
|
|
622
|
+
const from = await nub.boundingBox()
|
|
623
|
+
const target = await this.cell(to.row, to.col).boundingBox()
|
|
624
|
+
if (!from || !target) throw new Error('GridHarness: could not measure the fill drag endpoints')
|
|
625
|
+
await this.page.mouse.move(from.x + from.width / 2, from.y + from.height / 2)
|
|
626
|
+
await this.page.mouse.down()
|
|
627
|
+
if (options.modifier) await this.page.keyboard.down('Alt')
|
|
628
|
+
await this.page.mouse.move(target.x + target.width / 2, target.y + target.height / 2, { steps: 12 })
|
|
629
|
+
await this.page.mouse.up()
|
|
630
|
+
if (options.modifier) await this.page.keyboard.up('Alt')
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* The fill preview rectangle, read from the PER-CELL attributes rather than a
|
|
635
|
+
* marquee element — the attributes are deliberately the test surface.
|
|
636
|
+
*/
|
|
637
|
+
async readFillPreview(): Promise<{
|
|
638
|
+
cells: Array<{ row: number; col: number }>
|
|
639
|
+
targets: Array<{ row: number; col: number }>
|
|
640
|
+
clearing: Array<{ row: number; col: number }>
|
|
641
|
+
edges: { top: number; bottom: number; left: number; right: number }
|
|
642
|
+
}> {
|
|
643
|
+
return this.root.evaluate((root) => {
|
|
644
|
+
const read = (selector: string) =>
|
|
645
|
+
Array.from(root.querySelectorAll<HTMLElement>(selector)).map((td) => ({
|
|
646
|
+
row: Number(td.getAttribute('data-row')),
|
|
647
|
+
col: Number(td.getAttribute('data-col')),
|
|
648
|
+
}))
|
|
649
|
+
return {
|
|
650
|
+
cells: read('td[data-fill-preview="true"]'),
|
|
651
|
+
targets: read('td[data-fill-target="true"]'),
|
|
652
|
+
clearing: read('td[data-fill-clear="true"]'),
|
|
653
|
+
edges: {
|
|
654
|
+
top: root.querySelectorAll('td[data-fill-preview="true"][data-fill-top="true"]').length,
|
|
655
|
+
bottom: root.querySelectorAll('td[data-fill-preview="true"][data-fill-bottom="true"]').length,
|
|
656
|
+
left: root.querySelectorAll('td[data-fill-preview="true"][data-fill-left="true"]').length,
|
|
657
|
+
right: root.querySelectorAll('td[data-fill-preview="true"][data-fill-right="true"]').length,
|
|
658
|
+
},
|
|
659
|
+
}
|
|
660
|
+
})
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// ── Search, sort, views ───────────────────────────────────────────────
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Type into the list search and wait until the row count settles at `expected`.
|
|
667
|
+
* The standard way to isolate a spec's own fixtures from demo data.
|
|
668
|
+
*/
|
|
669
|
+
async searchFor(text: string, expected?: number): Promise<void> {
|
|
670
|
+
this.note(`searched for ${JSON.stringify(text)}`)
|
|
671
|
+
const box = this.root.getByPlaceholder(this.searchPlaceholder)
|
|
672
|
+
await box.fill('')
|
|
673
|
+
await box.fill(text)
|
|
674
|
+
if (expected != null) {
|
|
675
|
+
await expect.poll(async () => this.rowCount(), { timeout: 20_000 }).toBe(expected)
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Click a column header directly. NOTE: in the default (modern) layout this does
|
|
681
|
+
* NOT sort — the header renders only a label and a kebab; the click-to-sort code
|
|
682
|
+
* path belongs to the legacy layout. Use {@link sortViaColumnMenu} for the real
|
|
683
|
+
* user path. Kept because embedded tables may opt into the legacy layout via
|
|
684
|
+
* `uiConfig.disableBuiltinColumnMenu`.
|
|
685
|
+
*/
|
|
686
|
+
async clickHeader(col: GridColumn): Promise<void> {
|
|
687
|
+
this.note(`clicked the "${col.header}" header`)
|
|
688
|
+
await this.root.locator(`th[data-col="${col.index}"]`).click()
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* Sort a column the way a user actually does in the shipped UI: open the
|
|
693
|
+
* column's kebab ("Column options") and choose a sort direction.
|
|
694
|
+
*
|
|
695
|
+
* Verified against @freighttech/ui 0.12.0, where the menu offers
|
|
696
|
+
* "Sort A → Z" / "Sort Z → A" (labels vary by column data type — see
|
|
697
|
+
* `getSortDirectionLabels`, e.g. "Old → New" for dates), so the direction is
|
|
698
|
+
* matched positionally rather than by label text.
|
|
699
|
+
*/
|
|
700
|
+
async sortViaColumnMenu(col: GridColumn, direction: 'asc' | 'desc'): Promise<void> {
|
|
701
|
+
this.note(`sorted "${col.header}" ${direction} via the column menu`)
|
|
702
|
+
await this.root.locator(`th[data-col="${col.index}"] .hot-col-kebab`).click()
|
|
703
|
+
// ColumnHeaderMenu portals to document.body as `.hot-col-menu`, so it is
|
|
704
|
+
// outside `this.root` — query it from the page, not the grid container.
|
|
705
|
+
const items = this.page.locator('.hot-col-menu .hot-col-menu-item')
|
|
706
|
+
await expect(items.first(), 'the column options menu should open').toBeVisible({ timeout: 5_000 })
|
|
707
|
+
// Sort asc / desc are the menu's first two entries; their labels vary with
|
|
708
|
+
// the column's data type (A→Z, Old→New, 1→9), so match positionally.
|
|
709
|
+
await items.nth(direction === 'asc' ? 0 : 1).click()
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Whether ANY header communicates a sort to the user — glyph or `aria-sort`.
|
|
714
|
+
*
|
|
715
|
+
* As of 0.12.0 this is `false` even immediately after sorting: the ↑/↓/⇅ glyphs
|
|
716
|
+
* live in the legacy header branch, and nothing renders an indicator for the
|
|
717
|
+
* modern layout's `sortRules`. No `aria-sort` is emitted either, so the state is
|
|
718
|
+
* invisible to sighted and screen-reader users alike. Kept as a first-class
|
|
719
|
+
* query so a spec can pin the gap and fail loudly when it is closed.
|
|
720
|
+
*/
|
|
721
|
+
async sortIndicatorShownAnywhere(): Promise<{ glyph: boolean; ariaSort: boolean }> {
|
|
722
|
+
return this.root.evaluate((rootEl) => {
|
|
723
|
+
const headers = Array.from(rootEl.querySelectorAll('th[data-col]'))
|
|
724
|
+
const text = headers.map((h) => h.textContent ?? '').join('')
|
|
725
|
+
return {
|
|
726
|
+
glyph: text.includes('\u2191') || text.includes('\u2193'),
|
|
727
|
+
ariaSort: headers.some((h) => h.getAttribute('aria-sort')),
|
|
728
|
+
}
|
|
729
|
+
})
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* The sort direction the header is *showing* the user (↑ / ↓ / neither).
|
|
734
|
+
* Reads the glyph rather than internal state, which is the point: this is what
|
|
735
|
+
* makes the two-sources-of-truth sort defect observable from outside.
|
|
736
|
+
*/
|
|
737
|
+
async shownSortDirection(col: GridColumn): Promise<'asc' | 'desc' | null> {
|
|
738
|
+
const text = ((await this.root.locator(`th[data-col="${col.index}"]`).textContent()) ?? '')
|
|
739
|
+
if (text.includes('↑')) return 'asc'
|
|
740
|
+
if (text.includes('↓')) return 'desc'
|
|
741
|
+
return null
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// ── Per-column quick filter (Excel AutoFilter — workshop A1) ──────────
|
|
745
|
+
//
|
|
746
|
+
// The funnel in the column header. This is the route the workshop asked for
|
|
747
|
+
// by name; the Configure View drawer is the "advanced" route and is NOT what
|
|
748
|
+
// these verbs drive. The dropdown portals to `document.body`, so it is
|
|
749
|
+
// queried from the PAGE, not from `this.root`.
|
|
750
|
+
|
|
751
|
+
/** Open a column's funnel dropdown and wait for it to be on screen. */
|
|
752
|
+
async openColumnFilter(col: GridColumn): Promise<void> {
|
|
753
|
+
this.note(`opened the "${col.header}" column filter`)
|
|
754
|
+
await this.root.locator(`th[data-col="${col.index}"] .hot-col-funnel`).click()
|
|
755
|
+
await expect(
|
|
756
|
+
this.page.locator('.hot-quick-filter'),
|
|
757
|
+
'the column quick filter should open',
|
|
758
|
+
).toBeVisible({ timeout: 5_000 })
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/** Values the funnel is offering, in the order they are listed. */
|
|
762
|
+
async columnFilterValues(): Promise<string[]> {
|
|
763
|
+
// Settle the debounced suggestion fetch before reading the list, otherwise
|
|
764
|
+
// a fast assertion reads the loading state as "no values".
|
|
765
|
+
await expect
|
|
766
|
+
.poll(
|
|
767
|
+
async () =>
|
|
768
|
+
this.page.locator('.hot-quick-filter-list').getAttribute('data-suggestions-state'),
|
|
769
|
+
{ timeout: 10_000 },
|
|
770
|
+
)
|
|
771
|
+
.not.toBe('loading')
|
|
772
|
+
return this.page.locator('.hot-quick-filter-option-label').allTextContents()
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/** Type into the funnel's search box. The needle goes to the server. */
|
|
776
|
+
async searchColumnFilter(text: string): Promise<void> {
|
|
777
|
+
this.note(`searched the column filter for ${JSON.stringify(text)}`)
|
|
778
|
+
await this.page.locator('.hot-quick-filter-search input').fill(text)
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/** Tick one value by its visible label. */
|
|
782
|
+
async tickColumnFilterValue(value: string): Promise<void> {
|
|
783
|
+
this.note(`ticked ${JSON.stringify(value)}`)
|
|
784
|
+
await this.page.locator(`[data-quick-filter-value="${value}"]`).click()
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/** Tick / untick everything currently listed ("Select all"). */
|
|
788
|
+
async tickAllColumnFilterValues(): Promise<void> {
|
|
789
|
+
await this.page.locator('[data-quick-filter-all]').click()
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
/** Commit the ticked values as an `is_any_of` rule and close the dropdown. */
|
|
793
|
+
async applyColumnFilter(): Promise<void> {
|
|
794
|
+
this.note('applied the column filter')
|
|
795
|
+
await this.page.locator('[data-quick-filter-apply]').click()
|
|
796
|
+
await expect(this.page.locator('.hot-quick-filter')).toBeHidden({ timeout: 5_000 })
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/** Drop this column's rule entirely. */
|
|
800
|
+
async clearColumnFilter(): Promise<void> {
|
|
801
|
+
this.note('cleared the column filter')
|
|
802
|
+
await this.page.locator('[data-quick-filter-clear]').click()
|
|
803
|
+
await expect(this.page.locator('.hot-quick-filter')).toBeHidden({ timeout: 5_000 })
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* Headers the grid is SHOWING as filtered — workshop A13, "wolę mieć pewność,
|
|
808
|
+
* czy faktycznie ja wszystko widzę". Read from the rendered header, not from
|
|
809
|
+
* internal state, because being visible is the whole requirement.
|
|
810
|
+
*/
|
|
811
|
+
async filteredColumnHeaders(): Promise<string[]> {
|
|
812
|
+
const ths = this.root.locator('th[data-filtered]')
|
|
813
|
+
const n = await ths.count()
|
|
814
|
+
const out: string[] = []
|
|
815
|
+
for (let i = 0; i < n; i++) {
|
|
816
|
+
out.push(((await ths.nth(i).textContent()) ?? '').replace(/\s+/g, ' ').trim())
|
|
817
|
+
}
|
|
818
|
+
return out
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/** The count in the toolbar's "Clear all filters (n)" pill; 0 when absent. */
|
|
822
|
+
async activeFilterCount(): Promise<number> {
|
|
823
|
+
const chip = this.root.locator('[data-clear-all-filters]')
|
|
824
|
+
if ((await chip.count()) === 0) return 0
|
|
825
|
+
const match = /\((\d+)\)/.exec((await chip.textContent()) ?? '')
|
|
826
|
+
return match ? Number(match[1]) : 0
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/** Click the toolbar's "Clear all filters (n)". */
|
|
830
|
+
async clearAllFilters(): Promise<void> {
|
|
831
|
+
this.note('cleared all filters from the toolbar')
|
|
832
|
+
await this.root.locator('[data-clear-all-filters]').click()
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/** Saved-view tabs, in the order the user sees them. */
|
|
836
|
+
async viewNames(): Promise<string[]> {
|
|
837
|
+
const tabs = this.root.locator('[role="tab"]')
|
|
838
|
+
const n = await tabs.count()
|
|
839
|
+
const names: string[] = []
|
|
840
|
+
for (let i = 0; i < n; i++) {
|
|
841
|
+
const label = ((await tabs.nth(i).textContent()) ?? '').replace(/×|\s+/g, ' ').trim()
|
|
842
|
+
if (label && label !== '+') names.push(label)
|
|
843
|
+
}
|
|
844
|
+
return names
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
async activeViewName(): Promise<string | null> {
|
|
848
|
+
const active = this.root.locator('[role="tab"][aria-selected="true"]').first()
|
|
849
|
+
if ((await active.count()) === 0) return null
|
|
850
|
+
return ((await active.textContent()) ?? '').replace(/×|\s+/g, ' ').trim()
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
async switchToView(name: RegExp | string): Promise<void> {
|
|
854
|
+
const re = typeof name === 'string' ? new RegExp(escapeRegExp(name), 'i') : name
|
|
855
|
+
const tabs = this.root.locator('[role="tab"]')
|
|
856
|
+
const n = await tabs.count()
|
|
857
|
+
for (let i = 0; i < n; i++) {
|
|
858
|
+
const label = ((await tabs.nth(i).textContent()) ?? '').trim()
|
|
859
|
+
if (re.test(label)) {
|
|
860
|
+
this.note(`switched to the "${label}" saved view`)
|
|
861
|
+
await tabs.nth(i).click()
|
|
862
|
+
return
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
throw new Error(`GridHarness: no saved view matched ${re}. Saw: ${(await this.viewNames()).join(', ')}`)
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// ── Column widths (persistence across reloads) ────────────────────────
|
|
869
|
+
|
|
870
|
+
async columnWidth(col: GridColumn): Promise<number> {
|
|
871
|
+
const box = await this.root.locator(`th[data-col="${col.index}"]`).boundingBox()
|
|
872
|
+
return Math.round(box?.width ?? 0)
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/** Drag a column's resize grip by `deltaX` pixels, as a user would. */
|
|
876
|
+
async resizeColumn(col: GridColumn, deltaX: number): Promise<void> {
|
|
877
|
+
this.note(`dragged the "${col.header}" column edge by ${deltaX}px`)
|
|
878
|
+
const th = this.root.locator(`th[data-col="${col.index}"]`)
|
|
879
|
+
const box = await th.boundingBox()
|
|
880
|
+
if (!box) throw new Error(`GridHarness: header ${col.header} has no box to drag`)
|
|
881
|
+
const startX = box.x + box.width - 2
|
|
882
|
+
const y = box.y + box.height / 2
|
|
883
|
+
await this.page.mouse.move(startX, y)
|
|
884
|
+
await this.page.mouse.down()
|
|
885
|
+
await this.page.mouse.move(startX + deltaX, y, { steps: 8 })
|
|
886
|
+
await this.page.mouse.up()
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// ── Observing what the grid asked the server to do ─────────────────────
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* Start recording mutating requests. Lets a spec assert the *cost* of an
|
|
893
|
+
* interaction — "editing one cell issued exactly one PATCH" — which is the
|
|
894
|
+
* measurement the Tier-1 paste-batching decision will be made on.
|
|
895
|
+
*/
|
|
896
|
+
startRecordingWrites(): void {
|
|
897
|
+
if (this.recording) return
|
|
898
|
+
this.recording = true
|
|
899
|
+
this.writes = []
|
|
900
|
+
this.page.on('request', this.onRequest)
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
stopRecordingWrites(): RecordedWrite[] {
|
|
904
|
+
if (this.recording) {
|
|
905
|
+
this.page.off('request', this.onRequest)
|
|
906
|
+
this.recording = false
|
|
907
|
+
}
|
|
908
|
+
return this.writes
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/** Mutating writes seen so far, optionally filtered by URL substring. */
|
|
912
|
+
recordedWrites(urlContains?: string): RecordedWrite[] {
|
|
913
|
+
return urlContains ? this.writes.filter((w) => w.path.includes(urlContains)) : [...this.writes]
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
private onRequest = (request: Request): void => {
|
|
917
|
+
const method = request.method()
|
|
918
|
+
if (method === 'GET' || method === 'HEAD' || method === 'OPTIONS') return
|
|
919
|
+
const url = request.url()
|
|
920
|
+
this.writes.push({ method, url, path: url.split('?')[0] })
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// ── Observing the grid's own multi-cell write reports ──────────────────
|
|
924
|
+
|
|
925
|
+
/**
|
|
926
|
+
* Start recording `table:cell:batch:save:result` — the report every multi-cell
|
|
927
|
+
* write (paste, cut, clear, fill, replace, import) emits through
|
|
928
|
+
* `applyCellWrites`.
|
|
929
|
+
*
|
|
930
|
+
* WHY NOT ASSERT THE FLASH: the report is also shown to the user as a flash
|
|
931
|
+
* message, but that string is translated and the demo session's locale is not
|
|
932
|
+
* fixed, so asserting on it makes a test locale-flaky. The event carries the
|
|
933
|
+
* same facts — how many cells were applied, and one entry per REJECTED cell
|
|
934
|
+
* with its machine-readable reason — in a form that reads the same in every
|
|
935
|
+
* language.
|
|
936
|
+
*
|
|
937
|
+
* Listens on `document` in the CAPTURE phase so it sees the event before the
|
|
938
|
+
* grid's own mediator, which calls `stopPropagation()`.
|
|
939
|
+
*/
|
|
940
|
+
async startRecordingBatchResults(): Promise<void> {
|
|
941
|
+
await this.page.evaluate((eventName) => {
|
|
942
|
+
const w = window as unknown as {
|
|
943
|
+
__gridBatchResults?: unknown[]
|
|
944
|
+
__gridBatchListener?: EventListener
|
|
945
|
+
}
|
|
946
|
+
if (w.__gridBatchListener) document.removeEventListener(eventName, w.__gridBatchListener, true)
|
|
947
|
+
w.__gridBatchResults = []
|
|
948
|
+
w.__gridBatchListener = (event: Event) => {
|
|
949
|
+
w.__gridBatchResults!.push((event as CustomEvent).detail)
|
|
950
|
+
}
|
|
951
|
+
document.addEventListener(eventName, w.__gridBatchListener, true)
|
|
952
|
+
}, BATCH_RESULT_EVENT)
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/** Every batch report seen since `startRecordingBatchResults()`, in order. */
|
|
956
|
+
async recordedBatchResults(): Promise<BatchWriteReport[]> {
|
|
957
|
+
return this.page.evaluate(() => {
|
|
958
|
+
const w = window as unknown as { __gridBatchResults?: unknown[] }
|
|
959
|
+
return (w.__gridBatchResults ?? []) as BatchWriteReport[]
|
|
960
|
+
})
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/** Wait until `count` batch reports have arrived, then return them. */
|
|
964
|
+
async waitForBatchResults(count: number, timeout = 10_000): Promise<BatchWriteReport[]> {
|
|
965
|
+
await expect
|
|
966
|
+
.poll(async () => (await this.recordedBatchResults()).length, { timeout })
|
|
967
|
+
.toBeGreaterThanOrEqual(count)
|
|
968
|
+
return this.recordedBatchResults()
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
/** Let the in-flight save settle and its success indicator clear. */
|
|
972
|
+
async settle(): Promise<void> {
|
|
973
|
+
await this.page.waitForTimeout(SAVE_SETTLE_MS + 250)
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
// ── Semi-automatic: handing control to a human or an agent ─────────────
|
|
977
|
+
|
|
978
|
+
/**
|
|
979
|
+
* Every user-level action this harness performed, in order. Recorded so a run
|
|
980
|
+
* can explain itself in the vocabulary the test was written in ("edited row 0
|
|
981
|
+
* / Cost type") rather than as a Playwright call stack.
|
|
982
|
+
*/
|
|
983
|
+
readonly journal: string[] = []
|
|
984
|
+
|
|
985
|
+
private note(step: string): void {
|
|
986
|
+
this.journal.push(step)
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* Emit an agent-browser script that lands a live browser in this same state,
|
|
991
|
+
* so an agent (or a person) can take over and poke at whatever the automated
|
|
992
|
+
* run found. agent-browser is the project's standard interactive driver — see
|
|
993
|
+
* `.claude/skills/browser-verify`.
|
|
994
|
+
*
|
|
995
|
+
* Deliberately reproduces NAVIGATION and FILTERING only, not the mutations: the
|
|
996
|
+
* point of taking over is to explore the state the failure happened in, and
|
|
997
|
+
* silently replaying writes would change the data underneath the investigation.
|
|
998
|
+
*/
|
|
999
|
+
takeoverRecipe(): string[] {
|
|
1000
|
+
const url = this.page.url()
|
|
1001
|
+
const origin = safeOrigin(url)
|
|
1002
|
+
return [
|
|
1003
|
+
'# Verified sequence (agent-browser 0.31.1). The auth + wait steps are NOT',
|
|
1004
|
+
'# optional: without auth you land on /login, and without the wait the grid',
|
|
1005
|
+
'# has not rendered yet and every selector returns 0.',
|
|
1006
|
+
'export AGENT_BROWSER_IDLE_TIMEOUT_MS=60000',
|
|
1007
|
+
'',
|
|
1008
|
+
'# One-time per machine — stores credentials in agent-browser\'s auth vault,',
|
|
1009
|
+
'# so no secret is ever written into this briefing:',
|
|
1010
|
+
`# agent-browser auth save fms-local --url "${origin}/login" --username <email> --password-stdin`,
|
|
1011
|
+
'',
|
|
1012
|
+
'agent-browser auth login fms-local',
|
|
1013
|
+
`agent-browser open "${url}"`,
|
|
1014
|
+
'agent-browser wait "td[data-row]" # REQUIRED — grid renders after navigation',
|
|
1015
|
+
'agent-browser snapshot -i # refs go stale after any change — re-snapshot',
|
|
1016
|
+
'',
|
|
1017
|
+
'# Drive it: agent-browser click @eN | fill @eN "text" | dblclick @eN | press Tab',
|
|
1018
|
+
'# Grid-specific reads:',
|
|
1019
|
+
`agent-browser eval "document.querySelectorAll('tr[data-row]').length" # rendered rows`,
|
|
1020
|
+
`agent-browser eval "Array.from(document.querySelectorAll('th[data-col]')).map(h=>h.getAttribute('data-col')+':'+h.textContent.trim()).join(' | ')"`,
|
|
1021
|
+
`agent-browser eval "Array.from(document.querySelectorAll('td[data-save-state]')).map(c=>c.getAttribute('data-row')+','+c.getAttribute('data-col')+'='+c.getAttribute('data-save-state')).join(' ')"`,
|
|
1022
|
+
`agent-browser eval "Array.from(document.querySelectorAll('td[data-in-range=true]')).map(c=>c.getAttribute('data-row')+','+c.getAttribute('data-col')).join(' ')" # current selection`,
|
|
1023
|
+
'',
|
|
1024
|
+
'# Watch what the grid asks the server to do while you poke at it:',
|
|
1025
|
+
'agent-browser network requests --clear',
|
|
1026
|
+
'agent-browser network requests --filter "/api/"',
|
|
1027
|
+
'',
|
|
1028
|
+
'agent-browser close --all',
|
|
1029
|
+
]
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
/**
|
|
1033
|
+
* Write a takeover briefing to `apps/web/.ai/qa/test-results/takeover/<label>.md`
|
|
1034
|
+
* — the live URL, what the run did, what the grid looked like, the writes it
|
|
1035
|
+
* issued, and a ready-to-paste agent-browser script.
|
|
1036
|
+
*
|
|
1037
|
+
* Call it from a failure hook (see `attachTakeoverOnFailure`) to make every red
|
|
1038
|
+
* run immediately investigable instead of just reported.
|
|
1039
|
+
*/
|
|
1040
|
+
async dumpTakeover(label: string, extra?: Record<string, unknown>): Promise<string> {
|
|
1041
|
+
const safe = label.replace(/[^a-zA-Z0-9._-]+/g, '-').slice(0, 120)
|
|
1042
|
+
const file = resolve(process.cwd(), '.ai/qa/test-results/takeover', `${safe}.md`)
|
|
1043
|
+
let headers: string[] = []
|
|
1044
|
+
let rows = -1
|
|
1045
|
+
try {
|
|
1046
|
+
rows = await this.rowCount()
|
|
1047
|
+
const ths = this.root.locator('th[data-col]')
|
|
1048
|
+
const n = await ths.count()
|
|
1049
|
+
for (let i = 0; i < n; i++) {
|
|
1050
|
+
const th = ths.nth(i)
|
|
1051
|
+
headers.push(`${await th.getAttribute('data-col')}: ${((await th.textContent()) ?? '').trim()}`)
|
|
1052
|
+
}
|
|
1053
|
+
} catch {
|
|
1054
|
+
headers = ['(page was not readable at dump time)']
|
|
1055
|
+
}
|
|
1056
|
+
const body = [
|
|
1057
|
+
`# Takeover briefing — ${label}`,
|
|
1058
|
+
'',
|
|
1059
|
+
`- **URL**: ${this.page.url()}`,
|
|
1060
|
+
`- **Rendered rows**: ${rows}`,
|
|
1061
|
+
'',
|
|
1062
|
+
'## Columns the grid was showing',
|
|
1063
|
+
'',
|
|
1064
|
+
...headers.map((h) => `- \`${h}\``),
|
|
1065
|
+
'',
|
|
1066
|
+
'## What the automated run did',
|
|
1067
|
+
'',
|
|
1068
|
+
...(this.journal.length ? this.journal.map((s, i) => `${i + 1}. ${s}`) : ['_(nothing recorded)_']),
|
|
1069
|
+
'',
|
|
1070
|
+
'## Writes it issued',
|
|
1071
|
+
'',
|
|
1072
|
+
...(this.writes.length
|
|
1073
|
+
? this.writes.map((w) => `- \`${w.method} ${w.path}\``)
|
|
1074
|
+
: ['_(none recorded — call `startRecordingWrites()` to capture them)_']),
|
|
1075
|
+
'',
|
|
1076
|
+
...(extra
|
|
1077
|
+
? ['## Context passed by the spec', '', '```json', JSON.stringify(extra, null, 2), '```', '']
|
|
1078
|
+
: []),
|
|
1079
|
+
'## Take over in a live browser',
|
|
1080
|
+
'',
|
|
1081
|
+
'```bash',
|
|
1082
|
+
...this.takeoverRecipe(),
|
|
1083
|
+
'```',
|
|
1084
|
+
'',
|
|
1085
|
+
].join('\n')
|
|
1086
|
+
mkdirSync(dirname(file), { recursive: true })
|
|
1087
|
+
writeFileSync(file, body, 'utf8')
|
|
1088
|
+
return file
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
* Wire a grid up so any failing test leaves behind a takeover briefing. Put this
|
|
1094
|
+
* in an `afterEach`:
|
|
1095
|
+
*
|
|
1096
|
+
* test.afterEach(async ({}, testInfo) => {
|
|
1097
|
+
* await attachTakeoverOnFailure(activeGrid, testInfo)
|
|
1098
|
+
* })
|
|
1099
|
+
*
|
|
1100
|
+
* Keeps the suite fully automatic on green and hands off to an interactive driver
|
|
1101
|
+
* on red — which is the whole point of a semi-automatic harness.
|
|
1102
|
+
*/
|
|
1103
|
+
export async function attachTakeoverOnFailure(
|
|
1104
|
+
grid: GridHarness | null,
|
|
1105
|
+
testInfo: { status?: string; expectedStatus?: string; title: string; attach: (name: string, options: { path: string; contentType: string }) => Promise<void> },
|
|
1106
|
+
): Promise<void> {
|
|
1107
|
+
if (!grid) return
|
|
1108
|
+
if (testInfo.status === testInfo.expectedStatus) return
|
|
1109
|
+
try {
|
|
1110
|
+
const file = await grid.dumpTakeover(testInfo.title)
|
|
1111
|
+
await testInfo.attach('takeover-briefing', { path: file, contentType: 'text/markdown' })
|
|
1112
|
+
console.log(`\n[GridHarness] takeover briefing → ${file}\n`)
|
|
1113
|
+
} catch (error) {
|
|
1114
|
+
console.log(`[GridHarness] could not write takeover briefing: ${String(error)}`)
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
/**
|
|
1119
|
+
* Grant clipboard read/write so `copySelection()` can verify what the user would
|
|
1120
|
+
* actually paste elsewhere. Chromium-only; call before navigating.
|
|
1121
|
+
*/
|
|
1122
|
+
export async function grantClipboard(page: Page): Promise<void> {
|
|
1123
|
+
await page.context().grantPermissions(['clipboard-read', 'clipboard-write'])
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
/** Ctrl everywhere except WebKit/macOS, where the grid also accepts Meta. */
|
|
1127
|
+
function modifier(): 'Control' | 'Meta' {
|
|
1128
|
+
return process.platform === 'darwin' ? 'Meta' : 'Control'
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
/** Human-readable column label for the journal — header text when we have it. */
|
|
1132
|
+
function describeCol(col: GridColumn | string): string {
|
|
1133
|
+
return typeof col === 'string' ? `#${col}` : `"${col.header}"`
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
function colIndexOf(col: GridColumn | string): string {
|
|
1137
|
+
return typeof col === 'string' ? col : col.index
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
function escapeRegExp(value: string): string {
|
|
1141
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
/** Origin of a URL, falling back to the raw string if it will not parse. */
|
|
1145
|
+
function safeOrigin(url: string): string {
|
|
1146
|
+
try {
|
|
1147
|
+
return new URL(url).origin
|
|
1148
|
+
} catch {
|
|
1149
|
+
return url
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
function escapeHtml(value: string): string {
|
|
1154
|
+
return value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
1155
|
+
}
|