@multiplatform.one/components 7.10.0 → 7.15.0
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/README.md +100 -2
- package/dist/cjs/Accordion/index.cjs +2 -6
- package/dist/cjs/Accordion/index.native.js +2 -6
- package/dist/cjs/Accordion/index.native.js.map +1 -1
- package/dist/cjs/AnimateHeight/index.cjs +1 -3
- package/dist/cjs/AnimateHeight/index.native.js +1 -3
- package/dist/cjs/AnimateHeight/index.native.js.map +1 -1
- package/dist/cjs/Chip/index.cjs +8 -1
- package/dist/cjs/Chip/index.native.js +8 -1
- package/dist/cjs/Chip/index.native.js.map +1 -1
- package/dist/cjs/SheetModal/index.cjs +22 -3
- package/dist/cjs/SheetModal/index.native.js +115 -27
- package/dist/cjs/SheetModal/index.native.js.map +1 -1
- package/dist/cjs/Tabs/index.cjs +35 -18
- package/dist/cjs/Tabs/index.native.js +39 -18
- package/dist/cjs/Tabs/index.native.js.map +1 -1
- package/dist/cjs/Text.cjs +2 -0
- package/dist/cjs/Text.native.js +2 -0
- package/dist/cjs/Text.native.js.map +1 -1
- package/dist/cjs/TreeSelect/index.cjs +55 -52
- package/dist/cjs/TreeSelect/index.native.js +55 -52
- package/dist/cjs/TreeSelect/index.native.js.map +1 -1
- package/dist/cjs/Video/PlayerChrome.cjs +3 -9
- package/dist/cjs/Video/PlayerChrome.native.js +3 -9
- package/dist/cjs/Video/PlayerChrome.native.js.map +1 -1
- package/dist/cjs/code/Code/index.cjs +6 -0
- package/dist/cjs/code/Code/index.native.js +7 -4
- package/dist/cjs/code/Code/index.native.js.map +1 -1
- package/dist/cjs/code/CodeBlock/index.cjs +4 -5
- package/dist/cjs/code/CodeBlock/index.native.js +4 -5
- package/dist/cjs/code/CodeBlock/index.native.js.map +1 -1
- package/dist/cjs/icons/themed.cjs +2 -1
- package/dist/cjs/icons/themed.native.js +2 -1
- package/dist/cjs/icons/themed.native.js.map +1 -1
- package/dist/cjs/knobImmuneExports.cjs +3 -3
- package/dist/cjs/knobImmuneExports.native.js +3 -3
- package/dist/cjs/layouts/PaneScaffold.cjs +70 -42
- package/dist/cjs/layouts/PaneScaffold.native.js +75 -48
- package/dist/cjs/layouts/PaneScaffold.native.js.map +1 -1
- package/dist/cjs/layouts/page.cjs +2 -1
- package/dist/cjs/layouts/page.native.js +2 -1
- package/dist/cjs/layouts/page.native.js.map +1 -1
- package/dist/cjs/layouts/sidebar.cjs +1 -3
- package/dist/cjs/layouts/sidebar.native.js +1 -3
- package/dist/cjs/layouts/sidebar.native.js.map +1 -1
- package/dist/cjs/layouts/tags.native.js.map +1 -1
- package/dist/cjs/surfaces.cjs +0 -32
- package/dist/cjs/surfaces.native.js +0 -32
- package/dist/cjs/surfaces.native.js.map +1 -1
- package/dist/cjs/views/List.cjs +1 -3
- package/dist/cjs/views/List.native.js +1 -3
- package/dist/cjs/views/List.native.js.map +1 -1
- package/dist/cjs/views/Matrix.cjs +568 -0
- package/dist/cjs/views/Matrix.native.js +612 -0
- package/dist/cjs/views/Matrix.native.js.map +1 -0
- package/dist/cjs/views/TreeView.cjs +175 -43
- package/dist/cjs/views/TreeView.native.js +181 -46
- package/dist/cjs/views/TreeView.native.js.map +1 -1
- package/dist/cjs/views/ganttMath.cjs +2 -0
- package/dist/cjs/views/ganttMath.native.js +2 -0
- package/dist/cjs/views/ganttMath.native.js.map +1 -1
- package/dist/cjs/views/index.cjs +16 -0
- package/dist/cjs/views/index.native.js +16 -0
- package/dist/cjs/views/index.native.js.map +1 -1
- package/dist/cjs/views/matrixMath.cjs +157 -0
- package/dist/cjs/views/matrixMath.native.js +229 -0
- package/dist/cjs/views/matrixMath.native.js.map +1 -0
- package/dist/esm/Accordion/index.mjs +3 -7
- package/dist/esm/Accordion/index.mjs.map +1 -1
- package/dist/esm/Accordion/index.native.js +3 -7
- package/dist/esm/Accordion/index.native.js.map +1 -1
- package/dist/esm/AnimateHeight/index.mjs +2 -4
- package/dist/esm/AnimateHeight/index.mjs.map +1 -1
- package/dist/esm/AnimateHeight/index.native.js +2 -4
- package/dist/esm/AnimateHeight/index.native.js.map +1 -1
- package/dist/esm/Avatar/getInitials.native.js.map +1 -1
- package/dist/esm/Avatar/index.native.js.map +1 -1
- package/dist/esm/Badge/index.native.js.map +1 -1
- package/dist/esm/Button.native.js.map +1 -1
- package/dist/esm/Carousel/index.native.js.map +1 -1
- package/dist/esm/Chip/index.mjs +9 -2
- package/dist/esm/Chip/index.mjs.map +1 -1
- package/dist/esm/Chip/index.native.js +9 -2
- package/dist/esm/Chip/index.native.js.map +1 -1
- package/dist/esm/Collapsible.native.js.map +1 -1
- package/dist/esm/ContextMenu/index.native.js.map +1 -1
- package/dist/esm/CopyField/index.native.js.map +1 -1
- package/dist/esm/DotIndicator/index.native.js.map +1 -1
- package/dist/esm/DropdownMenu/index.native.js.map +1 -1
- package/dist/esm/ErrorBoundary/index.native.js.map +1 -1
- package/dist/esm/Heading.native.js.map +1 -1
- package/dist/esm/Meter/index.native.js.map +1 -1
- package/dist/esm/Meter/meter.native.js.map +1 -1
- package/dist/esm/Pagination.native.js.map +1 -1
- package/dist/esm/ProgressSteps/index.native.js.map +1 -1
- package/dist/esm/Quote/index.native.js.map +1 -1
- package/dist/esm/Separator/index.native.js.map +1 -1
- package/dist/esm/SheetModal/detents.native.js.map +1 -1
- package/dist/esm/SheetModal/index.mjs +22 -3
- package/dist/esm/SheetModal/index.mjs.map +1 -1
- package/dist/esm/SheetModal/index.native.js +117 -29
- package/dist/esm/SheetModal/index.native.js.map +1 -1
- package/dist/esm/Skeleton/index.native.js.map +1 -1
- package/dist/esm/SkipLink/index.native.js.map +1 -1
- package/dist/esm/SortableList/SortableHandle.native.js.map +1 -1
- package/dist/esm/SortableList/index.native.js.map +1 -1
- package/dist/esm/SortableList/useSortableRows.native.js.map +1 -1
- package/dist/esm/Tabs/index.mjs +36 -19
- package/dist/esm/Tabs/index.mjs.map +1 -1
- package/dist/esm/Tabs/index.native.js +40 -19
- package/dist/esm/Tabs/index.native.js.map +1 -1
- package/dist/esm/Text.mjs +3 -1
- package/dist/esm/Text.mjs.map +1 -1
- package/dist/esm/Text.native.js +3 -1
- package/dist/esm/Text.native.js.map +1 -1
- package/dist/esm/Toast/index.native.js.map +1 -1
- package/dist/esm/Toast/store.native.js.map +1 -1
- package/dist/esm/Tooltip/nativePress.native.js.map +1 -1
- package/dist/esm/Tooltip/tooltipShared.native.js.map +1 -1
- package/dist/esm/TreeSelect/index.mjs +56 -53
- package/dist/esm/TreeSelect/index.mjs.map +1 -1
- package/dist/esm/TreeSelect/index.native.js +56 -53
- package/dist/esm/TreeSelect/index.native.js.map +1 -1
- package/dist/esm/TreeSelect/treeSelectModel.native.js.map +1 -1
- package/dist/esm/Video/PlayerChrome.mjs +4 -10
- package/dist/esm/Video/PlayerChrome.mjs.map +1 -1
- package/dist/esm/Video/PlayerChrome.native.js +4 -10
- package/dist/esm/Video/PlayerChrome.native.js.map +1 -1
- package/dist/esm/Video/VideoFrame.native.js.map +1 -1
- package/dist/esm/Video/types.native.js.map +1 -1
- package/dist/esm/Wheel/index.native.js.map +1 -1
- package/dist/esm/Wizard/index.native.js.map +1 -1
- package/dist/esm/actions/ActionBar.native.js.map +1 -1
- package/dist/esm/actions/DestructiveAction.native.js.map +1 -1
- package/dist/esm/actions/devWarn.native.js.map +1 -1
- package/dist/esm/actions/index.native.js.map +1 -1
- package/dist/esm/actions/severity.native.js.map +1 -1
- package/dist/esm/charts/Axis.native.js.map +1 -1
- package/dist/esm/charts/BarChart.native.js.map +1 -1
- package/dist/esm/charts/ChartDataView.native.js.map +1 -1
- package/dist/esm/charts/ChartSurface.native.js.map +1 -1
- package/dist/esm/charts/LineChart.native.js.map +1 -1
- package/dist/esm/charts/PieChart.native.js.map +1 -1
- package/dist/esm/charts/Sparkline.native.js.map +1 -1
- package/dist/esm/charts/accessibility.native.js.map +1 -1
- package/dist/esm/charts/composeChart.native.js.map +1 -1
- package/dist/esm/charts/index.native.js.map +1 -1
- package/dist/esm/charts/math.native.js.map +1 -1
- package/dist/esm/charts/series.native.js.map +1 -1
- package/dist/esm/charts/svg.native.js.map +1 -1
- package/dist/esm/charts/types.native.js.map +1 -1
- package/dist/esm/charts/useChartTheme.native.js.map +1 -1
- package/dist/esm/code/Code/index.mjs +6 -0
- package/dist/esm/code/Code/index.mjs.map +1 -1
- package/dist/esm/code/Code/index.native.js +10 -7
- package/dist/esm/code/Code/index.native.js.map +1 -1
- package/dist/esm/code/CodeBlock/index.mjs +5 -6
- package/dist/esm/code/CodeBlock/index.mjs.map +1 -1
- package/dist/esm/code/CodeBlock/index.native.js +5 -6
- package/dist/esm/code/CodeBlock/index.native.js.map +1 -1
- package/dist/esm/code/HighlightedCode/index.native.js.map +1 -1
- package/dist/esm/code/Highlighter/index.native.js.map +1 -1
- package/dist/esm/code/Pre/index.native.js.map +1 -1
- package/dist/esm/code/index.native.js.map +1 -1
- package/dist/esm/componentColors.native.js.map +1 -1
- package/dist/esm/feedback/ConfirmDialog.native.js.map +1 -1
- package/dist/esm/feedback/NotifyHost.native.js.map +1 -1
- package/dist/esm/feedback/copy.native.js.map +1 -1
- package/dist/esm/feedback/index.native.js.map +1 -1
- package/dist/esm/feedback/notify.native.js.map +1 -1
- package/dist/esm/feedback/route.native.js.map +1 -1
- package/dist/esm/feedback/store.native.js.map +1 -1
- package/dist/esm/hooks/index.native.js.map +1 -1
- package/dist/esm/hooks/useClipboard.native.js.map +1 -1
- package/dist/esm/hooks/useDocumentTitle.native.js.map +1 -1
- package/dist/esm/icons/CheckRegular.native.js.map +1 -1
- package/dist/esm/icons/MinusRegular.native.js.map +1 -1
- package/dist/esm/icons/index.native.js.map +1 -1
- package/dist/esm/icons/lucide.native.js.map +1 -1
- package/dist/esm/icons/themed.mjs +3 -2
- package/dist/esm/icons/themed.mjs.map +1 -1
- package/dist/esm/icons/themed.native.js +3 -2
- package/dist/esm/icons/themed.native.js.map +1 -1
- package/dist/esm/images/Image/ImageFrame.native.js.map +1 -1
- package/dist/esm/images/Image/shared.native.js.map +1 -1
- package/dist/esm/images/Image/types.native.js.map +1 -1
- package/dist/esm/images/index.native.js.map +1 -1
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/knobImmuneExports.mjs +3 -3
- package/dist/esm/knobImmuneExports.native.js +3 -3
- package/dist/esm/knobImmuneExports.native.js.map +1 -1
- package/dist/esm/layouts/AsyncBoundary.native.js.map +1 -1
- package/dist/esm/layouts/Debug/index.native.js.map +1 -1
- package/dist/esm/layouts/EmptyState.native.js.map +1 -1
- package/dist/esm/layouts/KeyboardAvoidingWrapper/behavior.native.js.map +1 -1
- package/dist/esm/layouts/PageTemplates.native.js.map +1 -1
- package/dist/esm/layouts/PaneScaffold.mjs +70 -42
- package/dist/esm/layouts/PaneScaffold.mjs.map +1 -1
- package/dist/esm/layouts/PaneScaffold.native.js +75 -48
- package/dist/esm/layouts/PaneScaffold.native.js.map +1 -1
- package/dist/esm/layouts/SafeAreaWrapper/insets.native.js.map +1 -1
- package/dist/esm/layouts/ViewSwitcher.native.js.map +1 -1
- package/dist/esm/layouts/attachments.native.js.map +1 -1
- package/dist/esm/layouts/breadcrumbs.native.js.map +1 -1
- package/dist/esm/layouts/index.native.js.map +1 -1
- package/dist/esm/layouts/page.mjs +3 -2
- package/dist/esm/layouts/page.mjs.map +1 -1
- package/dist/esm/layouts/page.native.js +3 -2
- package/dist/esm/layouts/page.native.js.map +1 -1
- package/dist/esm/layouts/pageHeadingScope.native.js.map +1 -1
- package/dist/esm/layouts/sidebar.mjs +2 -4
- package/dist/esm/layouts/sidebar.mjs.map +1 -1
- package/dist/esm/layouts/sidebar.native.js +2 -4
- package/dist/esm/layouts/sidebar.native.js.map +1 -1
- package/dist/esm/layouts/tags.mjs.map +1 -1
- package/dist/esm/layouts/tags.native.js.map +1 -1
- package/dist/esm/layouts/timeline.native.js.map +1 -1
- package/dist/esm/layouts/toolbar.native.js.map +1 -1
- package/dist/esm/shared/actionScan.native.js.map +1 -1
- package/dist/esm/shared/i18n.native.js.map +1 -1
- package/dist/esm/shared/t.native.js.map +1 -1
- package/dist/esm/shared/textRidesText.native.js.map +1 -1
- package/dist/esm/shared/useReturnFocusOnClose.native.js.map +1 -1
- package/dist/esm/surfaces.mjs +1 -33
- package/dist/esm/surfaces.mjs.map +1 -1
- package/dist/esm/surfaces.native.js +1 -33
- package/dist/esm/surfaces.native.js.map +1 -1
- package/dist/esm/tamagui.native.js.map +1 -1
- package/dist/esm/testSetup.native.js.map +1 -1
- package/dist/esm/utils/setTinted.native.js.map +1 -1
- package/dist/esm/utils/unwrapText.native.js.map +1 -1
- package/dist/esm/views/Calendar.native.js.map +1 -1
- package/dist/esm/views/Dashboard.native.js.map +1 -1
- package/dist/esm/views/Gantt.native.js.map +1 -1
- package/dist/esm/views/ImageGrid.native.js.map +1 -1
- package/dist/esm/views/List.mjs +2 -4
- package/dist/esm/views/List.mjs.map +1 -1
- package/dist/esm/views/List.native.js +2 -4
- package/dist/esm/views/List.native.js.map +1 -1
- package/dist/esm/views/Matrix.mjs +543 -0
- package/dist/esm/views/Matrix.mjs.map +1 -0
- package/dist/esm/views/Matrix.native.js +573 -0
- package/dist/esm/views/Matrix.native.js.map +1 -0
- package/dist/esm/views/ReportBuilder.native.js.map +1 -1
- package/dist/esm/views/TreeView.mjs +175 -43
- package/dist/esm/views/TreeView.mjs.map +1 -1
- package/dist/esm/views/TreeView.native.js +181 -46
- package/dist/esm/views/TreeView.native.js.map +1 -1
- package/dist/esm/views/calendarMath.native.js.map +1 -1
- package/dist/esm/views/ganttMath.mjs +1 -1
- package/dist/esm/views/ganttMath.mjs.map +1 -1
- package/dist/esm/views/ganttMath.native.js +1 -1
- package/dist/esm/views/ganttMath.native.js.map +1 -1
- package/dist/esm/views/index.mjs +4 -2
- package/dist/esm/views/index.mjs.map +1 -1
- package/dist/esm/views/index.native.js +4 -2
- package/dist/esm/views/index.native.js.map +1 -1
- package/dist/esm/views/kanbanDnd.native.js.map +1 -1
- package/dist/esm/views/matrixMath.mjs +122 -0
- package/dist/esm/views/matrixMath.mjs.map +1 -0
- package/dist/esm/views/matrixMath.native.js +191 -0
- package/dist/esm/views/matrixMath.native.js.map +1 -0
- package/dist/esm/views/treeViewDnd.native.js.map +1 -1
- package/dist/esm/views/treeViewLayout.native.js.map +1 -1
- package/dist/esm/views/treeViewMotion.native.js.map +1 -1
- package/dist/esm/views/treeViewReveal.native.js.map +1 -1
- package/dist/jsx/Accordion/index.mjs +3 -7
- package/dist/jsx/Accordion/index.mjs.map +1 -1
- package/dist/jsx/Accordion/index.native.js +2 -6
- package/dist/jsx/Accordion/index.native.js.map +1 -1
- package/dist/jsx/AnimateHeight/index.mjs +2 -4
- package/dist/jsx/AnimateHeight/index.mjs.map +1 -1
- package/dist/jsx/AnimateHeight/index.native.js +1 -3
- package/dist/jsx/AnimateHeight/index.native.js.map +1 -1
- package/dist/jsx/Chip/index.mjs +9 -2
- package/dist/jsx/Chip/index.mjs.map +1 -1
- package/dist/jsx/Chip/index.native.js +8 -1
- package/dist/jsx/Chip/index.native.js.map +1 -1
- package/dist/jsx/SheetModal/index.mjs +22 -3
- package/dist/jsx/SheetModal/index.mjs.map +1 -1
- package/dist/jsx/SheetModal/index.native.js +115 -27
- package/dist/jsx/SheetModal/index.native.js.map +1 -1
- package/dist/jsx/Tabs/index.mjs +36 -19
- package/dist/jsx/Tabs/index.mjs.map +1 -1
- package/dist/jsx/Tabs/index.native.js +39 -18
- package/dist/jsx/Tabs/index.native.js.map +1 -1
- package/dist/jsx/Text.mjs +3 -1
- package/dist/jsx/Text.mjs.map +1 -1
- package/dist/jsx/Text.native.js +2 -0
- package/dist/jsx/Text.native.js.map +1 -1
- package/dist/jsx/TreeSelect/index.mjs +56 -53
- package/dist/jsx/TreeSelect/index.mjs.map +1 -1
- package/dist/jsx/TreeSelect/index.native.js +55 -52
- package/dist/jsx/TreeSelect/index.native.js.map +1 -1
- package/dist/jsx/Video/PlayerChrome.mjs +4 -10
- package/dist/jsx/Video/PlayerChrome.mjs.map +1 -1
- package/dist/jsx/Video/PlayerChrome.native.js +3 -9
- package/dist/jsx/Video/PlayerChrome.native.js.map +1 -1
- package/dist/jsx/code/Code/index.mjs +6 -0
- package/dist/jsx/code/Code/index.mjs.map +1 -1
- package/dist/jsx/code/Code/index.native.js +7 -4
- package/dist/jsx/code/Code/index.native.js.map +1 -1
- package/dist/jsx/code/CodeBlock/index.mjs +5 -6
- package/dist/jsx/code/CodeBlock/index.mjs.map +1 -1
- package/dist/jsx/code/CodeBlock/index.native.js +4 -5
- package/dist/jsx/code/CodeBlock/index.native.js.map +1 -1
- package/dist/jsx/icons/themed.mjs +3 -2
- package/dist/jsx/icons/themed.mjs.map +1 -1
- package/dist/jsx/icons/themed.native.js +2 -1
- package/dist/jsx/icons/themed.native.js.map +1 -1
- package/dist/jsx/knobImmuneExports.mjs +3 -3
- package/dist/jsx/knobImmuneExports.native.js +3 -3
- package/dist/jsx/layouts/PaneScaffold.mjs +70 -42
- package/dist/jsx/layouts/PaneScaffold.mjs.map +1 -1
- package/dist/jsx/layouts/PaneScaffold.native.js +75 -48
- package/dist/jsx/layouts/PaneScaffold.native.js.map +1 -1
- package/dist/jsx/layouts/page.mjs +3 -2
- package/dist/jsx/layouts/page.mjs.map +1 -1
- package/dist/jsx/layouts/page.native.js +2 -1
- package/dist/jsx/layouts/page.native.js.map +1 -1
- package/dist/jsx/layouts/sidebar.mjs +2 -4
- package/dist/jsx/layouts/sidebar.mjs.map +1 -1
- package/dist/jsx/layouts/sidebar.native.js +1 -3
- package/dist/jsx/layouts/sidebar.native.js.map +1 -1
- package/dist/jsx/layouts/tags.mjs.map +1 -1
- package/dist/jsx/layouts/tags.native.js.map +1 -1
- package/dist/jsx/surfaces.mjs +1 -33
- package/dist/jsx/surfaces.mjs.map +1 -1
- package/dist/jsx/surfaces.native.js +0 -32
- package/dist/jsx/surfaces.native.js.map +1 -1
- package/dist/jsx/views/List.mjs +2 -4
- package/dist/jsx/views/List.mjs.map +1 -1
- package/dist/jsx/views/List.native.js +1 -3
- package/dist/jsx/views/List.native.js.map +1 -1
- package/dist/jsx/views/Matrix.mjs +543 -0
- package/dist/jsx/views/Matrix.mjs.map +1 -0
- package/dist/jsx/views/Matrix.native.js +612 -0
- package/dist/jsx/views/Matrix.native.js.map +1 -0
- package/dist/jsx/views/TreeView.mjs +175 -43
- package/dist/jsx/views/TreeView.mjs.map +1 -1
- package/dist/jsx/views/TreeView.native.js +181 -46
- package/dist/jsx/views/TreeView.native.js.map +1 -1
- package/dist/jsx/views/ganttMath.mjs +1 -1
- package/dist/jsx/views/ganttMath.mjs.map +1 -1
- package/dist/jsx/views/ganttMath.native.js +2 -0
- package/dist/jsx/views/ganttMath.native.js.map +1 -1
- package/dist/jsx/views/index.mjs +4 -2
- package/dist/jsx/views/index.mjs.map +1 -1
- package/dist/jsx/views/index.native.js +16 -0
- package/dist/jsx/views/index.native.js.map +1 -1
- package/dist/jsx/views/matrixMath.mjs +122 -0
- package/dist/jsx/views/matrixMath.mjs.map +1 -0
- package/dist/jsx/views/matrixMath.native.js +229 -0
- package/dist/jsx/views/matrixMath.native.js.map +1 -0
- package/package.json +12 -12
- package/src/Accordion/Accordion.spec.tsx +20 -0
- package/src/Accordion/index.tsx +8 -4
- package/src/AnimateHeight/index.tsx +143 -142
- package/src/Chip/Chip.borderLadder.spec.tsx +54 -0
- package/src/Chip/Chip.pressSlop.spec.tsx +57 -0
- package/src/Chip/index.tsx +15 -1
- package/src/Heading.native.spec.tsx +1 -1
- package/src/Separator/index.tsx +3 -3
- package/src/SheetModal/SheetModal.detentLayout.spec.tsx +173 -0
- package/src/SheetModal/SheetModal.fill.spec.tsx +147 -0
- package/src/SheetModal/SheetModal.ownedBody.spec.tsx +200 -0
- package/src/SheetModal/SheetModal.stories.spec.tsx +23 -2
- package/src/SheetModal/SheetModal.stories.tsx +275 -2
- package/src/SheetModal/SheetModal.touch.spec.tsx +63 -0
- package/src/SheetModal/index.native.tsx +143 -21
- package/src/SheetModal/index.tsx +40 -3
- package/src/Tabs/Tabs.hydration.spec.tsx +123 -0
- package/src/Tabs/Tabs.lifecycle.spec.tsx +123 -0
- package/src/Tabs/Tabs.measurement.spec.tsx +186 -0
- package/src/Tabs/Tabs.stories.tsx +43 -0
- package/src/Tabs/index.tsx +45 -15
- package/src/Text.spec.tsx +32 -2
- package/src/Text.tsx +7 -6
- package/src/Toast/Toast.spec.tsx +50 -0
- package/src/Tooltip/Tooltip.spec.tsx +55 -2
- package/src/TreeSelect/TreeSelect.spec.tsx +15 -15
- package/src/TreeSelect/index.tsx +53 -50
- package/src/Video/PlayerChrome.tsx +4 -3
- package/src/Wheel/Wheel.spec.tsx +4 -4
- package/src/adjacency.l1.spec.tsx +175 -0
- package/src/code/Code/code.spec.tsx +2 -19
- package/src/code/Code/index.tsx +0 -6
- package/src/code/CodeBlock/index.tsx +5 -3
- package/src/code/highlightedCode.spec.tsx +2 -2
- package/src/code/hostProps.spec.tsx +55 -0
- package/src/icons/themed.hydration.spec.tsx +60 -0
- package/src/icons/themed.tsx +3 -6
- package/src/knobImmuneExports.ts +3 -3
- package/src/layouts/PaneScaffold.spec.tsx +148 -2
- package/src/layouts/PaneScaffold.stories.tsx +33 -1
- package/src/layouts/PaneScaffold.tsx +101 -48
- package/src/layouts/page.designLaw.spec.tsx +20 -0
- package/src/layouts/page.tsx +2 -0
- package/src/layouts/sidebar.tsx +2 -3
- package/src/layouts/tags.tsx +2 -1
- package/src/overlayElevation.spec.tsx +39 -51
- package/src/surfaces.spec.tsx +13 -10
- package/src/surfaces.tsx +4 -33
- package/src/tamagui.spec.ts +4 -3
- package/src/textAccentSecondary.spec.tsx +100 -2
- package/src/views/List.tsx +2 -1
- package/src/views/Matrix.spec.tsx +339 -0
- package/src/views/Matrix.stories.spec.tsx +68 -0
- package/src/views/Matrix.stories.tsx +153 -0
- package/src/views/Matrix.tsx +738 -0
- package/src/views/TreeView.reveal.spec.tsx +7 -0
- package/src/views/TreeView.reveal.stories.tsx +134 -0
- package/src/views/TreeView.scroll-index.spec.tsx +246 -0
- package/src/views/TreeView.scroll.spec.tsx +711 -50
- package/src/views/TreeView.tsx +230 -49
- package/src/views/ganttMath.ts +4 -2
- package/src/views/index.ts +30 -0
- package/src/views/matrixMath.spec.ts +237 -0
- package/src/views/matrixMath.ts +251 -0
- package/src/views/views.spec.tsx +3 -1
- package/types/Accordion/index.d.ts.map +1 -1
- package/types/AnimateHeight/index.d.ts.map +1 -1
- package/types/Chip/index.d.ts.map +1 -1
- package/types/Separator/index.d.ts +3 -3
- package/types/SheetModal/index.d.ts +8 -1
- package/types/SheetModal/index.d.ts.map +1 -1
- package/types/SheetModal/index.native.d.ts +1 -1
- package/types/SheetModal/index.native.d.ts.map +1 -1
- package/types/Tabs/index.d.ts.map +1 -1
- package/types/Text.d.ts.map +1 -1
- package/types/TreeSelect/index.d.ts.map +1 -1
- package/types/Video/PlayerChrome.d.ts.map +1 -1
- package/types/code/Code/index.d.ts.map +1 -1
- package/types/code/CodeBlock/index.d.ts.map +1 -1
- package/types/icons/themed.d.ts.map +1 -1
- package/types/layouts/PaneScaffold.d.ts +14 -1
- package/types/layouts/PaneScaffold.d.ts.map +1 -1
- package/types/layouts/page.d.ts.map +1 -1
- package/types/layouts/sidebar.d.ts.map +1 -1
- package/types/layouts/tags.d.ts.map +1 -1
- package/types/surfaces.d.ts.map +1 -1
- package/types/views/List.d.ts.map +1 -1
- package/types/views/Matrix.d.ts +102 -0
- package/types/views/Matrix.d.ts.map +1 -0
- package/types/views/TreeView.d.ts +10 -1
- package/types/views/TreeView.d.ts.map +1 -1
- package/types/views/ganttMath.d.ts +4 -0
- package/types/views/ganttMath.d.ts.map +1 -1
- package/types/views/index.d.ts +3 -1
- package/types/views/index.d.ts.map +1 -1
- package/types/views/matrixMath.d.ts +114 -0
- package/types/views/matrixMath.d.ts.map +1 -0
package/src/surfaces.tsx
CHANGED
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
Popover,
|
|
8
8
|
Sheet,
|
|
9
9
|
View,
|
|
10
|
-
isWeb,
|
|
11
10
|
type DialogContentProps as TamaguiDialogContentProps,
|
|
12
11
|
type DialogOverlayProps as TamaguiDialogOverlayProps,
|
|
13
12
|
type TamaguiElement,
|
|
@@ -103,42 +102,16 @@ export const DialogContent = forwardRef<
|
|
|
103
102
|
);
|
|
104
103
|
});
|
|
105
104
|
|
|
106
|
-
/**
|
|
107
|
-
* Reference §8 overlay shadow — rgba(0,0,0,0.12) 0 12px 24px. Not Button $1,
|
|
108
|
-
* and not the `$2` fragment `elevatedSurface` carries either: that fragment's
|
|
109
|
-
* web paint arrives as an INLINE style (`elevationPaint` emits
|
|
110
|
-
* `style.boxShadow`, and Tamagui stamps `!important` only on pseudo atoms),
|
|
111
|
-
* so a shadow expressed only as `shadow*` props is a dead class atom under a
|
|
112
|
-
* live `$2` paint. The §8 value must land on the `style` prop too, replacing
|
|
113
|
-
* the fragment's. Dark inverts the hue and keeps the geometry — LC-85
|
|
114
|
-
* DARK-ELEVATION-TINT forbids a black umbra on a dark ground.
|
|
115
|
-
*/
|
|
116
|
-
const overlayElevationShadow = {
|
|
117
|
-
light: {
|
|
118
|
-
shadowColor: "rgba(0,0,0,0.12)",
|
|
119
|
-
shadowOffset: { width: 0, height: 12 },
|
|
120
|
-
shadowOpacity: 1,
|
|
121
|
-
shadowRadius: 24,
|
|
122
|
-
...(isWeb ? { style: { boxShadow: "0px 12px 24px rgba(0,0,0,0.12)" } } : null),
|
|
123
|
-
},
|
|
124
|
-
dark: {
|
|
125
|
-
shadowColor: "rgba(255,255,255,0.12)",
|
|
126
|
-
shadowOffset: { width: 0, height: 12 },
|
|
127
|
-
shadowOpacity: 1,
|
|
128
|
-
shadowRadius: 24,
|
|
129
|
-
...(isWeb ? { style: { boxShadow: "0px 12px 24px rgba(255,255,255,0.12)" } } : null),
|
|
130
|
-
},
|
|
131
|
-
} as const;
|
|
132
|
-
|
|
133
105
|
const DialogContentInner = forwardRef<
|
|
134
106
|
TamaguiElement,
|
|
135
107
|
TamaguiDialogContentProps & { children?: ReactNode }
|
|
136
108
|
>(function DialogContentInner({ children, ...props }, ref) {
|
|
137
109
|
const { knobProps } = useResolvedKnobs({ component: "DialogContent" });
|
|
138
110
|
// ThemeableStack unstyled:false defaults `elevate: true`, which paints
|
|
139
|
-
// getElevation(size) — `$true` is
|
|
140
|
-
//
|
|
141
|
-
// containerRadius 9, border 1px) stays on the
|
|
111
|
+
// getElevation(size) — `$true` is a control stop. `elevate={false}` leaves
|
|
112
|
+
// the overlay ladder in `elevatedSurface` as the only shadow (MPO-99).
|
|
113
|
+
// Geometry (panelPadding 18, containerRadius 9, border 1px) stays on the
|
|
114
|
+
// recipe fragments below.
|
|
142
115
|
return (
|
|
143
116
|
<Dialog.Content
|
|
144
117
|
ref={ref}
|
|
@@ -147,7 +120,6 @@ const DialogContentInner = forwardRef<
|
|
|
147
120
|
{...knobProps.panelPadding}
|
|
148
121
|
{...knobProps.gap}
|
|
149
122
|
elevate={false}
|
|
150
|
-
{...overlayElevationShadow[knobProps.scheme]}
|
|
151
123
|
transition={knobProps.transition}
|
|
152
124
|
enterStyle={overlayEnterExit}
|
|
153
125
|
exitStyle={overlayEnterExit}
|
|
@@ -227,7 +199,6 @@ const AlertDialogContentInner = forwardRef<
|
|
|
227
199
|
{...knobProps.panelPadding}
|
|
228
200
|
{...knobProps.gap}
|
|
229
201
|
elevate={false}
|
|
230
|
-
{...overlayElevationShadow[knobProps.scheme]}
|
|
231
202
|
transition={knobProps.transition}
|
|
232
203
|
enterStyle={overlayEnterExit}
|
|
233
204
|
exitStyle={overlayEnterExit}
|
package/src/tamagui.spec.ts
CHANGED
|
@@ -48,11 +48,11 @@ const ALLOWLIST_VALUE_NAMES = [
|
|
|
48
48
|
// barrel and had been red ever since. MPO-78: CI runs `turbo run build`
|
|
49
49
|
// only, so nothing caught it.)
|
|
50
50
|
"Circle",
|
|
51
|
-
// MPO-21
|
|
51
|
+
// MPO-21 G3 / G4. Restored by MPO-148; MPO-169 locks them here so a
|
|
52
52
|
// later Adapt-style rewrite cannot drop them without this spec going red.
|
|
53
53
|
// LinearGradient (G2) is not on the main tamagui barrel — see the dedicated
|
|
54
|
-
// assertion below rather than this identity check.
|
|
55
|
-
"
|
|
54
|
+
// assertion below rather than this identity check. Collapsible (G1) is a
|
|
55
|
+
// house shadow since MPO-265: its Trigger defaults to type="button".
|
|
56
56
|
"Group",
|
|
57
57
|
"ListItem",
|
|
58
58
|
"ScrollView",
|
|
@@ -138,6 +138,7 @@ const HOUSE_SHADOWS = [
|
|
|
138
138
|
"Avatar",
|
|
139
139
|
"Button",
|
|
140
140
|
"Card",
|
|
141
|
+
"Collapsible",
|
|
141
142
|
"Fieldset",
|
|
142
143
|
"H1",
|
|
143
144
|
"H2",
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { cleanup, screen } from "@testing-library/react";
|
|
20
20
|
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
21
|
-
import { Preset } from "@multiplatform.one/theme";
|
|
21
|
+
import { Preset, useResolvedKnobs } from "@multiplatform.one/theme";
|
|
22
22
|
import type { ReactElement } from "react";
|
|
23
|
-
import { Theme } from "tamagui";
|
|
23
|
+
import { SizableText, Theme } from "tamagui";
|
|
24
24
|
import { afterEach, describe, expect, it } from "vitest";
|
|
25
25
|
import { Accordion } from "./Accordion";
|
|
26
26
|
import { componentColors } from "./componentColors";
|
|
@@ -177,6 +177,104 @@ describe("Meter — textAccent reaches the ok-zone value readout (MPO-154)", ()
|
|
|
177
177
|
});
|
|
178
178
|
});
|
|
179
179
|
|
|
180
|
+
/**
|
|
181
|
+
* low == medium alone also passes a surface pinned to a static step, so the
|
|
182
|
+
* dim stops are checked against a probe painting the resolver's own colour.
|
|
183
|
+
*/
|
|
184
|
+
function restInk(el: Element): string | undefined {
|
|
185
|
+
for (const cls of Array.from(el.classList)) {
|
|
186
|
+
if (!cls.startsWith("_col-")) continue;
|
|
187
|
+
const value = cls.slice("_col-".length);
|
|
188
|
+
if (/^\d*(hover|active|focus|press|disabled)/.test(value)) continue;
|
|
189
|
+
return value;
|
|
190
|
+
}
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function ResolverInk() {
|
|
195
|
+
const { knobProps } = useResolvedKnobs();
|
|
196
|
+
return (
|
|
197
|
+
<SizableText data-resolver-ink={knobProps.textAccentColor} color={knobProps.textAccentColor}>
|
|
198
|
+
resolver ink
|
|
199
|
+
</SizableText>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function againstResolver(
|
|
204
|
+
scheme: Scheme,
|
|
205
|
+
accent: Accent,
|
|
206
|
+
node: ReactElement,
|
|
207
|
+
pick: (container: HTMLElement) => Element | null,
|
|
208
|
+
) {
|
|
209
|
+
const { container } = renderWithProviders(
|
|
210
|
+
inScheme(
|
|
211
|
+
scheme,
|
|
212
|
+
<Preset overrides={{ textAccent: accent }}>
|
|
213
|
+
{node}
|
|
214
|
+
<ResolverInk />
|
|
215
|
+
</Preset>,
|
|
216
|
+
),
|
|
217
|
+
);
|
|
218
|
+
const surface = pick(container);
|
|
219
|
+
const probe = container.querySelector("[data-resolver-ink]");
|
|
220
|
+
const result = {
|
|
221
|
+
surface: surface ? restInk(surface) : undefined,
|
|
222
|
+
resolver: probe ? restInk(probe) : undefined,
|
|
223
|
+
token: probe?.getAttribute("data-resolver-ink") ?? null,
|
|
224
|
+
};
|
|
225
|
+
cleanup();
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
describe("the dim stops paint the resolver's AA floor, not a pinned step (MPO-154)", () => {
|
|
230
|
+
const surfaces: Array<[string, ReactElement, (container: HTMLElement) => Element | null]> = [
|
|
231
|
+
[
|
|
232
|
+
"Tabs inactive label",
|
|
233
|
+
<Tabs
|
|
234
|
+
key="tabs"
|
|
235
|
+
items={[
|
|
236
|
+
{ value: "address", label: "Address & Contact", content: <>address panel</> },
|
|
237
|
+
{ value: "settings", label: "Settings", content: <>settings panel</> },
|
|
238
|
+
]}
|
|
239
|
+
defaultValue="address"
|
|
240
|
+
/>,
|
|
241
|
+
() => screen.getByText("Settings"),
|
|
242
|
+
],
|
|
243
|
+
[
|
|
244
|
+
"Accordion description",
|
|
245
|
+
<Accordion key="accordion" defaultValue="shipping">
|
|
246
|
+
<Accordion.Item value="shipping">
|
|
247
|
+
<Accordion.Trigger description="Where should we deliver the order?">
|
|
248
|
+
Shipping details
|
|
249
|
+
</Accordion.Trigger>
|
|
250
|
+
<Accordion.Content>ship</Accordion.Content>
|
|
251
|
+
</Accordion.Item>
|
|
252
|
+
</Accordion>,
|
|
253
|
+
() => screen.getByText("Where should we deliver the order?"),
|
|
254
|
+
],
|
|
255
|
+
[
|
|
256
|
+
"Quote attribution",
|
|
257
|
+
<Quote key="quote" cite="Alan Kay">
|
|
258
|
+
The best way to predict the future.
|
|
259
|
+
</Quote>,
|
|
260
|
+
(container) => container.querySelector("cite"),
|
|
261
|
+
],
|
|
262
|
+
];
|
|
263
|
+
|
|
264
|
+
for (const scheme of schemes) {
|
|
265
|
+
for (const [name, node, pick] of surfaces) {
|
|
266
|
+
for (const accent of ["low", "medium"] as const) {
|
|
267
|
+
it(`${scheme}: ${name} at ${accent} equals the resolver's textAccentColor`, () => {
|
|
268
|
+
const { surface, resolver, token } = againstResolver(scheme, accent, node, pick);
|
|
269
|
+
expect(token).toBe("$color11");
|
|
270
|
+
expect(resolver).toBeDefined();
|
|
271
|
+
expect(surface).toBe(resolver);
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
180
278
|
describe("componentColors keeps no second answer for secondary ink (MPO-154)", () => {
|
|
181
279
|
it("the text table exposes no static secondary or muted step", () => {
|
|
182
280
|
const text = componentColors.text as Record<string, string | undefined>;
|
package/src/views/List.tsx
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
useReadableTextOn,
|
|
14
14
|
useResolvedKnobs,
|
|
15
15
|
wasKeyboardFocus,
|
|
16
|
+
transitionProps,
|
|
16
17
|
} from "@multiplatform.one/theme";
|
|
17
18
|
import { useHotkey } from "@tanstack/react-hotkeys";
|
|
18
19
|
import type { RegisterableHotkey } from "@tanstack/react-hotkeys";
|
|
@@ -513,7 +514,7 @@ export function List<T>({
|
|
|
513
514
|
...corners,
|
|
514
515
|
}}
|
|
515
516
|
cursor="pointer"
|
|
516
|
-
{...(knobProps.transition
|
|
517
|
+
{...transitionProps(knobProps.transition)}
|
|
517
518
|
onPress={() => {
|
|
518
519
|
commitSelection(index);
|
|
519
520
|
onItemClick?.(item);
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import { cleanup, fireEvent, screen } from "@testing-library/react";
|
|
2
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
3
|
+
import { formatMonthYear } from "@multiplatform.one/theme";
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { MatrixView, type MatrixRow } from "./Matrix";
|
|
6
|
+
import { generateBuckets, type MatrixCell } from "./matrixMath";
|
|
7
|
+
|
|
8
|
+
afterEach(cleanup);
|
|
9
|
+
|
|
10
|
+
const rows: MatrixRow[] = [
|
|
11
|
+
{ id: "a", label: "Checking" },
|
|
12
|
+
{ id: "b", label: "Savings" },
|
|
13
|
+
{ id: "c", label: "HSA" },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
// Checking spans Jan–Mar with two points in January (folded to 10, the grid
|
|
17
|
+
// max); Savings carries a real zero in January and an error in March; HSA
|
|
18
|
+
// has no point at all.
|
|
19
|
+
const cells: MatrixCell[] = [
|
|
20
|
+
{ rowId: "a", date: new Date(2026, 0, 12), value: 4 },
|
|
21
|
+
{ rowId: "a", date: new Date(2026, 0, 20), value: 6 },
|
|
22
|
+
{ rowId: "a", date: new Date(2026, 1, 3), value: 2 },
|
|
23
|
+
{ rowId: "a", date: new Date(2026, 2, 9), value: 10 },
|
|
24
|
+
{ rowId: "b", date: new Date(2026, 0, 5), value: 0 },
|
|
25
|
+
{ rowId: "b", date: new Date(2026, 2, 2), value: 3, intent: "error" },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const cell = (container: HTMLElement, id: string) =>
|
|
29
|
+
container.querySelector(`[data-matrix-cell="${id}"]`) as HTMLElement;
|
|
30
|
+
|
|
31
|
+
describe("MatrixView", () => {
|
|
32
|
+
it("generates the column axis from the rule over the data's range, one row per subject", () => {
|
|
33
|
+
const { container } = renderWithProviders(
|
|
34
|
+
<MatrixView rows={rows} cells={cells} rule="month" />,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const columns = Array.from(container.querySelectorAll("[data-matrix-column]"));
|
|
38
|
+
expect(columns.map((el) => el.getAttribute("data-matrix-column"))).toEqual([
|
|
39
|
+
"2026-01-01",
|
|
40
|
+
"2026-02-01",
|
|
41
|
+
"2026-03-01",
|
|
42
|
+
]);
|
|
43
|
+
expect(columns[0].textContent).toBe(formatMonthYear(new Date(2026, 0, 1)));
|
|
44
|
+
expect(columns[0].getAttribute("aria-label")).toBe(formatMonthYear(new Date(2026, 0, 1)));
|
|
45
|
+
expect(container.querySelector('[data-matrix-view="month"]')).toBeTruthy();
|
|
46
|
+
|
|
47
|
+
const labels = Array.from(container.querySelectorAll("[data-matrix-row-label]"));
|
|
48
|
+
expect(labels.map((el) => el.textContent)).toEqual(["Checking", "Savings", "HSA"]);
|
|
49
|
+
expect(container.querySelectorAll("[data-matrix-row]")).toHaveLength(3);
|
|
50
|
+
expect(container.querySelectorAll("[data-matrix-cell]")).toHaveLength(9);
|
|
51
|
+
expect(screen.getByText("Subject")).toBeInTheDocument();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("regenerates the axis when the rule is switched, nothing re-declared", () => {
|
|
55
|
+
const onRuleChange = vi.fn();
|
|
56
|
+
const { container } = renderWithProviders(
|
|
57
|
+
<MatrixView rows={rows} cells={cells} rule="month" onRuleChange={onRuleChange} />,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
fireEvent.click(screen.getByText("Week"));
|
|
61
|
+
|
|
62
|
+
expect(onRuleChange).toHaveBeenCalledWith("week");
|
|
63
|
+
expect(container.querySelector('[data-matrix-view="week"]')).toBeTruthy();
|
|
64
|
+
const expected = generateBuckets(
|
|
65
|
+
{ start: new Date(2026, 0, 5), end: new Date(2026, 2, 10) },
|
|
66
|
+
"week",
|
|
67
|
+
);
|
|
68
|
+
const columns = Array.from(container.querySelectorAll("[data-matrix-column]"));
|
|
69
|
+
expect(columns.map((el) => el.getAttribute("data-matrix-column"))).toEqual(
|
|
70
|
+
expected.map((b) => b.key),
|
|
71
|
+
);
|
|
72
|
+
expect(columns.length).toBeGreaterThan(3);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("snaps an explicit window to bucket boundaries", () => {
|
|
76
|
+
const { container } = renderWithProviders(
|
|
77
|
+
<MatrixView
|
|
78
|
+
rows={rows}
|
|
79
|
+
cells={cells}
|
|
80
|
+
rule="month"
|
|
81
|
+
range={{ start: new Date(2025, 10, 20), end: new Date(2026, 4, 2) }}
|
|
82
|
+
/>,
|
|
83
|
+
);
|
|
84
|
+
const columns = Array.from(container.querySelectorAll("[data-matrix-column]"));
|
|
85
|
+
expect(columns.map((el) => el.getAttribute("data-matrix-column"))).toEqual([
|
|
86
|
+
"2025-11-01",
|
|
87
|
+
"2025-12-01",
|
|
88
|
+
"2026-01-01",
|
|
89
|
+
"2026-02-01",
|
|
90
|
+
"2026-03-01",
|
|
91
|
+
"2026-04-01",
|
|
92
|
+
"2026-05-01",
|
|
93
|
+
]);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("a cell carries its folded value and a paint strength; an intent rides the theme ramp", () => {
|
|
97
|
+
const { container } = renderWithProviders(
|
|
98
|
+
<MatrixView rows={rows} cells={cells} rule="month" />,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const aJan = cell(container, "a:2026-01-01");
|
|
102
|
+
expect(aJan.textContent).toBe("10");
|
|
103
|
+
expect(aJan.getAttribute("data-matrix-cell-state")).toBe("value");
|
|
104
|
+
expect(aJan.querySelector("[data-matrix-fill]")!.getAttribute("data-matrix-fill")).toBe("1");
|
|
105
|
+
expect(aJan.getAttribute("aria-label")).toBe(
|
|
106
|
+
`Checking, ${formatMonthYear(new Date(2026, 0, 1))}: 10`,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const aFeb = cell(container, "a:2026-02-01");
|
|
110
|
+
expect(aFeb.textContent).toBe("2");
|
|
111
|
+
expect(aFeb.querySelector("[data-matrix-fill]")!.getAttribute("data-matrix-fill")).toBe("0.2");
|
|
112
|
+
|
|
113
|
+
const bMar = cell(container, "b:2026-03-01");
|
|
114
|
+
expect(bMar.getAttribute("data-matrix-cell-intent")).toBe("error");
|
|
115
|
+
expect(cell(container, "a:2026-03-01").getAttribute("data-matrix-cell-intent")).toBeNull();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("an empty bucket is a different state from a zero", () => {
|
|
119
|
+
const { container } = renderWithProviders(
|
|
120
|
+
<MatrixView rows={rows} cells={cells} rule="month" />,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const zero = cell(container, "b:2026-01-01");
|
|
124
|
+
expect(zero.getAttribute("data-matrix-cell-state")).toBe("zero");
|
|
125
|
+
expect(zero.textContent).toBe("0");
|
|
126
|
+
expect(zero.querySelector("[data-matrix-fill]")!.getAttribute("data-matrix-fill")).toBe("0");
|
|
127
|
+
|
|
128
|
+
const gap = cell(container, "c:2026-01-01");
|
|
129
|
+
expect(gap.getAttribute("data-matrix-cell-state")).toBe("empty");
|
|
130
|
+
expect(gap.querySelector("[data-matrix-fill]")).toBeNull();
|
|
131
|
+
expect(gap.textContent).toBe("·");
|
|
132
|
+
expect(gap.getAttribute("aria-label")).toBe(
|
|
133
|
+
`HSA, ${formatMonthYear(new Date(2026, 0, 1))}: No data`,
|
|
134
|
+
);
|
|
135
|
+
expect(cell(container, "b:2026-02-01").getAttribute("data-matrix-cell-state")).toBe("empty");
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("carries a legend naming the empty state and the ramp, plus the caller's entries", () => {
|
|
139
|
+
const { container } = renderWithProviders(
|
|
140
|
+
<MatrixView
|
|
141
|
+
rows={rows}
|
|
142
|
+
cells={cells}
|
|
143
|
+
rule="month"
|
|
144
|
+
legendItems={[{ label: "Failed import", intent: "error" }]}
|
|
145
|
+
/>,
|
|
146
|
+
);
|
|
147
|
+
const legend = container.querySelector("[data-matrix-legend]")!;
|
|
148
|
+
expect(legend).toBeTruthy();
|
|
149
|
+
expect(legend.textContent).toContain("No data");
|
|
150
|
+
expect(legend.textContent).toContain("Less");
|
|
151
|
+
expect(legend.textContent).toContain("More");
|
|
152
|
+
expect(legend.textContent).toContain("Failed import");
|
|
153
|
+
const swatches = Array.from(legend.querySelectorAll("[data-matrix-legend-swatch]")).map((el) =>
|
|
154
|
+
el.getAttribute("data-matrix-legend-swatch"),
|
|
155
|
+
);
|
|
156
|
+
expect(swatches).toEqual(["empty", "0", "0.25", "0.5", "0.75", "1", "1"]);
|
|
157
|
+
|
|
158
|
+
cleanup();
|
|
159
|
+
const { container: bare } = renderWithProviders(
|
|
160
|
+
<MatrixView rows={rows} cells={cells} rule="month" showLegend={false} />,
|
|
161
|
+
);
|
|
162
|
+
expect(bare.querySelector("[data-matrix-legend]")).toBeNull();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("anchors every inset fill to its own swatch or cell, so no fill resolves against the viewport", () => {
|
|
166
|
+
const { container } = renderWithProviders(
|
|
167
|
+
<MatrixView
|
|
168
|
+
rows={rows}
|
|
169
|
+
cells={cells}
|
|
170
|
+
rule="month"
|
|
171
|
+
legendItems={[{ label: "Failed import", intent: "error" }]}
|
|
172
|
+
/>,
|
|
173
|
+
);
|
|
174
|
+
const swatches = Array.from(
|
|
175
|
+
container.querySelectorAll(
|
|
176
|
+
'[data-matrix-legend-swatch]:not([data-matrix-legend-swatch="empty"])',
|
|
177
|
+
),
|
|
178
|
+
) as HTMLElement[];
|
|
179
|
+
const cellFills = Array.from(container.querySelectorAll("[data-matrix-fill]")) as HTMLElement[];
|
|
180
|
+
expect(swatches).toHaveLength(6);
|
|
181
|
+
expect(cellFills.length).toBeGreaterThan(0);
|
|
182
|
+
|
|
183
|
+
const fills = [
|
|
184
|
+
...swatches.map((swatch) => swatch.firstElementChild as HTMLElement),
|
|
185
|
+
...cellFills,
|
|
186
|
+
];
|
|
187
|
+
for (const fill of fills) {
|
|
188
|
+
expect(getComputedStyle(fill).position).toBe("absolute");
|
|
189
|
+
expect(getComputedStyle(fill.parentElement!).position).toBe("relative");
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("a cell press reports its row, bucket and summary; an empty bucket presses too", () => {
|
|
194
|
+
const onCellPress = vi.fn();
|
|
195
|
+
const { container } = renderWithProviders(
|
|
196
|
+
<MatrixView rows={rows} cells={cells} rule="month" onCellPress={onCellPress} />,
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const aJan = cell(container, "a:2026-01-01");
|
|
200
|
+
expect(aJan.getAttribute("role")).toBe("button");
|
|
201
|
+
fireEvent.click(aJan);
|
|
202
|
+
expect(onCellPress).toHaveBeenCalledTimes(1);
|
|
203
|
+
const ref = onCellPress.mock.calls[0][0];
|
|
204
|
+
expect(ref.row.id).toBe("a");
|
|
205
|
+
expect(ref.bucket.key).toBe("2026-01-01");
|
|
206
|
+
expect(ref.bucket.start).toEqual(new Date(2026, 0, 1));
|
|
207
|
+
expect(ref.summary.value).toBe(10);
|
|
208
|
+
expect(ref.summary.count).toBe(2);
|
|
209
|
+
|
|
210
|
+
fireEvent.click(cell(container, "c:2026-02-01"));
|
|
211
|
+
expect(onCellPress).toHaveBeenCalledTimes(2);
|
|
212
|
+
expect(onCellPress.mock.calls[1][0].summary).toBeUndefined();
|
|
213
|
+
expect(onCellPress.mock.calls[1][0].row.id).toBe("c");
|
|
214
|
+
|
|
215
|
+
fireEvent.keyDown(aJan, { key: "Enter" });
|
|
216
|
+
expect(onCellPress).toHaveBeenCalledTimes(3);
|
|
217
|
+
fireEvent.keyDown(aJan, { key: " " });
|
|
218
|
+
expect(onCellPress).toHaveBeenCalledTimes(4);
|
|
219
|
+
fireEvent.keyDown(aJan, { key: "a" });
|
|
220
|
+
expect(onCellPress).toHaveBeenCalledTimes(4);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("without onCellPress a cell is not a control", () => {
|
|
224
|
+
const { container } = renderWithProviders(
|
|
225
|
+
<MatrixView rows={rows} cells={cells} rule="month" />,
|
|
226
|
+
);
|
|
227
|
+
expect(cell(container, "a:2026-01-01").getAttribute("role")).toBeNull();
|
|
228
|
+
expect(container.querySelectorAll('[data-matrix-cell][role="button"]')).toHaveLength(0);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("keeps the header row and subject column outside the grid scrollers, and the header follows the grid scroll", () => {
|
|
232
|
+
const scrolls: { target: HTMLElement; left: number }[] = [];
|
|
233
|
+
const original = Object.getOwnPropertyDescriptor(HTMLElement.prototype, "scroll");
|
|
234
|
+
Object.defineProperty(HTMLElement.prototype, "scroll", {
|
|
235
|
+
configurable: true,
|
|
236
|
+
writable: true,
|
|
237
|
+
value(this: HTMLElement, options: { left?: number }) {
|
|
238
|
+
if (typeof options?.left === "number") scrolls.push({ target: this, left: options.left });
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
try {
|
|
242
|
+
const { container } = renderWithProviders(
|
|
243
|
+
<MatrixView rows={rows} cells={cells} rule="month" />,
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
const axisRow = container.querySelector("[data-matrix-axis-row]") as HTMLElement;
|
|
247
|
+
const axis = container.querySelector("[data-matrix-axis]") as HTMLElement;
|
|
248
|
+
const body = container.querySelector("[data-matrix-scroll-body]") as HTMLElement;
|
|
249
|
+
const labels = container.querySelector("[data-matrix-labels]") as HTMLElement;
|
|
250
|
+
const canvas = container.querySelector("[data-matrix-canvas]") as HTMLElement;
|
|
251
|
+
expect(axisRow).toBeTruthy();
|
|
252
|
+
expect(body.contains(axisRow)).toBe(false);
|
|
253
|
+
expect(axisRow.contains(axis)).toBe(true);
|
|
254
|
+
expect(body.contains(labels)).toBe(true);
|
|
255
|
+
expect(body.contains(canvas)).toBe(true);
|
|
256
|
+
|
|
257
|
+
let scroller: HTMLElement | null = canvas.parentElement;
|
|
258
|
+
while (scroller && scroller !== body && !scroller.contains(labels)) {
|
|
259
|
+
scroller = scroller.parentElement;
|
|
260
|
+
}
|
|
261
|
+
expect(scroller).toBe(body);
|
|
262
|
+
|
|
263
|
+
let node: HTMLElement | null = canvas.parentElement;
|
|
264
|
+
let followed = false;
|
|
265
|
+
while (node && node !== body && !followed) {
|
|
266
|
+
Object.defineProperty(node, "scrollLeft", { configurable: true, value: 300 });
|
|
267
|
+
fireEvent.scroll(node);
|
|
268
|
+
followed = scrolls.some((call) => call.left === 300 && call.target.contains(axis));
|
|
269
|
+
node = node.parentElement;
|
|
270
|
+
}
|
|
271
|
+
expect(followed).toBe(true);
|
|
272
|
+
expect(scrolls.some((call) => call.target.contains(canvas))).toBe(false);
|
|
273
|
+
} finally {
|
|
274
|
+
if (original) Object.defineProperty(HTMLElement.prototype, "scroll", original);
|
|
275
|
+
else delete (HTMLElement.prototype as any).scroll;
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("shows the matrix skeleton while loading with no rows (W9 / DG-ST-02)", () => {
|
|
280
|
+
const { container } = renderWithProviders(<MatrixView rows={[]} cells={[]} isLoading />);
|
|
281
|
+
expect(container.querySelector('[data-async-skeleton="matrix"]')).toBeTruthy();
|
|
282
|
+
expect(container.querySelector("[data-matrix-column]")).toBeNull();
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it("forbids empty chrome when error is set, and shows the empty state otherwise (W9 / DG-ST-01)", () => {
|
|
286
|
+
renderWithProviders(
|
|
287
|
+
<MatrixView rows={[]} cells={[]} emptyTitle="No accounts yet" error="Coverage failed" />,
|
|
288
|
+
);
|
|
289
|
+
expect(screen.queryByText("No accounts yet")).toBeNull();
|
|
290
|
+
expect(screen.getByText("Coverage failed")).toBeInTheDocument();
|
|
291
|
+
cleanup();
|
|
292
|
+
|
|
293
|
+
renderWithProviders(<MatrixView rows={[]} cells={[]} emptyTitle="No accounts yet" />);
|
|
294
|
+
expect(screen.getByText("No accounts yet")).toBeInTheDocument();
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it("formatCell overrides the visible text and a point label wins over the number", () => {
|
|
298
|
+
const { container } = renderWithProviders(
|
|
299
|
+
<MatrixView
|
|
300
|
+
rows={rows}
|
|
301
|
+
cells={[...cells, { rowId: "c", date: new Date(2026, 1, 10), value: 7, label: "7 new" }]}
|
|
302
|
+
rule="month"
|
|
303
|
+
formatCell={(summary) => summary.label ?? `${summary.count}p`}
|
|
304
|
+
/>,
|
|
305
|
+
);
|
|
306
|
+
expect(cell(container, "a:2026-01-01").textContent).toBe("2p");
|
|
307
|
+
expect(cell(container, "c:2026-02-01").textContent).toBe("7 new");
|
|
308
|
+
cleanup();
|
|
309
|
+
|
|
310
|
+
const { container: plain } = renderWithProviders(
|
|
311
|
+
<MatrixView
|
|
312
|
+
rows={rows}
|
|
313
|
+
cells={[...cells, { rowId: "c", date: new Date(2026, 1, 10), value: 7, label: "7 new" }]}
|
|
314
|
+
rule="month"
|
|
315
|
+
/>,
|
|
316
|
+
);
|
|
317
|
+
expect(cell(plain, "a:2026-01-01").textContent).toBe("10");
|
|
318
|
+
expect(cell(plain, "c:2026-02-01").textContent).toBe("7 new");
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it("scales intensity against the largest value on screen, not one outside the window or the rows", () => {
|
|
322
|
+
const { container } = renderWithProviders(
|
|
323
|
+
<MatrixView
|
|
324
|
+
rows={rows}
|
|
325
|
+
cells={[
|
|
326
|
+
...cells,
|
|
327
|
+
{ rowId: "a", date: new Date(2025, 5, 1), value: 1000 },
|
|
328
|
+
{ rowId: "zz", date: new Date(2026, 0, 3), value: 500 },
|
|
329
|
+
]}
|
|
330
|
+
rule="month"
|
|
331
|
+
range={{ start: new Date(2026, 0, 1), end: new Date(2026, 3, 1) }}
|
|
332
|
+
/>,
|
|
333
|
+
);
|
|
334
|
+
const fill = (id: string) =>
|
|
335
|
+
cell(container, id).querySelector("[data-matrix-fill]")!.getAttribute("data-matrix-fill");
|
|
336
|
+
expect(fill("a:2026-01-01")).toBe("1");
|
|
337
|
+
expect(fill("a:2026-02-01")).toBe("0.2");
|
|
338
|
+
});
|
|
339
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Matrix stories are the only in-repo render of the view (MPO-254 AC5:
|
|
3
|
+
* a subject-by-month grid with gaps). happy-dom runs no layout, so the
|
|
4
|
+
* pinned scrollers are not exercised here; what is asserted is that every
|
|
5
|
+
* story mounts and that the Main story's data really does put a zero and a
|
|
6
|
+
* gap on screen as two different cell states.
|
|
7
|
+
*/
|
|
8
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
9
|
+
import { cleanup } from "@testing-library/react";
|
|
10
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
11
|
+
import meta, { Default, Intents, WeekRule } from "./Matrix.stories";
|
|
12
|
+
|
|
13
|
+
vi.mock("@multiplatform.one/storybook", () => ({ action: () => () => undefined }));
|
|
14
|
+
|
|
15
|
+
afterEach(cleanup);
|
|
16
|
+
|
|
17
|
+
describe("Matrix stories", () => {
|
|
18
|
+
it("declares the Components/Matrix title", () => {
|
|
19
|
+
expect(meta.title).toBe("Components/Matrix");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("every exported story mounts", () => {
|
|
23
|
+
for (const story of [Default, Intents, WeekRule]) {
|
|
24
|
+
const { container } = renderWithProviders(<>{story.render()}</>);
|
|
25
|
+
expect(container.querySelector("[data-matrix-view]")).toBeTruthy();
|
|
26
|
+
cleanup();
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("the Main story renders a subject-by-month grid where a gap and a zero are different states", () => {
|
|
31
|
+
const { container } = renderWithProviders(<>{Default.render()}</>);
|
|
32
|
+
expect(container.querySelector('[data-matrix-view="month"]')).toBeTruthy();
|
|
33
|
+
expect(container.querySelectorAll("[data-matrix-column]")).toHaveLength(12);
|
|
34
|
+
expect(container.querySelectorAll("[data-matrix-row-label]")).toHaveLength(6);
|
|
35
|
+
|
|
36
|
+
const zero = container.querySelector('[data-matrix-cell="savings:2026-04-01"]')!;
|
|
37
|
+
expect(zero.getAttribute("data-matrix-cell-state")).toBe("zero");
|
|
38
|
+
expect(zero.querySelector("[data-matrix-fill]")).toBeTruthy();
|
|
39
|
+
expect(zero.textContent).toBe("0");
|
|
40
|
+
|
|
41
|
+
const gap = container.querySelector('[data-matrix-cell="credit:2026-05-01"]')!;
|
|
42
|
+
expect(gap.getAttribute("data-matrix-cell-state")).toBe("empty");
|
|
43
|
+
expect(gap.querySelector("[data-matrix-fill]")).toBeNull();
|
|
44
|
+
expect(gap.textContent).not.toBe("0");
|
|
45
|
+
|
|
46
|
+
const hsaRow = container.querySelector('[data-matrix-row="hsa"]')!;
|
|
47
|
+
expect(hsaRow.querySelectorAll('[data-matrix-cell-state="empty"]')).toHaveLength(12);
|
|
48
|
+
|
|
49
|
+
const filled = container.querySelector('[data-matrix-cell="checking:2026-12-01"]')!;
|
|
50
|
+
expect(filled.getAttribute("data-matrix-cell-state")).toBe("value");
|
|
51
|
+
expect(filled.querySelector("[data-matrix-fill]")?.getAttribute("data-matrix-fill")).toBe("1");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("the Intents story rides the theme ramps and names them in the legend", () => {
|
|
55
|
+
const { container } = renderWithProviders(<>{Intents.render()}</>);
|
|
56
|
+
const down = container.querySelector('[data-matrix-cell="auth:2026-03-05"]')!;
|
|
57
|
+
expect(down.getAttribute("data-matrix-cell-intent")).toBe("error");
|
|
58
|
+
expect(
|
|
59
|
+
container
|
|
60
|
+
.querySelector('[data-matrix-cell="search:2026-03-01"]')!
|
|
61
|
+
.getAttribute("data-matrix-cell-state"),
|
|
62
|
+
).toBe("empty");
|
|
63
|
+
const legend = container.querySelector("[data-matrix-legend]")!;
|
|
64
|
+
expect(legend.textContent).toContain("Healthy");
|
|
65
|
+
expect(legend.textContent).toContain("Degraded");
|
|
66
|
+
expect(legend.textContent).toContain("Down");
|
|
67
|
+
});
|
|
68
|
+
});
|