@titan-design/react-ui 0.12.1 → 0.14.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 +60 -47
- package/dist/bodymap.js +856 -736
- package/dist/bodymap.js.map +1 -1
- package/dist/bodymap.mjs +858 -738
- package/dist/bodymap.mjs.map +1 -1
- package/dist/index.d.mts +1431 -237
- package/dist/index.d.ts +1431 -237
- package/dist/index.js +6506 -4781
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6280 -4642
- package/dist/index.mjs.map +1 -1
- package/dist/{pages-B1hqnGIC.d.ts → pages-CsjsUn0G.d.ts} +51 -16
- package/dist/{pages-CvsFqNtx.d.mts → pages-ZbT_pD_U.d.mts} +51 -16
- package/dist/pages.d.mts +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.js +1919 -1937
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +1927 -1945
- package/dist/pages.mjs.map +1 -1
- package/dist/theme/index.d.mts +225 -140
- package/dist/theme/index.d.ts +225 -140
- package/dist/theme/index.js +298 -319
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +283 -315
- package/dist/theme/index.mjs.map +1 -1
- package/dist/theme/tokens-css.js +145 -24
- package/dist/theme/tokens-css.js.map +1 -1
- package/dist/theme/tokens-css.mjs +145 -24
- package/dist/theme/tokens-css.mjs.map +1 -1
- package/dist/theme/tokens.d.mts +128 -11
- package/dist/theme/tokens.d.ts +128 -11
- package/dist/theme/tokens.js +126 -26
- package/dist/theme/tokens.js.map +1 -1
- package/dist/theme/tokens.mjs +124 -27
- package/dist/theme/tokens.mjs.map +1 -1
- package/docs/audits/2026-09-08-critique-addenda.md +51 -0
- package/docs/audits/2026-09-08-library-critique.md +212 -0
- package/docs/library-roadmap.md +99 -0
- package/docs/render-testing-pattern.md +130 -0
- package/package.json +1 -1
- package/src/arch/Architecture.stories.tsx +3 -1
- package/src/arch/arch-graph.freshness.test.ts +52 -0
- package/src/arch/arch-graph.json +4989 -1583
- package/src/components/custom/ActiveWork/CoChangeChip.stories.tsx +44 -0
- package/src/components/custom/ActiveWork/CoChangeChip.test.tsx +29 -0
- package/src/components/custom/ActiveWork/CoChangeChip.tsx +48 -0
- package/src/components/custom/ActiveWork/Eyebrow.stories.tsx +25 -0
- package/src/components/custom/ActiveWork/Eyebrow.test.tsx +16 -0
- package/src/components/custom/ActiveWork/Eyebrow.tsx +26 -0
- package/src/components/custom/ActiveWork/FileActivityDetail.stories.tsx +50 -0
- package/src/components/custom/ActiveWork/FileActivityDetail.test.tsx +66 -0
- package/src/components/custom/ActiveWork/FileActivityDetail.tsx +202 -0
- package/src/components/custom/ActiveWork/FileActivityRow.stories.tsx +70 -0
- package/src/components/custom/ActiveWork/FileActivityRow.test.tsx +67 -0
- package/src/components/custom/ActiveWork/FileActivityRow.tsx +130 -0
- package/src/components/custom/ActiveWork/FileHistoryExplorer.stories.tsx +84 -0
- package/src/components/custom/ActiveWork/FileHistoryExplorer.test.tsx +99 -0
- package/src/components/custom/ActiveWork/FileHistoryExplorer.tsx +138 -0
- package/src/components/custom/ActiveWork/FilePathLabel.stories.tsx +39 -0
- package/src/components/custom/ActiveWork/FilePathLabel.test.tsx +53 -0
- package/src/components/custom/ActiveWork/FilePathLabel.tsx +63 -0
- package/src/components/custom/ActiveWork/InitiativeBrief.stories.tsx +50 -0
- package/src/components/custom/ActiveWork/InitiativeBrief.test.tsx +62 -0
- package/src/components/custom/ActiveWork/InitiativeBrief.tsx +182 -0
- package/src/components/custom/ActiveWork/InitiativeCard.stories.tsx +73 -0
- package/src/components/custom/ActiveWork/InitiativeCard.test.tsx +68 -0
- package/src/components/custom/ActiveWork/InitiativeCard.tsx +136 -0
- package/src/components/custom/ActiveWork/InitiativeHeader.stories.tsx +59 -0
- package/src/components/custom/ActiveWork/InitiativeHeader.test.tsx +40 -0
- package/src/components/custom/ActiveWork/InitiativeHeader.tsx +73 -0
- package/src/components/custom/ActiveWork/InitiativeReader.stories.tsx +145 -0
- package/src/components/custom/ActiveWork/OpenLoops.stories.tsx +58 -0
- package/src/components/custom/ActiveWork/OpenLoops.test.tsx +61 -0
- package/src/components/custom/ActiveWork/OpenLoops.tsx +107 -0
- package/src/components/custom/ActiveWork/PortfolioOverview.stories.tsx +101 -0
- package/src/components/custom/ActiveWork/PortfolioOverview.test.tsx +81 -0
- package/src/components/custom/ActiveWork/PortfolioOverview.tsx +83 -0
- package/src/components/custom/ActiveWork/README.md +305 -0
- package/src/components/custom/ActiveWork/SessionDetail.stories.tsx +71 -0
- package/src/components/custom/ActiveWork/SessionDetail.test.tsx +84 -0
- package/src/components/custom/ActiveWork/SessionDetail.tsx +180 -0
- package/src/components/custom/ActiveWork/SessionList.stories.tsx +69 -0
- package/src/components/custom/ActiveWork/SessionList.test.tsx +78 -0
- package/src/components/custom/ActiveWork/SessionList.tsx +103 -0
- package/src/components/custom/ActiveWork/SessionListItem.stories.tsx +64 -0
- package/src/components/custom/ActiveWork/SessionListItem.test.tsx +62 -0
- package/src/components/custom/ActiveWork/SessionListItem.tsx +152 -0
- package/src/components/custom/ActiveWork/SessionReader.stories.tsx +113 -0
- package/src/components/custom/ActiveWork/SeverityLabel.stories.tsx +51 -0
- package/src/components/custom/ActiveWork/SeverityLabel.test.tsx +68 -0
- package/src/components/custom/ActiveWork/SeverityLabel.tsx +97 -0
- package/src/components/custom/ActiveWork/TaskRow.stories.tsx +63 -0
- package/src/components/custom/ActiveWork/TaskRow.test.tsx +112 -0
- package/src/components/custom/ActiveWork/TaskRow.tsx +176 -0
- package/src/components/custom/ActiveWork/TaskTable.stories.tsx +123 -0
- package/src/components/custom/ActiveWork/TaskTable.test.tsx +233 -0
- package/src/components/custom/ActiveWork/TaskTable.tsx +321 -0
- package/src/components/custom/ActiveWork/active-work-depth.test.tsx +102 -0
- package/src/components/custom/ActiveWork/file-history-fixture.ts +91 -0
- package/src/components/custom/ActiveWork/format-time.test.ts +23 -0
- package/src/components/custom/ActiveWork/format-time.ts +32 -0
- package/src/components/custom/ActiveWork/index.ts +19 -0
- package/src/components/custom/ActiveWork/initiative-fixture.ts +104 -0
- package/src/components/custom/ActiveWork/session-fixture.ts +267 -0
- package/src/components/custom/ActiveWork/session-linkers.ts +56 -0
- package/src/components/custom/ActiveWork/task-list-fixture.ts +97 -0
- package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +12 -6
- package/src/components/custom/DateTime/DateTime.stories.tsx +2 -1
- package/src/components/custom/DateTime/DateTime.test.tsx +3 -1
- package/src/components/custom/EmptyState/EmptyState.stories.tsx +22 -9
- package/src/components/custom/EmptyState/EmptyState.test.tsx +7 -13
- package/src/components/custom/EmptyState/EmptyState.tsx +4 -18
- package/src/components/custom/Fatigue/DualGhostSpark.stories.tsx +4 -3
- package/src/components/custom/Fatigue/FatigueLights.stories.tsx +2 -2
- package/src/components/custom/Fatigue/GhostSpark.stories.tsx +4 -3
- package/src/components/custom/Fatigue/LiveFatigueCard.stories.tsx +2 -2
- package/src/components/custom/Fatigue/LiveFatigueCard.tsx +8 -4
- package/src/components/custom/Fatigue/LiveFatiguePanel.stories.tsx +116 -6
- package/src/components/custom/Fatigue/LiveFatiguePanel.test.tsx +134 -0
- package/src/components/custom/Fatigue/LiveFatiguePanel.tsx +54 -83
- package/src/components/custom/Fatigue/README.md +40 -3
- package/src/components/custom/Fatigue/RomProgressionChart.stories.tsx +2 -2
- package/src/components/custom/Fatigue/SilverRedPalette.stories.tsx +2 -1
- package/src/components/custom/Fatigue/VelocityHero.stories.tsx +4 -3
- package/src/components/custom/Fatigue/VerdictHero.stories.tsx +2 -2
- package/src/components/custom/Fatigue/fatigue-mock.test.ts +0 -5
- package/src/components/custom/Fatigue/fatigue-mock.ts +2 -10
- package/src/components/custom/Fatigue/fatigue-tokens.ts +12 -0
- package/src/components/custom/Fatigue/index.ts +17 -5
- package/src/components/custom/Fatigue/panel-layout.test.ts +149 -0
- package/src/components/custom/Fatigue/panel-layout.ts +185 -0
- package/src/components/custom/Gauge/Gauge.stories.tsx +6 -3
- package/src/components/custom/Gauge/Gauge.test.tsx +1 -1
- package/src/components/custom/Gauge/Gauge.tsx +7 -2
- package/src/components/custom/Metric/Metric.stories.tsx +10 -1
- package/src/components/custom/Metric/Metric.test.tsx +1 -3
- package/src/components/custom/Metric/Metric.tsx +5 -20
- package/src/components/custom/Prose/MarkdownProse.stories.tsx +88 -0
- package/src/components/custom/Prose/MarkdownProse.test.tsx +87 -0
- package/src/components/custom/Prose/MarkdownProse.tsx +201 -0
- package/src/components/custom/Prose/README.md +58 -0
- package/src/components/custom/Prose/index.ts +1 -0
- package/src/components/custom/Scatter/Scatter.stories.tsx +55 -14
- package/src/components/custom/Scatter/Scatter.test.tsx +2 -2
- package/src/components/custom/Scatter/Scatter.tsx +122 -18
- package/src/components/custom/Sidebar/Sidebar.stories.tsx +41 -8
- package/src/components/custom/Sidebar/Sidebar.test.tsx +5 -1
- package/src/components/custom/Sidebar/Sidebar.tsx +16 -33
- package/src/components/custom/Table/Table.stories.tsx +99 -16
- package/src/components/custom/Table/Table.test.tsx +273 -86
- package/src/components/custom/Table/Table.tsx +159 -67
- package/src/components/custom/Table/column-fit.test.ts +75 -0
- package/src/components/custom/Table/column-fit.ts +116 -0
- package/src/components/custom/Table/index.ts +9 -0
- package/src/components/custom/TimerReadout/TimerReadout.stories.tsx +8 -6
- package/src/components/custom/Treemap/Treemap.stories.tsx +15 -12
- package/src/components/custom/Treemap/Treemap.test.tsx +1 -1
- package/src/components/custom/Treemap/Treemap.tsx +11 -3
- package/src/components/custom/Typography/Typography.stories.tsx +28 -1
- package/src/components/custom/Typography/Typography.test.tsx +34 -0
- package/src/components/custom/Typography/Typography.tsx +6 -0
- package/src/components/custom/Typography/index.ts +1 -8
- package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +11 -2
- package/src/components/custom/Workout/BaseBadge.stories.tsx +27 -11
- package/src/components/custom/Workout/BaseBadge.test.tsx +31 -11
- package/src/components/custom/Workout/BaseBadge.tsx +6 -2
- package/src/components/custom/Workout/BodyMap.stories.tsx +4 -1
- package/src/components/custom/Workout/BodyMap.tsx +8 -4
- package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +28 -6
- package/src/components/custom/Workout/BodyMapDetailPanel.test.tsx +11 -15
- package/src/components/custom/Workout/BodyMapDetailPanel.tsx +50 -12
- package/src/components/custom/Workout/CapacityBandChart.stories.tsx +12 -9
- package/src/components/custom/Workout/CapacityBandChart.test.tsx +24 -51
- package/src/components/custom/Workout/CapacityBandChart.tsx +144 -111
- package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
- package/src/components/custom/Workout/DeviationBar.test.tsx +10 -4
- package/src/components/custom/Workout/DeviationBar.tsx +24 -16
- package/src/components/custom/Workout/DualVelocityStrip.stories.tsx +183 -0
- package/src/components/custom/Workout/DualVelocityStrip.test.tsx +32 -18
- package/src/components/custom/Workout/ExerciseCard.stories.tsx +12 -11
- package/src/components/custom/Workout/ExerciseCard.test.tsx +52 -22
- package/src/components/custom/Workout/ExerciseCard.tsx +99 -183
- package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +95 -12
- package/src/components/custom/Workout/ExerciseCardHeading.test.tsx +189 -1
- package/src/components/custom/Workout/ExerciseCardHeading.tsx +167 -18
- package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +9 -1
- package/src/components/custom/Workout/ExerciseDetailPage.tsx +3 -2
- package/src/components/custom/Workout/ExerciseHeading.stories.tsx +8 -8
- package/src/components/custom/Workout/ExerciseHeading.tsx +194 -46
- package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +12 -9
- package/src/components/custom/Workout/ExerciseIndicator.test.tsx +27 -11
- package/src/components/custom/Workout/ExerciseIndicator.tsx +14 -15
- package/src/components/custom/Workout/FatigueMeter.stories.tsx +5 -5
- package/src/components/custom/Workout/InputBar.stories.tsx +4 -5
- package/src/components/custom/Workout/InputBar.tsx +4 -2
- package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
- package/src/components/custom/Workout/IntensityBar.test.tsx +5 -1
- package/src/components/custom/Workout/IntensityBar.tsx +8 -3
- package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +5 -4
- package/src/components/custom/Workout/MesoCard.stories.tsx +4 -1
- package/src/components/custom/Workout/MesoCard.test.tsx +7 -18
- package/src/components/custom/Workout/MesoCard.tsx +14 -10
- package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
- package/src/components/custom/Workout/MesoProgressBar.test.tsx +18 -11
- package/src/components/custom/Workout/MesoProgressBar.tsx +15 -13
- package/src/components/custom/Workout/MesoStatusCard.stories.tsx +2 -1
- package/src/components/custom/Workout/MesoStatusCard.test.tsx +14 -44
- package/src/components/custom/Workout/MesoStatusCard.tsx +20 -33
- package/src/components/custom/Workout/MetricTiles.stories.tsx +3 -2
- package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +2 -8
- package/src/components/custom/Workout/MuscleGroupChip.test.tsx +23 -31
- package/src/components/custom/Workout/MuscleGroupChip.tsx +28 -67
- package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +9 -4
- package/src/components/custom/Workout/PlaceholderStrip.test.tsx +17 -10
- package/src/components/custom/Workout/PlaceholderStrip.tsx +9 -8
- package/src/components/custom/Workout/PrBadge.stories.tsx +2 -1
- package/src/components/custom/Workout/PrBadge.test.tsx +3 -9
- package/src/components/custom/Workout/PrBadge.tsx +13 -17
- package/src/components/custom/Workout/PrHistoryModal.stories.tsx +16 -3
- package/src/components/custom/Workout/PrHistoryModal.test.tsx +13 -36
- package/src/components/custom/Workout/PrHistoryModal.tsx +8 -4
- package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +6 -1
- package/src/components/custom/Workout/README.md +68 -17
- package/src/components/custom/Workout/ReadinessCheck.stories.tsx +3 -2
- package/src/components/custom/Workout/ReadinessCheck.test.tsx +12 -18
- package/src/components/custom/Workout/ReadinessCheck.tsx +22 -4
- package/src/components/custom/Workout/RestTimer.stories.tsx +16 -4
- package/src/components/custom/Workout/RestTimer.tsx +4 -2
- package/src/components/custom/Workout/ScheduleTiles.stories.tsx +3 -2
- package/src/components/custom/Workout/ScheduleTiles.test.tsx +3 -4
- package/src/components/custom/Workout/ScheduleTiles.tsx +4 -7
- package/src/components/custom/Workout/SegmentedBar.stories.tsx +21 -17
- package/src/components/custom/Workout/SegmentedBar.tsx +10 -5
- package/src/components/custom/Workout/SegmentedProgressBar.stories.tsx +5 -5
- package/src/components/custom/Workout/SessionHeader.stories.tsx +6 -6
- package/src/components/custom/Workout/SessionHeader.tsx +12 -12
- package/src/components/custom/Workout/SessionRail.stories.tsx +12 -11
- package/src/components/custom/Workout/SessionRail.tsx +7 -9
- package/src/components/custom/Workout/SetBar.stories.tsx +10 -6
- package/src/components/custom/Workout/SetBar.test.tsx +49 -4
- package/src/components/custom/Workout/SetBar.tsx +38 -12
- package/src/components/custom/Workout/SetRow.stories.tsx +3 -2
- package/src/components/custom/Workout/SetRow.test.tsx +16 -2
- package/src/components/custom/Workout/SetRow.tsx +61 -23
- package/src/components/custom/Workout/SetStrip.stories.tsx +63 -7
- package/src/components/custom/Workout/SetStrip.test.tsx +25 -0
- package/src/components/custom/Workout/SetStrip.tsx +24 -4
- package/src/components/custom/Workout/SetTableHeader.stories.tsx +6 -6
- package/src/components/custom/Workout/SetTableHeader.tsx +9 -12
- package/src/components/custom/Workout/SetsRepsLoad.stories.tsx +7 -7
- package/src/components/custom/Workout/SetsRepsLoad.tsx +24 -12
- package/src/components/custom/Workout/Sparkline.stories.tsx +14 -11
- package/src/components/custom/Workout/Sparkline.test.tsx +37 -19
- package/src/components/custom/Workout/Sparkline.tsx +14 -18
- package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
- package/src/components/custom/Workout/StatusDot.test.tsx +44 -5
- package/src/components/custom/Workout/StatusDot.tsx +93 -63
- package/src/components/custom/Workout/StatusPill.stories.tsx +1 -1
- package/src/components/custom/Workout/StatusPill.test.tsx +6 -15
- package/src/components/custom/Workout/StatusPill.tsx +27 -38
- package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +11 -4
- package/src/components/custom/Workout/StrengthTrendChart.test.tsx +15 -37
- package/src/components/custom/Workout/StrengthTrendChart.tsx +39 -36
- package/src/components/custom/Workout/SupersetWrapper.stories.tsx +27 -64
- package/src/components/custom/Workout/SupersetWrapper.test.tsx +16 -14
- package/src/components/custom/Workout/SupersetWrapper.tsx +17 -21
- package/src/components/custom/Workout/TempoDisplay.stories.tsx +26 -10
- package/src/components/custom/Workout/TempoDisplay.test.tsx +18 -4
- package/src/components/custom/Workout/TempoDisplay.tsx +2 -0
- package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +9 -1
- package/src/components/custom/Workout/VelocityStrip.compact.stories.tsx +4 -1
- package/src/components/custom/Workout/VelocityStrip.dual.stories.tsx +10 -6
- package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +5 -2
- package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +44 -1
- package/src/components/custom/Workout/VelocityStrip.stories.tsx +12 -6
- package/src/components/custom/Workout/VelocityStrip.test.tsx +48 -6
- package/src/components/custom/Workout/VelocityStrip.tsx +26 -14
- package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +6 -7
- package/src/components/custom/Workout/VolumeLandmarkBar.tsx +15 -6
- package/src/components/custom/Workout/WeekRow.stories.tsx +4 -1
- package/src/components/custom/Workout/WeekRow.test.tsx +18 -15
- package/src/components/custom/Workout/WeekRow.tsx +23 -16
- package/src/components/custom/Workout/WeightBadge.stories.tsx +2 -1
- package/src/components/custom/Workout/WeightBadge.test.tsx +50 -19
- package/src/components/custom/Workout/WeightBadge.tsx +28 -27
- package/src/components/custom/Workout/WorkoutCard.stories.tsx +6 -1
- package/src/components/custom/Workout/WorkoutCard.test.tsx +18 -0
- package/src/components/custom/Workout/WorkoutCard.tsx +35 -57
- package/src/components/custom/Workout/WorkoutPill.stories.tsx +5 -2
- package/src/components/custom/Workout/WorkoutPill.test.tsx +39 -7
- package/src/components/custom/Workout/WorkoutPill.tsx +88 -57
- package/src/components/custom/Workout/ZoneTrack.stories.tsx +10 -7
- package/src/components/custom/Workout/ZoneTrack.tsx +10 -9
- package/src/components/custom/Workout/exerciseRowState.ts +29 -0
- package/src/components/custom/Workout/index.ts +17 -2
- package/src/components/custom/Workout/metricText.stories.tsx +15 -13
- package/src/components/custom/Workout/metricText.tsx +8 -2
- package/src/components/custom/Workout/setHeadingKit.tsx +219 -36
- package/src/components/custom/Workout/velocity-story-kit.tsx +2 -2
- package/src/components/custom/charts/README.md +66 -0
- package/src/components/custom/charts/SetBarChart.test.tsx +46 -1
- package/src/components/custom/charts/SetBarChart.tsx +56 -9
- package/src/components/custom/charts/SparkBars.stories.tsx +55 -0
- package/src/components/custom/charts/SparkBars.test.tsx +69 -0
- package/src/components/custom/charts/SparkBars.tsx +85 -0
- package/src/components/custom/charts/flatBarGeometry.test.ts +41 -0
- package/src/components/custom/charts/flatBarGeometry.ts +32 -0
- package/src/components/custom/charts/index.ts +3 -0
- package/src/components/custom/index.ts +3 -0
- package/src/components/custom/stepper/Stepper.stories.tsx +13 -10
- package/src/components/custom/stepper/Stepper.test.tsx +1 -7
- package/src/components/custom/stepper/Stepper.tsx +21 -34
- package/src/components/icons/icons.stories.tsx +1 -1
- package/src/components/icons/icons.tsx +51 -0
- package/src/components/shell/AppShell.stories.tsx +150 -0
- package/src/components/shell/AppShell.test.tsx +70 -0
- package/src/components/shell/AppShell.tsx +90 -0
- package/src/components/shell/BrandLockup.stories.tsx +66 -5
- package/src/components/shell/BrandLockup.test.tsx +34 -1
- package/src/components/shell/BrandLockup.tsx +27 -11
- package/src/components/shell/NavItem.stories.tsx +4 -4
- package/src/components/shell/NavItem.test.tsx +17 -0
- package/src/components/shell/NavItem.tsx +17 -5
- package/src/components/shell/README.md +89 -26
- package/src/components/shell/SideNav.stories.tsx +38 -7
- package/src/components/shell/SideNav.test.tsx +16 -8
- package/src/components/shell/SideNav.tsx +17 -17
- package/src/components/shell/TopBar.stories.tsx +44 -19
- package/src/components/shell/TopBar.test.tsx +34 -12
- package/src/components/shell/TopBar.tsx +61 -40
- package/src/components/shell/brands.tsx +66 -0
- package/src/components/shell/index.ts +9 -8
- package/src/components/shell/{DeviceIndicator.stories.tsx → workout/DeviceIndicator.stories.tsx} +2 -2
- package/src/components/shell/{DeviceIndicator.tsx → workout/DeviceIndicator.tsx} +2 -2
- package/src/components/shell/{DeviceMenu.stories.tsx → workout/DeviceMenu.stories.tsx} +2 -2
- package/src/components/shell/{DeviceMenu.tsx → workout/DeviceMenu.tsx} +3 -3
- package/src/components/shell/{DeviceRow.stories.tsx → workout/DeviceRow.stories.tsx} +2 -2
- package/src/components/shell/{DeviceRow.tsx → workout/DeviceRow.tsx} +3 -3
- package/src/components/shell/{SessionStatePill.stories.tsx → workout/SessionStatePill.stories.tsx} +2 -2
- package/src/components/shell/{SessionStatePill.tsx → workout/SessionStatePill.tsx} +5 -3
- package/src/components/shell/workout/WorkoutShell.stories.tsx +71 -0
- package/src/components/shell/{DashboardShell.test.tsx → workout/WorkoutShell.test.tsx} +19 -8
- package/src/components/shell/workout/WorkoutShell.tsx +77 -0
- package/src/components/shell/workout/WorkoutTopBar.stories.tsx +58 -0
- package/src/components/shell/workout/WorkoutTopBar.test.tsx +39 -0
- package/src/components/shell/workout/WorkoutTopBar.tsx +52 -0
- package/src/components/shell/workout/index.ts +9 -0
- package/src/components/shell/workout/workoutNavItems.tsx +16 -0
- package/src/components/ui/README.md +110 -0
- package/src/components/ui/alert/Alert.stories.tsx +15 -5
- package/src/components/ui/alert/Alert.tsx +24 -3
- package/src/components/ui/autocomplete/Autocomplete.stories.tsx +11 -13
- package/src/components/ui/autocomplete/Autocomplete.test.tsx +7 -25
- package/src/components/ui/autocomplete/Autocomplete.tsx +21 -28
- package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
- package/src/components/ui/avatar/Avatar.tsx +2 -10
- package/src/components/ui/badge/Badge.stories.tsx +83 -28
- package/src/components/ui/badge/Badge.test.tsx +19 -3
- package/src/components/ui/badge/Badge.tsx +48 -55
- package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
- package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +4 -16
- package/src/components/ui/button/Button.stories.tsx +46 -12
- package/src/components/ui/button/Button.tsx +44 -37
- package/src/components/ui/card/Card.stories.tsx +90 -94
- package/src/components/ui/card/Card.test.tsx +80 -6
- package/src/components/ui/card/Card.tsx +150 -203
- package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
- package/src/components/ui/checkbox/Checkbox.tsx +7 -27
- package/src/components/ui/chip/Chip.stories.tsx +37 -13
- package/src/components/ui/chip/Chip.test.tsx +10 -6
- package/src/components/ui/chip/Chip.tsx +76 -91
- package/src/components/ui/collapse/Collapse.stories.tsx +23 -23
- package/src/components/ui/collapse/Collapse.tsx +6 -11
- package/src/components/ui/data-row/DataRow.stories.tsx +14 -3
- package/src/components/ui/data-row/DataRow.test.tsx +5 -15
- package/src/components/ui/data-row/DataRow.tsx +18 -7
- package/src/components/ui/divider/Divider.stories.tsx +1 -1
- package/src/components/ui/divider/Divider.tsx +1 -5
- package/src/components/ui/drawer/Drawer.stories.tsx +5 -16
- package/src/components/ui/drawer/Drawer.tsx +13 -27
- package/src/components/ui/floating-overlay-depth.test.tsx +143 -0
- package/src/components/ui/form-field/FormField.stories.tsx +6 -20
- package/src/components/ui/form-field/FormField.test.tsx +1 -5
- package/src/components/ui/form-field/FormField.tsx +10 -46
- package/src/components/ui/help-tip/HelpTip.stories.tsx +8 -19
- package/src/components/ui/help-tip/HelpTip.test.tsx +1 -3
- package/src/components/ui/help-tip/HelpTip.tsx +21 -22
- package/src/components/ui/help-tip/index.ts +7 -1
- package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
- package/src/components/ui/icon-box/IconBox.test.tsx +1 -7
- package/src/components/ui/icon-box/IconBox.tsx +10 -18
- package/src/components/ui/indicator/Indicator.stories.tsx +5 -4
- package/src/components/ui/indicator/Indicator.tsx +20 -14
- package/src/components/ui/input/Input.stories.tsx +3 -11
- package/src/components/ui/input/Input.test.tsx +2 -10
- package/src/components/ui/input/Input.tsx +10 -25
- package/src/components/ui/input/index.ts +7 -1
- package/src/components/ui/link/Link.stories.tsx +19 -7
- package/src/components/ui/list-item/ListItem.stories.tsx +17 -5
- package/src/components/ui/list-item/ListItem.tsx +2 -9
- package/src/components/ui/menu/Menu.stories.tsx +21 -15
- package/src/components/ui/menu/Menu.test.tsx +2 -11
- package/src/components/ui/menu/Menu.tsx +12 -14
- package/src/components/ui/menu/index.ts +8 -1
- package/src/components/ui/modal/Modal.stories.tsx +2 -2
- package/src/components/ui/modal/Modal.tsx +31 -19
- package/src/components/ui/pill/Pill.stories.tsx +111 -23
- package/src/components/ui/pill/Pill.test.tsx +101 -3
- package/src/components/ui/pill/Pill.tsx +168 -49
- package/src/components/ui/pill/index.ts +1 -1
- package/src/components/ui/popover/Popover.stories.tsx +6 -8
- package/src/components/ui/popover/Popover.test.tsx +1 -6
- package/src/components/ui/popover/Popover.tsx +20 -15
- package/src/components/ui/popover/index.ts +7 -1
- package/src/components/ui/progress/Progress.stories.tsx +3 -12
- package/src/components/ui/progress/Progress.test.tsx +1 -5
- package/src/components/ui/progress/Progress.tsx +9 -6
- package/src/components/ui/radio/Radio.stories.tsx +42 -21
- package/src/components/ui/radio/Radio.test.tsx +6 -2
- package/src/components/ui/radio/Radio.tsx +23 -22
- package/src/components/ui/section/Section.stories.tsx +12 -21
- package/src/components/ui/section/Section.test.tsx +1 -6
- package/src/components/ui/section/Section.tsx +1 -3
- package/src/components/ui/section/index.ts +1 -5
- package/src/components/ui/select/Select.stories.tsx +3 -13
- package/src/components/ui/select/Select.test.tsx +2 -11
- package/src/components/ui/select/Select.tsx +16 -15
- package/src/components/ui/skeleton/Skeleton.stories.tsx +3 -11
- package/src/components/ui/skeleton/Skeleton.tsx +3 -14
- package/src/components/ui/skeleton/index.ts +1 -7
- package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
- package/src/components/ui/spinner/Spinner.tsx +4 -5
- package/src/components/ui/stack/Stack.stories.tsx +8 -8
- package/src/components/ui/stack/Stack.tsx +1 -1
- package/src/components/ui/surface/Surface.stories.tsx +92 -33
- package/src/components/ui/surface/Surface.test.tsx +88 -28
- package/src/components/ui/surface/Surface.tsx +37 -87
- package/src/components/ui/surface/SurfaceContext.ts +22 -62
- package/src/components/ui/surface/index.ts +8 -0
- package/src/components/ui/surface/resolveSurface.ts +98 -0
- package/src/components/ui/surface/surface.contract.test.ts +6 -1
- package/src/components/ui/switch/Switch.stories.tsx +1 -1
- package/src/components/ui/switch/Switch.tsx +7 -6
- package/src/components/ui/tabs/Tabs.stories.tsx +1 -1
- package/src/components/ui/tabs/Tabs.tsx +8 -21
- package/src/components/ui/tabs/index.ts +1 -7
- package/src/components/ui/tile/Tile.stories.tsx +1 -1
- package/src/components/ui/tile/Tile.tsx +3 -0
- package/src/components/ui/toast/Toast.stories.tsx +1 -1
- package/src/components/ui/toast/Toast.test.tsx +2 -8
- package/src/components/ui/toast/Toast.tsx +49 -57
- package/src/components/ui/toast/index.ts +0 -1
- package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +92 -71
- package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +3 -18
- package/src/components/ui/toolbar-button/ToolbarButton.tsx +38 -49
- package/src/components/ui/tooltip/Tooltip.stories.tsx +12 -3
- package/src/components/ui/tooltip/Tooltip.test.tsx +21 -3
- package/src/components/ui/tooltip/Tooltip.tsx +55 -36
- package/src/components/ui/tooltip/index.ts +1 -1
- package/src/components/ui/trigger/TriggerSurface.test.tsx +215 -0
- package/src/components/ui/trigger/TriggerSurface.tsx +81 -0
- package/src/components/ui/trigger/index.ts +2 -0
- package/src/stories/PresetShowcase.stories.tsx +101 -48
- package/src/test/no-device-internals.test.ts +3 -3
- package/src/test/no-raw-color.test.ts +39 -0
- package/src/theme/Alpha.stories.tsx +103 -0
- package/src/theme/ChoosingTokens.stories.tsx +48 -0
- package/src/theme/ColorPalettes.stories.tsx +62 -45
- package/src/theme/ColorPrimitives.stories.tsx +35 -35
- package/src/theme/Depth.stories.tsx +132 -65
- package/src/theme/DepthCalibration.stories.tsx +23 -21
- package/src/theme/ProposedTokensVW82.stories.tsx +414 -0
- package/src/theme/ThemeProvider.test.tsx +4 -4
- package/src/theme/barrel.ts +7 -6
- package/src/theme/color-stories.coverage.test.ts +1 -1
- package/src/theme/color-utils.ts +41 -29
- package/src/theme/config.completeness.test.ts +1 -1
- package/src/theme/config.ts +40 -0
- package/src/theme/elevation.test.ts +82 -56
- package/src/theme/elevation.ts +77 -561
- package/src/theme/global.css +45 -5
- package/src/theme/grey-ramp.test.ts +24 -8
- package/src/theme/lift.test.ts +39 -0
- package/src/theme/lift.ts +102 -0
- package/src/theme/materials.test.ts +5 -2
- package/src/theme/materials.ts +38 -4
- package/src/theme/native-theming-guard.test.ts +5 -5
- package/src/theme/presets/apply.ts +4 -0
- package/src/theme/presets/audiobook.ts +5 -2
- package/src/theme/presets/default.ts +4 -0
- package/src/theme/presets/index.ts +14 -1
- package/src/theme/presets/types.ts +1 -1
- package/src/theme/semantic-contrast.test.ts +13 -4
- package/src/theme/status-distinctness.test.ts +66 -0
- package/src/theme/subtle-tone-weight.test.ts +163 -0
- package/src/theme/surface-planes.test.ts +37 -0
- package/src/theme/surface-planes.ts +68 -0
- package/src/theme/tailwind-var-opacity.test.ts +163 -0
- package/src/theme/token-export-guard.test.ts +1 -3
- package/src/theme/tokens/primitives.test.ts +15 -7
- package/src/theme/tokens/primitives.ts +98 -33
- package/src/theme/tokens/semantic-hex-guard.test.ts +322 -0
- package/src/theme/tokens/semantic.ts +124 -54
- package/src/theme/wash-ladder.test.ts +58 -0
- package/src/theme/workout-tokens.ts +1 -7
- package/src/types/jest-axe.d.ts +2 -6
- package/src/types/raw-md.d.ts +5 -0
- package/src/utils/colors.alpha-adoption.test.ts +408 -0
- package/src/utils/colors.ts +4 -4
- package/src/utils/form.ts +1 -3
- package/src/utils/index.ts +2 -7
- package/src/utils/number-format.ts +17 -0
- package/src/utils/workout-format.ts +15 -6
- package/src/web-jsx/jsx-runtime.ts +1 -5
- package/tailwind.config.js +55 -9
- package/src/components/custom/Workout/ExerciseCardUnified.stories.tsx +0 -151
- package/src/components/custom/Workout/RowInventory.stories.tsx +0 -408
- package/src/components/custom/Workout/S3FullRail.stories.tsx +0 -46
- package/src/components/custom/Workout/S3SessionPace.stories.tsx +0 -95
- package/src/components/custom/Workout/S3SessionRailHeading.stories.tsx +0 -61
- package/src/components/custom/Workout/S3SetTypes.stories.tsx +0 -176
- package/src/components/custom/Workout/S3WorkoutExpansion.stories.tsx +0 -240
- package/src/components/shell/DashboardShell.stories.tsx +0 -65
- package/src/components/shell/DashboardShell.tsx +0 -75
- package/src/components/shell/SessionRailSpecimen.stories.tsx +0 -753
- package/src/theme/elevation.stories.tsx +0 -346
- package/src/utils/useTheme.ts +0 -72
- /package/src/components/shell/{DeviceIndicator.test.tsx → workout/DeviceIndicator.test.tsx} +0 -0
- /package/src/components/shell/{DeviceMenu.test.tsx → workout/DeviceMenu.test.tsx} +0 -0
- /package/src/components/shell/{DeviceRow.test.tsx → workout/DeviceRow.test.tsx} +0 -0
- /package/src/components/shell/{SessionStatePill.test.tsx → workout/SessionStatePill.test.tsx} +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { Surface } from '../../ui/surface'
|
|
3
|
+
import { CoChangeChip } from './CoChangeChip'
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof CoChangeChip> = {
|
|
6
|
+
title: 'Custom/ActiveWork/CoChangeChip',
|
|
7
|
+
component: CoChangeChip,
|
|
8
|
+
tags: ['autodocs', 'status:candidate', '!status:review'],
|
|
9
|
+
decorators: [
|
|
10
|
+
(Story) => (
|
|
11
|
+
<Surface level="base" className="min-h-screen p-6" testID="page-surface">
|
|
12
|
+
<Story />
|
|
13
|
+
</Surface>
|
|
14
|
+
),
|
|
15
|
+
],
|
|
16
|
+
parameters: {
|
|
17
|
+
layout: 'fullscreen',
|
|
18
|
+
docs: {
|
|
19
|
+
description: {
|
|
20
|
+
component:
|
|
21
|
+
'**Molecule.** One symmetric "these two files change together" pair. Only basenames are ' +
|
|
22
|
+
'shown — at chip size the directory is noise. Composes ' +
|
|
23
|
+
'[Card](?path=/docs/components-molecules-card--docs), ' +
|
|
24
|
+
'[Pill](?path=/docs/components-atoms-pill--docs) and ' +
|
|
25
|
+
'[FilePathLabel](?path=/docs/custom-activework-filepathlabel--docs). ' +
|
|
26
|
+
'Used by [FileHistoryExplorer](?path=/docs/custom-activework-filehistoryexplorer--docs).',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
args: {
|
|
31
|
+
a: 'src/commands/open.ts',
|
|
32
|
+
b: 'src/commands/_open-helpers.ts',
|
|
33
|
+
count: 19,
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
export default meta
|
|
37
|
+
type Story = StoryObj<typeof CoChangeChip>
|
|
38
|
+
|
|
39
|
+
export const Default: Story = {}
|
|
40
|
+
|
|
41
|
+
/** A weak pair — the count pill is the only thing distinguishing it. */
|
|
42
|
+
export const WeakPair: Story = {
|
|
43
|
+
args: { a: 'src/utils/fs-atomic.ts', b: 'src/utils/flock.ts', count: 2 },
|
|
44
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { render, screen } from '@testing-library/react'
|
|
3
|
+
import { axe } from 'jest-axe'
|
|
4
|
+
import { CoChangeChip } from './CoChangeChip'
|
|
5
|
+
|
|
6
|
+
describe('CoChangeChip', () => {
|
|
7
|
+
it('renders both basenames and the count', () => {
|
|
8
|
+
render(<CoChangeChip a="src/commands/open.ts" b="src/registry/index.ts" count={19} />)
|
|
9
|
+
expect(screen.getByText('open.ts')).toBeInTheDocument()
|
|
10
|
+
expect(screen.getByText('index.ts')).toBeInTheDocument()
|
|
11
|
+
expect(screen.getByText('19×')).toBeInTheDocument()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('hides the directory prefixes', () => {
|
|
15
|
+
render(<CoChangeChip a="src/commands/open.ts" b="src/registry/index.ts" count={19} />)
|
|
16
|
+
expect(screen.queryByText('src/commands/')).not.toBeInTheDocument()
|
|
17
|
+
expect(screen.queryByText('src/registry/')).not.toBeInTheDocument()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('describes the pair for screen readers', () => {
|
|
21
|
+
render(<CoChangeChip a="a.ts" b="b.ts" count={3} />)
|
|
22
|
+
expect(screen.getByLabelText('a.ts and b.ts changed together 3 times')).toBeInTheDocument()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('has no a11y violations', async () => {
|
|
26
|
+
const { container } = render(<CoChangeChip a="a.ts" b="b.ts" count={3} />)
|
|
27
|
+
expect(await axe(container)).toHaveNoViolations()
|
|
28
|
+
})
|
|
29
|
+
})
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
|
+
import { View } from 'react-native'
|
|
3
|
+
import { Card } from '../../ui/card'
|
|
4
|
+
import { Pill } from '../../ui/pill'
|
|
5
|
+
import { Typography } from '../Typography'
|
|
6
|
+
import { FilePathLabel } from './FilePathLabel'
|
|
7
|
+
|
|
8
|
+
export interface CoChangeChipProps {
|
|
9
|
+
/** One side of the pair. */
|
|
10
|
+
a: string
|
|
11
|
+
/** The other side. Order is not meaningful — co-change is symmetric. */
|
|
12
|
+
b: string
|
|
13
|
+
/** How many sessions changed both files. */
|
|
14
|
+
count: number
|
|
15
|
+
className?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* CoChangeChip — one symmetric "these two files change together" pair.
|
|
20
|
+
*
|
|
21
|
+
* Only basenames are shown: at chip size the directory is noise, and the pair
|
|
22
|
+
* is a pointer into {@link FileActivityDetail} rather than a full identifier.
|
|
23
|
+
* Used by {@link FileHistoryExplorer}'s repo-wide co-change strip.
|
|
24
|
+
*
|
|
25
|
+
* @deprecated Use `<Pill tone="brand" size="xs">` — removed after AW-127 consumer migration.
|
|
26
|
+
*/
|
|
27
|
+
export function CoChangeChip({ a, b, count, className }: CoChangeChipProps) {
|
|
28
|
+
return (
|
|
29
|
+
<Card
|
|
30
|
+
variant="filled"
|
|
31
|
+
className={`gap-1 p-2.5 ${className ?? ''}`}
|
|
32
|
+
accessibilityRole="text"
|
|
33
|
+
accessibilityLabel={`${a} and ${b} changed together ${count} times`}
|
|
34
|
+
testID="co-change-chip"
|
|
35
|
+
>
|
|
36
|
+
<View className="flex-row items-center gap-2">
|
|
37
|
+
<FilePathLabel path={a} size="sm" baseOnly />
|
|
38
|
+
<Typography variant="caption" className="text-text-tertiary">
|
|
39
|
+
↔
|
|
40
|
+
</Typography>
|
|
41
|
+
<FilePathLabel path={b} size="sm" baseOnly />
|
|
42
|
+
<Pill variant="subtle" color="primary" size="xs">
|
|
43
|
+
{`${count}×`}
|
|
44
|
+
</Pill>
|
|
45
|
+
</View>
|
|
46
|
+
</Card>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { Eyebrow } from './Eyebrow'
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Eyebrow> = {
|
|
5
|
+
title: 'Components/Molecules/Eyebrow',
|
|
6
|
+
component: Eyebrow,
|
|
7
|
+
tags: ['autodocs', 'status:candidate', '!status:review'],
|
|
8
|
+
args: { children: 'Focused · by rank' },
|
|
9
|
+
argTypes: {
|
|
10
|
+
children: { control: 'text' },
|
|
11
|
+
},
|
|
12
|
+
parameters: {
|
|
13
|
+
docs: {
|
|
14
|
+
description: {
|
|
15
|
+
component:
|
|
16
|
+
'**Molecule.** An uppercase micro-label used above a value or a section of ' +
|
|
17
|
+
"content. Composes [Typography](?path=/docs/foundations-typography--docs)'s `overline` variant.",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
export default meta
|
|
23
|
+
type Story = StoryObj<typeof Eyebrow>
|
|
24
|
+
|
|
25
|
+
export const Default: Story = {}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { render, screen } from '@testing-library/react'
|
|
3
|
+
import { axe } from 'jest-axe'
|
|
4
|
+
import { Eyebrow } from './Eyebrow'
|
|
5
|
+
|
|
6
|
+
describe('Eyebrow', () => {
|
|
7
|
+
it('renders its children', () => {
|
|
8
|
+
render(<Eyebrow>Focused · by rank</Eyebrow>)
|
|
9
|
+
expect(screen.getByText('Focused · by rank')).toBeInTheDocument()
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('has no accessibility violations', async () => {
|
|
13
|
+
const { container } = render(<Eyebrow>Backburner</Eyebrow>)
|
|
14
|
+
expect(await axe(container)).toHaveNoViolations()
|
|
15
|
+
})
|
|
16
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
|
+
import { cn } from '../../../utils/cn'
|
|
4
|
+
import { Typography } from '../Typography'
|
|
5
|
+
|
|
6
|
+
export interface EyebrowProps {
|
|
7
|
+
children: ReactNode
|
|
8
|
+
className?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Eyebrow — an uppercase micro-label used above a value or a section of
|
|
13
|
+
* content (e.g. "Focused · by rank", a stat tile's caption). Composes
|
|
14
|
+
* {@link Typography}'s `overline` variant; never hand-roll the letter-spacing.
|
|
15
|
+
*/
|
|
16
|
+
export function Eyebrow({ children, className }: EyebrowProps) {
|
|
17
|
+
return (
|
|
18
|
+
<Typography
|
|
19
|
+
variant="overline"
|
|
20
|
+
color="inherit"
|
|
21
|
+
className={cn('text-xs font-semibold uppercase tracking-wider text-text-tertiary', className)}
|
|
22
|
+
>
|
|
23
|
+
{children}
|
|
24
|
+
</Typography>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { Surface } from '../../ui/surface'
|
|
3
|
+
import { FileActivityDetail } from './FileActivityDetail'
|
|
4
|
+
import { FILE_HISTORY_FILES } from './file-history-fixture'
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof FileActivityDetail> = {
|
|
7
|
+
title: 'Custom/ActiveWork/FileActivityDetail',
|
|
8
|
+
component: FileActivityDetail,
|
|
9
|
+
tags: ['autodocs', 'status:candidate', '!status:review'],
|
|
10
|
+
decorators: [
|
|
11
|
+
(Story) => (
|
|
12
|
+
<Surface level="base" className="min-h-screen w-full max-w-[560px] p-6" testID="page-surface">
|
|
13
|
+
<Story />
|
|
14
|
+
</Surface>
|
|
15
|
+
),
|
|
16
|
+
],
|
|
17
|
+
parameters: {
|
|
18
|
+
layout: 'fullscreen',
|
|
19
|
+
docs: {
|
|
20
|
+
description: {
|
|
21
|
+
component:
|
|
22
|
+
"**Card.** One file's full mined history: activity split, net char growth over " +
|
|
23
|
+
'sessions, and the files it changes together with. Composes ' +
|
|
24
|
+
'[Card](?path=/docs/components-molecules-card--docs), ' +
|
|
25
|
+
'[Tile](?path=/docs/components-atoms-tile--docs), ' +
|
|
26
|
+
'[Pill](?path=/docs/components-atoms-pill--docs), ' +
|
|
27
|
+
'[DataRow](?path=/docs/components-molecules-datarow--docs), ' +
|
|
28
|
+
'[DateTime](?path=/docs/custom-datetime--docs), ' +
|
|
29
|
+
'[SparkBars](?path=/docs/custom-charts-sparkbars--docs), ' +
|
|
30
|
+
'[FilePathLabel](?path=/docs/custom-activework-filepathlabel--docs) and ' +
|
|
31
|
+
'[Eyebrow](?path=/docs/components-molecules-eyebrow--docs).',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
args: { file: FILE_HISTORY_FILES[0] },
|
|
36
|
+
}
|
|
37
|
+
export default meta
|
|
38
|
+
type Story = StoryObj<typeof FileActivityDetail>
|
|
39
|
+
|
|
40
|
+
export const Default: Story = {}
|
|
41
|
+
|
|
42
|
+
/** Net-negative growth: the readout and the sparkline both flip to the error token. */
|
|
43
|
+
export const NetShrink: Story = {
|
|
44
|
+
args: { file: FILE_HISTORY_FILES[2] },
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** No co-changes mined — the section falls back to an explicit empty line. */
|
|
48
|
+
export const NoCoChanges: Story = {
|
|
49
|
+
args: { file: FILE_HISTORY_FILES[3] },
|
|
50
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { render, screen } from '@testing-library/react'
|
|
3
|
+
import { axe } from 'jest-axe'
|
|
4
|
+
import { FileActivityDetail } from './FileActivityDetail'
|
|
5
|
+
import { FILE_HISTORY_FILES } from './file-history-fixture'
|
|
6
|
+
|
|
7
|
+
const grew = FILE_HISTORY_FILES[0]
|
|
8
|
+
const shrank = FILE_HISTORY_FILES[2]
|
|
9
|
+
const noCoChange = FILE_HISTORY_FILES[3]
|
|
10
|
+
|
|
11
|
+
describe('FileActivityDetail', () => {
|
|
12
|
+
it('renders the split path header and session summary', () => {
|
|
13
|
+
render(<FileActivityDetail file={grew} />)
|
|
14
|
+
// The header dir and a co-change entry share this prefix, hence getAllByText.
|
|
15
|
+
expect(screen.getAllByText('src/commands/').length).toBeGreaterThan(0)
|
|
16
|
+
expect(screen.getByText('open.ts')).toBeInTheDocument()
|
|
17
|
+
expect(screen.getByText(/23 sessions/)).toBeInTheDocument()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('renders a tile per activity kind', () => {
|
|
21
|
+
render(<FileActivityDetail file={grew} />)
|
|
22
|
+
for (const label of ['Reads', 'Writes', 'Edits', 'Touches']) {
|
|
23
|
+
expect(screen.getByText(label)).toBeInTheDocument()
|
|
24
|
+
}
|
|
25
|
+
expect(screen.getByText('157')).toBeInTheDocument()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('compacts the growth figures', () => {
|
|
29
|
+
render(<FileActivityDetail file={grew} />)
|
|
30
|
+
expect(screen.getByText('+31.6k ch')).toBeInTheDocument()
|
|
31
|
+
expect(screen.getByText('+41.3k')).toBeInTheDocument()
|
|
32
|
+
expect(screen.getByText('-9.6k')).toBeInTheDocument()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('signs a net-negative growth readout', () => {
|
|
36
|
+
render(<FileActivityDetail file={shrank} />)
|
|
37
|
+
expect(screen.getByText('-3.4k ch')).toBeInTheDocument()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('lists co-changed files with their counts', () => {
|
|
41
|
+
render(<FileActivityDetail file={grew} />)
|
|
42
|
+
expect(screen.getByText('_open-helpers.ts')).toBeInTheDocument()
|
|
43
|
+
expect(screen.getByText('19×')).toBeInTheDocument()
|
|
44
|
+
expect(screen.getByText('11×')).toBeInTheDocument()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('falls back to an explicit line when nothing co-changed', () => {
|
|
48
|
+
render(<FileActivityDetail file={noCoChange} />)
|
|
49
|
+
expect(screen.getByText('no co-changes recorded')).toBeInTheDocument()
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('falls back to a dash for a missing touch date', () => {
|
|
53
|
+
render(<FileActivityDetail file={{ ...grew, firstTouched: null, lastTouched: null }} />)
|
|
54
|
+
expect(screen.getAllByText('—')).toHaveLength(2)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('renders "./" for a file at the repo root', () => {
|
|
58
|
+
render(<FileActivityDetail file={noCoChange} />)
|
|
59
|
+
expect(screen.getByText('./')).toBeInTheDocument()
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('has no a11y violations', async () => {
|
|
63
|
+
const { container } = render(<FileActivityDetail file={grew} />)
|
|
64
|
+
expect(await axe(container)).toHaveNoViolations()
|
|
65
|
+
})
|
|
66
|
+
})
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
|
+
import { View } from 'react-native'
|
|
3
|
+
// Deep import: `CardInset` is not on the card barrel yet (Lab/Depth reaches it the same way).
|
|
4
|
+
import { Card, CardInset } from '../../ui/card/Card'
|
|
5
|
+
import { Pill } from '../../ui/pill'
|
|
6
|
+
import { Tile } from '../../ui/tile'
|
|
7
|
+
import { DataRow } from '../../ui/data-row'
|
|
8
|
+
import { SparkBars } from '../charts'
|
|
9
|
+
import { DateTime } from '../DateTime'
|
|
10
|
+
import { Typography } from '../Typography'
|
|
11
|
+
import { formatCompact, formatSignedCompact } from '../../../utils/number-format'
|
|
12
|
+
import { resolveColor } from '../../../theme/resolve-color'
|
|
13
|
+
import { Eyebrow } from './Eyebrow'
|
|
14
|
+
import { FilePathLabel, splitPath } from './FilePathLabel'
|
|
15
|
+
import { FILE_EVENT_COLOR, type FileEventColors, type FileActivity } from './FileActivityRow'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Growth stats are *char deltas* — a measurement moving up or down — so they take
|
|
19
|
+
* the `result-*` family, not `status-*` and not {@link FILE_EVENT_COLOR}. A file
|
|
20
|
+
* that net-shrank was refactored, not broken. See TOKENS.md §1.
|
|
21
|
+
*/
|
|
22
|
+
const GROWTH_COLOR = {
|
|
23
|
+
added: resolveColor('result-improve'),
|
|
24
|
+
removed: resolveColor('result-degrade'),
|
|
25
|
+
neutral: resolveColor('result-neutral'),
|
|
26
|
+
} as const
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* `Tile` paints `surface-raised` itself, which IS this card's plane. Inside the
|
|
30
|
+
* lifted pane it has to read one plane up to separate at all. Overriding the
|
|
31
|
+
* fill at the call site keeps Tile's own default right for a page-level strip;
|
|
32
|
+
* teaching Tile to resolve its plane from the surface context is its own epic.
|
|
33
|
+
*/
|
|
34
|
+
const TILE_PLANE = 'bg-surface-overlay'
|
|
35
|
+
|
|
36
|
+
/** Another file that tends to change in the same session as this one. */
|
|
37
|
+
export interface FileCoChange {
|
|
38
|
+
path: string
|
|
39
|
+
count: number
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** {@link FileActivity} plus the history only the detail pane shows. */
|
|
43
|
+
export interface FileActivityDetailData extends FileActivity {
|
|
44
|
+
/** Distinct sessions that touched this file. */
|
|
45
|
+
sessions: number
|
|
46
|
+
charsAdded: number
|
|
47
|
+
charsRemoved: number
|
|
48
|
+
/** `charsAdded - charsRemoved`; negative means the file net-shrank. */
|
|
49
|
+
netGrowth: number
|
|
50
|
+
firstTouched?: string | null
|
|
51
|
+
lastTouched?: string | null
|
|
52
|
+
coChange: FileCoChange[]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface FileActivityDetailProps {
|
|
56
|
+
file: FileActivityDetailData
|
|
57
|
+
/** Override the read/write/edit fills. Defaults to {@link FILE_EVENT_COLOR}. */
|
|
58
|
+
eventColors?: FileEventColors
|
|
59
|
+
className?: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function GrowthStat({ label, value, color }: { label: string; value: string; color: string }) {
|
|
63
|
+
return (
|
|
64
|
+
<View className="flex-1 gap-0.5">
|
|
65
|
+
<Eyebrow>{label}</Eyebrow>
|
|
66
|
+
<Typography variant="mono" className="text-lg font-bold" style={{ color }}>
|
|
67
|
+
{value}
|
|
68
|
+
</Typography>
|
|
69
|
+
</View>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* FileActivityDetail — the right-hand pane of {@link FileHistoryExplorer}: one
|
|
75
|
+
* file's full mined history. Activity split, net char growth over sessions, and
|
|
76
|
+
* the files it changes together with — the co-change list being the part a
|
|
77
|
+
* plain file tree cannot show.
|
|
78
|
+
*
|
|
79
|
+
* Composes Card / CardInset / Tile / Pill / DataRow / DateTime plus
|
|
80
|
+
* {@link SparkBars}, {@link FilePathLabel} and {@link Eyebrow}.
|
|
81
|
+
*
|
|
82
|
+
* One lift only: the pane itself. Inside it the activity split reads as filled
|
|
83
|
+
* tiles a plane up and the growth block as a `CardInset` well a plane down.
|
|
84
|
+
*/
|
|
85
|
+
export function FileActivityDetail({
|
|
86
|
+
file,
|
|
87
|
+
eventColors = FILE_EVENT_COLOR,
|
|
88
|
+
className,
|
|
89
|
+
}: FileActivityDetailProps) {
|
|
90
|
+
const { dir, base } = splitPath(file.path)
|
|
91
|
+
const grew = file.netGrowth >= 0
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<Card className={`flex-1 gap-4 p-4 ${className ?? ''}`} testID="file-activity-detail">
|
|
95
|
+
<View className="gap-1">
|
|
96
|
+
<Typography variant="mono" className="text-xs text-text-tertiary">
|
|
97
|
+
{dir || './'}
|
|
98
|
+
</Typography>
|
|
99
|
+
<Typography variant="mono" className="text-base font-bold text-text-primary">
|
|
100
|
+
{base}
|
|
101
|
+
</Typography>
|
|
102
|
+
<View className="flex-row items-center gap-1">
|
|
103
|
+
<Typography variant="caption" className="text-xs text-text-tertiary">
|
|
104
|
+
{`${file.sessions} sessions · last touched `}
|
|
105
|
+
</Typography>
|
|
106
|
+
<DateTime
|
|
107
|
+
value={file.lastTouched}
|
|
108
|
+
format="short"
|
|
109
|
+
fallback="—"
|
|
110
|
+
className="text-xs text-text-tertiary"
|
|
111
|
+
/>
|
|
112
|
+
<Typography variant="caption" className="text-xs text-text-tertiary">
|
|
113
|
+
{' · first '}
|
|
114
|
+
</Typography>
|
|
115
|
+
<DateTime
|
|
116
|
+
value={file.firstTouched}
|
|
117
|
+
format="short"
|
|
118
|
+
fallback="—"
|
|
119
|
+
className="text-xs text-text-tertiary"
|
|
120
|
+
/>
|
|
121
|
+
</View>
|
|
122
|
+
</View>
|
|
123
|
+
|
|
124
|
+
<View className="flex-row gap-2">
|
|
125
|
+
{/* Tile's own fill is the card plane, which is this card; step it one up. */}
|
|
126
|
+
<Tile
|
|
127
|
+
label="Reads"
|
|
128
|
+
value={String(file.reads)}
|
|
129
|
+
valueColor={eventColors.reads}
|
|
130
|
+
className={TILE_PLANE}
|
|
131
|
+
/>
|
|
132
|
+
<Tile
|
|
133
|
+
label="Writes"
|
|
134
|
+
value={String(file.writes)}
|
|
135
|
+
valueColor={eventColors.writes}
|
|
136
|
+
className={TILE_PLANE}
|
|
137
|
+
/>
|
|
138
|
+
<Tile
|
|
139
|
+
label="Edits"
|
|
140
|
+
value={String(file.edits)}
|
|
141
|
+
valueColor={eventColors.edits}
|
|
142
|
+
className={TILE_PLANE}
|
|
143
|
+
/>
|
|
144
|
+
<Tile label="Touches" value={String(file.touches)} className={TILE_PLANE} />
|
|
145
|
+
</View>
|
|
146
|
+
|
|
147
|
+
<CardInset className="gap-2 rounded-lg p-3" testID="growth-well">
|
|
148
|
+
<View className="flex-row items-center justify-between">
|
|
149
|
+
<Eyebrow>Net change over sessions</Eyebrow>
|
|
150
|
+
<Typography
|
|
151
|
+
variant="mono"
|
|
152
|
+
className="text-sm font-bold"
|
|
153
|
+
style={{ color: grew ? GROWTH_COLOR.added : GROWTH_COLOR.removed }}
|
|
154
|
+
>
|
|
155
|
+
{`${formatSignedCompact(file.netGrowth)} ch`}
|
|
156
|
+
</Typography>
|
|
157
|
+
</View>
|
|
158
|
+
<SparkBars
|
|
159
|
+
values={file.timeline}
|
|
160
|
+
height={34}
|
|
161
|
+
label={`Per-session net char change for ${file.path}`}
|
|
162
|
+
/>
|
|
163
|
+
<View className="flex-row gap-4">
|
|
164
|
+
<GrowthStat
|
|
165
|
+
label="Added"
|
|
166
|
+
value={`+${formatCompact(file.charsAdded)}`}
|
|
167
|
+
color={GROWTH_COLOR.added}
|
|
168
|
+
/>
|
|
169
|
+
<GrowthStat
|
|
170
|
+
label="Removed"
|
|
171
|
+
value={`-${formatCompact(file.charsRemoved)}`}
|
|
172
|
+
color={GROWTH_COLOR.removed}
|
|
173
|
+
/>
|
|
174
|
+
<GrowthStat label="Sessions" value={String(file.sessions)} color={GROWTH_COLOR.neutral} />
|
|
175
|
+
</View>
|
|
176
|
+
</CardInset>
|
|
177
|
+
|
|
178
|
+
<View className="gap-0.5">
|
|
179
|
+
<Eyebrow>Changes together with</Eyebrow>
|
|
180
|
+
{file.coChange.length ? (
|
|
181
|
+
file.coChange.map((c) => (
|
|
182
|
+
<DataRow
|
|
183
|
+
key={c.path}
|
|
184
|
+
className="py-1"
|
|
185
|
+
labelClassName="shrink"
|
|
186
|
+
label={<FilePathLabel path={c.path} size="sm" />}
|
|
187
|
+
value={
|
|
188
|
+
<Pill variant="subtle" color="primary" size="xs">
|
|
189
|
+
{`${c.count}×`}
|
|
190
|
+
</Pill>
|
|
191
|
+
}
|
|
192
|
+
/>
|
|
193
|
+
))
|
|
194
|
+
) : (
|
|
195
|
+
<Typography variant="caption" className="text-xs text-text-tertiary">
|
|
196
|
+
no co-changes recorded
|
|
197
|
+
</Typography>
|
|
198
|
+
)}
|
|
199
|
+
</View>
|
|
200
|
+
</Card>
|
|
201
|
+
)
|
|
202
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import {
|
|
3
|
+
FileActivityRow,
|
|
4
|
+
FILE_EVENT_COLOR_CATEGORICAL,
|
|
5
|
+
FILE_EVENT_COLOR_SEMANTIC,
|
|
6
|
+
} from './FileActivityRow'
|
|
7
|
+
import { Surface } from '../../ui/surface'
|
|
8
|
+
import { FILE_HISTORY_FILES } from './file-history-fixture'
|
|
9
|
+
|
|
10
|
+
const meta: Meta<typeof FileActivityRow> = {
|
|
11
|
+
title: 'Custom/ActiveWork/FileActivityRow',
|
|
12
|
+
component: FileActivityRow,
|
|
13
|
+
tags: ['autodocs', 'status:candidate', '!status:review'],
|
|
14
|
+
parameters: {
|
|
15
|
+
docs: {
|
|
16
|
+
description: {
|
|
17
|
+
component:
|
|
18
|
+
'**Row.** One file in the ranked "hottest files" list: path, total touches, the ' +
|
|
19
|
+
'read/write/edit split, and a sparkline of per-session growth. Composes ' +
|
|
20
|
+
'[FilePathLabel](?path=/docs/custom-activework-filepathlabel--docs) and ' +
|
|
21
|
+
'[SparkBars](?path=/docs/custom-charts-sparkbars--docs). ' +
|
|
22
|
+
'Used by [FileHistoryExplorer](?path=/docs/custom-activework-filehistoryexplorer--docs).',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
args: { file: FILE_HISTORY_FILES[0], selected: false },
|
|
27
|
+
// The row is a listbox `option`, which is only valid inside a listbox.
|
|
28
|
+
decorators: [
|
|
29
|
+
(Story) => (
|
|
30
|
+
<Surface level="base" className="min-h-screen p-6" testID="page-surface">
|
|
31
|
+
<div role="listbox" aria-label="Hottest files" style={{ width: 404 }}>
|
|
32
|
+
<Story />
|
|
33
|
+
</div>
|
|
34
|
+
</Surface>
|
|
35
|
+
),
|
|
36
|
+
],
|
|
37
|
+
parameters: { layout: 'fullscreen' },
|
|
38
|
+
}
|
|
39
|
+
export default meta
|
|
40
|
+
type Story = StoryObj<typeof FileActivityRow>
|
|
41
|
+
|
|
42
|
+
export const Default: Story = {}
|
|
43
|
+
|
|
44
|
+
export const Selected: Story = {
|
|
45
|
+
args: { selected: true },
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** A net-shrinking file — negative sparkline bars take `result-degrade`. */
|
|
49
|
+
export const NetShrink: Story = {
|
|
50
|
+
args: { file: FILE_HISTORY_FILES[2] },
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Palette option A (current default) — reads/writes/edits coloured by
|
|
55
|
+
* *importance*: quiet tertiary, brand for the consequential writes, info blue
|
|
56
|
+
* between. Borrows a status token for non-status data.
|
|
57
|
+
*/
|
|
58
|
+
export const PaletteSemantic: Story = {
|
|
59
|
+
args: { eventColors: FILE_EVENT_COLOR_SEMANTIC },
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Palette option B — the same three as *peer categories*, taken in order from the
|
|
64
|
+
* canonical CVD-safe palette (TOKENS.md §2). Systematic and colourblind-safe, but
|
|
65
|
+
* it drops the importance ordering, and its red sits next to `result-degrade` red
|
|
66
|
+
* in the same row.
|
|
67
|
+
*/
|
|
68
|
+
export const PaletteCategorical: Story = {
|
|
69
|
+
args: { eventColors: FILE_EVENT_COLOR_CATEGORICAL },
|
|
70
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react'
|
|
3
|
+
import { axe } from 'jest-axe'
|
|
4
|
+
import { FileActivityRow, type FileActivity } from './FileActivityRow'
|
|
5
|
+
|
|
6
|
+
const file: FileActivity = {
|
|
7
|
+
path: 'src/commands/open.ts',
|
|
8
|
+
reads: 84,
|
|
9
|
+
writes: 12,
|
|
10
|
+
edits: 61,
|
|
11
|
+
touches: 157,
|
|
12
|
+
timeline: [120, 340, -80, 910],
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe('FileActivityRow', () => {
|
|
16
|
+
it('renders the path, touch count, and the read/write/edit split', () => {
|
|
17
|
+
render(<FileActivityRow file={file} />)
|
|
18
|
+
expect(screen.getByText('src/commands/')).toBeInTheDocument()
|
|
19
|
+
expect(screen.getByText('open.ts')).toBeInTheDocument()
|
|
20
|
+
expect(screen.getByText('157')).toBeInTheDocument()
|
|
21
|
+
expect(screen.getByText('84r')).toBeInTheDocument()
|
|
22
|
+
expect(screen.getByText('12w')).toBeInTheDocument()
|
|
23
|
+
expect(screen.getByText('61e')).toBeInTheDocument()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('renders a sparkline of the timeline', () => {
|
|
27
|
+
render(<FileActivityRow file={file} />)
|
|
28
|
+
expect(screen.getAllByTestId(/^spark-bars-bar-/)).toHaveLength(4)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('shows the selection accent only when selected', () => {
|
|
32
|
+
const { rerender } = render(<FileActivityRow file={file} />)
|
|
33
|
+
expect(screen.queryByTestId('file-activity-row-accent')).not.toBeInTheDocument()
|
|
34
|
+
rerender(<FileActivityRow file={file} selected />)
|
|
35
|
+
expect(screen.getByTestId('file-activity-row-accent')).toBeInTheDocument()
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('reports selection state to assistive tech as a listbox option', () => {
|
|
39
|
+
const { rerender } = render(<FileActivityRow file={file} selected />)
|
|
40
|
+
expect(screen.getByRole('option', { selected: true })).toBeInTheDocument()
|
|
41
|
+
rerender(<FileActivityRow file={file} />)
|
|
42
|
+
expect(screen.getByRole('option', { selected: false })).toBeInTheDocument()
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('calls onSelect when pressed', () => {
|
|
46
|
+
const onSelect = vi.fn()
|
|
47
|
+
render(<FileActivityRow file={file} onSelect={onSelect} />)
|
|
48
|
+
fireEvent.click(screen.getByRole('option'))
|
|
49
|
+
expect(onSelect).toHaveBeenCalledOnce()
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('does not throw when pressed without an onSelect handler', () => {
|
|
53
|
+
render(<FileActivityRow file={file} />)
|
|
54
|
+
expect(() => fireEvent.click(screen.getByRole('option'))).not.toThrow()
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('has no a11y violations inside its listbox', async () => {
|
|
58
|
+
// `role="option"` is only valid within a listbox — which is how
|
|
59
|
+
// FileHistoryExplorer renders it, so the a11y check mirrors that context.
|
|
60
|
+
const { container } = render(
|
|
61
|
+
<div role="listbox" aria-label="Hottest files">
|
|
62
|
+
<FileActivityRow file={file} />
|
|
63
|
+
</div>
|
|
64
|
+
)
|
|
65
|
+
expect(await axe(container)).toHaveNoViolations()
|
|
66
|
+
})
|
|
67
|
+
})
|