@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
|
@@ -3,6 +3,7 @@ import { render, screen, fireEvent } from '@testing-library/react'
|
|
|
3
3
|
import { axe } from 'jest-axe'
|
|
4
4
|
import { WeightBadge } from './WeightBadge'
|
|
5
5
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
6
|
+
import { resolveColor } from '../../../theme/resolve-color'
|
|
6
7
|
|
|
7
8
|
// Read from the token, not pinned: these used to be hand-copied hexes and
|
|
8
9
|
// silently desynced when borders became alpha hairlines (TD-07.16).
|
|
@@ -47,18 +48,14 @@ describe('WeightBadge', () => {
|
|
|
47
48
|
describe('reps (rep-max context)', () => {
|
|
48
49
|
it('shows the estimated 1RM label when reps is omitted', () => {
|
|
49
50
|
render(<WeightBadge value={315} unit="lbs" />)
|
|
50
|
-
expect(
|
|
51
|
-
screen.getByLabelText('Estimated one rep max: 315 lbs'),
|
|
52
|
-
).toBeInTheDocument()
|
|
51
|
+
expect(screen.getByLabelText('Estimated one rep max: 315 lbs')).toBeInTheDocument()
|
|
53
52
|
expect(screen.queryByTestId('weight-badge-repmax')).not.toBeInTheDocument()
|
|
54
53
|
})
|
|
55
54
|
|
|
56
55
|
it('shows the rep-max qualifier and label when reps is provided', () => {
|
|
57
56
|
render(<WeightBadge value={275} unit="lbs" reps={5} />)
|
|
58
57
|
expect(screen.getByTestId('weight-badge-repmax')).toHaveTextContent('5RM')
|
|
59
|
-
expect(
|
|
60
|
-
screen.getByLabelText('5 rep max: 275 lbs'),
|
|
61
|
-
).toBeInTheDocument()
|
|
58
|
+
expect(screen.getByLabelText('5 rep max: 275 lbs')).toBeInTheDocument()
|
|
62
59
|
})
|
|
63
60
|
})
|
|
64
61
|
|
|
@@ -127,27 +124,63 @@ describe('WeightBadge', () => {
|
|
|
127
124
|
})
|
|
128
125
|
})
|
|
129
126
|
|
|
127
|
+
// Regression (E3 B3): the value, rep-max and delta colours were read through
|
|
128
|
+
// `getSemanticColors('dark')` / `greyRamp` ON THE RENDER PATH, which pins the badge
|
|
129
|
+
// to dark-mode hex and leaves it unreadable once `.light` is on <html>. Each of these
|
|
130
|
+
// asserts the `var()` reference `resolveColor` returns on web; every one of them
|
|
131
|
+
// fails against the old literal.
|
|
132
|
+
describe('theme-correct colours', () => {
|
|
133
|
+
it('draws a PR value from the brand-primary token, not a dark-mode hex', () => {
|
|
134
|
+
render(<WeightBadge value={315} isPr />)
|
|
135
|
+
expect(screen.getByText(/315 lbs/)).toHaveStyle({ color: resolveColor('brand-primary') })
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
// A load value is not a heading. Typography's h-variants render role="heading",
|
|
139
|
+
// which both misreports the badge and breaks the parity layer's text selector.
|
|
140
|
+
it('renders the value as text, not a heading', () => {
|
|
141
|
+
render(<WeightBadge value={225} />)
|
|
142
|
+
expect(screen.queryByRole('heading')).not.toBeInTheDocument()
|
|
143
|
+
expect(screen.getByText('225 lbs').getAttribute('dir')).toBe('auto')
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('draws a non-PR value from the text-secondary token', () => {
|
|
147
|
+
render(<WeightBadge value={225} />)
|
|
148
|
+
expect(screen.getByText('225 lbs')).toHaveStyle({ color: resolveColor('text-secondary') })
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('tints the rep-max qualifier with the same token as the value', () => {
|
|
152
|
+
render(<WeightBadge value={275} reps={5} isPr />)
|
|
153
|
+
expect(screen.getByTestId('weight-badge-repmax')).toHaveStyle({
|
|
154
|
+
color: resolveColor('brand-primary'),
|
|
155
|
+
})
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it.each([
|
|
159
|
+
[3, 'result-improve'],
|
|
160
|
+
[-2, 'result-degrade'],
|
|
161
|
+
] as const)('draws a %s%% delta from the %s token', (delta, token) => {
|
|
162
|
+
render(<WeightBadge value={225} delta={delta} />)
|
|
163
|
+
expect(screen.getByTestId('weight-badge-delta')).toHaveStyle({
|
|
164
|
+
color: resolveColor(token),
|
|
165
|
+
})
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
|
|
130
169
|
describe('accessibility', () => {
|
|
131
170
|
it('has no accessibility violations', async () => {
|
|
132
|
-
const { container } = render(
|
|
133
|
-
<WeightBadge value={225} isPr onPress={() => {}} />,
|
|
134
|
-
)
|
|
171
|
+
const { container } = render(<WeightBadge value={225} isPr onPress={() => {}} />)
|
|
135
172
|
const results = await axe(container)
|
|
136
173
|
expect(results).toHaveNoViolations()
|
|
137
174
|
})
|
|
138
175
|
|
|
139
176
|
it('has no accessibility violations with delta', async () => {
|
|
140
|
-
const { container } = render(
|
|
141
|
-
<WeightBadge value={225} delta={3} onPress={() => {}} />,
|
|
142
|
-
)
|
|
177
|
+
const { container } = render(<WeightBadge value={225} delta={3} onPress={() => {}} />)
|
|
143
178
|
const results = await axe(container)
|
|
144
179
|
expect(results).toHaveNoViolations()
|
|
145
180
|
})
|
|
146
181
|
|
|
147
182
|
it('has no accessibility violations as static badge', async () => {
|
|
148
|
-
const { container } = render(
|
|
149
|
-
<WeightBadge value={315} unit="kg" isPr delta={-2} />,
|
|
150
|
-
)
|
|
183
|
+
const { container } = render(<WeightBadge value={315} unit="kg" isPr delta={-2} />)
|
|
151
184
|
const results = await axe(container)
|
|
152
185
|
expect(results).toHaveNoViolations()
|
|
153
186
|
})
|
|
@@ -155,15 +188,13 @@ describe('WeightBadge', () => {
|
|
|
155
188
|
it('includes delta in the e1RM accessibility label', () => {
|
|
156
189
|
render(<WeightBadge value={225} delta={5} />)
|
|
157
190
|
expect(
|
|
158
|
-
screen.getByLabelText('Estimated one rep max: 225 lbs, +5% change')
|
|
191
|
+
screen.getByLabelText('Estimated one rep max: 225 lbs, +5% change')
|
|
159
192
|
).toBeInTheDocument()
|
|
160
193
|
})
|
|
161
194
|
|
|
162
195
|
it('includes delta in the rep-max accessibility label', () => {
|
|
163
196
|
render(<WeightBadge value={275} reps={5} delta={-3} />)
|
|
164
|
-
expect(
|
|
165
|
-
screen.getByLabelText('5 rep max: 275 lbs, -3% change'),
|
|
166
|
-
).toBeInTheDocument()
|
|
197
|
+
expect(screen.getByLabelText('5 rep max: 275 lbs, -3% change')).toBeInTheDocument()
|
|
167
198
|
})
|
|
168
199
|
})
|
|
169
200
|
})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
|
-
import {
|
|
2
|
+
import { Typography } from '../Typography'
|
|
3
3
|
import { DumbbellIcon } from './icons'
|
|
4
4
|
import {
|
|
5
5
|
BaseBadge,
|
|
@@ -7,13 +7,11 @@ import {
|
|
|
7
7
|
type BaseBadgeSize,
|
|
8
8
|
type BaseBadgeProps,
|
|
9
9
|
} from './BaseBadge'
|
|
10
|
-
import {
|
|
11
|
-
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
10
|
+
import { resolveColor } from '../../../theme/resolve-color'
|
|
12
11
|
|
|
13
12
|
export type WeightBadgeSize = BaseBadgeSize
|
|
14
13
|
|
|
15
|
-
export interface WeightBadgeProps
|
|
16
|
-
extends Omit<BaseBadgeProps, 'variant' | 'icon' | 'children'> {
|
|
14
|
+
export interface WeightBadgeProps extends Omit<BaseBadgeProps, 'variant' | 'icon' | 'children'> {
|
|
17
15
|
value: number
|
|
18
16
|
unit?: 'lbs' | 'kg'
|
|
19
17
|
/**
|
|
@@ -31,6 +29,9 @@ export interface WeightBadgeProps
|
|
|
31
29
|
className?: string
|
|
32
30
|
}
|
|
33
31
|
|
|
32
|
+
/**
|
|
33
|
+
* @deprecated Use `<Pill tone="neutral">` — removed after AW-127 consumer migration.
|
|
34
|
+
*/
|
|
34
35
|
export function WeightBadge({
|
|
35
36
|
value,
|
|
36
37
|
unit = 'lbs',
|
|
@@ -44,15 +45,14 @@ export function WeightBadge({
|
|
|
44
45
|
...props
|
|
45
46
|
}: WeightBadgeProps) {
|
|
46
47
|
const config = baseBadgeSizeConfig[size]
|
|
47
|
-
const textColor = isPr ?
|
|
48
|
+
const textColor = isPr ? resolveColor('brand-primary') : resolveColor('text-secondary')
|
|
48
49
|
// Dumbbell reads clearly as a weight/strength metric and is unit-agnostic
|
|
49
50
|
// across estimated-1RM and N-rep-max contexts (vs. TrendingUp which implies
|
|
50
51
|
// change/trend rather than a load value).
|
|
51
52
|
const iconColor = textColor
|
|
52
53
|
|
|
53
54
|
const repMaxClause = reps != null ? `${reps} rep max` : 'Estimated one rep max'
|
|
54
|
-
const deltaLabel =
|
|
55
|
-
delta != null ? `, ${delta >= 0 ? '+' : ''}${delta}% change` : ''
|
|
55
|
+
const deltaLabel = delta != null ? `, ${delta >= 0 ? '+' : ''}${delta}% change` : ''
|
|
56
56
|
const fullLabel = `${repMaxClause}: ${value} ${unit}${deltaLabel}`
|
|
57
57
|
|
|
58
58
|
return (
|
|
@@ -70,22 +70,24 @@ export function WeightBadge({
|
|
|
70
70
|
testID="weight-badge"
|
|
71
71
|
{...props}
|
|
72
72
|
>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
73
|
+
{/* `body2` + `font-heading`, NOT `h6`: a load value is not a heading, and the
|
|
74
|
+
heading variants make Typography render `role="heading"`. The size prop still
|
|
75
|
+
drives the step, so the fontSize stays computed (BaseBadge's 9/10/12 config). */}
|
|
76
|
+
<Typography
|
|
77
|
+
variant="body2"
|
|
78
|
+
color="inherit"
|
|
79
|
+
className="font-heading font-semibold leading-[normal]"
|
|
80
|
+
style={{ fontSize: config.fontSize, color: textColor }}
|
|
80
81
|
>
|
|
81
82
|
{isPr ? '✳ ' : ''}
|
|
82
83
|
{value} {unit}
|
|
83
|
-
</
|
|
84
|
+
</Typography>
|
|
84
85
|
{reps != null && (
|
|
85
|
-
<
|
|
86
|
+
<Typography
|
|
87
|
+
variant="body2"
|
|
88
|
+
color="inherit"
|
|
89
|
+
className="font-heading font-semibold leading-[normal]"
|
|
86
90
|
style={{
|
|
87
|
-
fontFamily: '"Space Grotesk", sans-serif',
|
|
88
|
-
fontWeight: '600',
|
|
89
91
|
fontSize: config.fontSize,
|
|
90
92
|
marginLeft: 2,
|
|
91
93
|
color: textColor,
|
|
@@ -94,24 +96,23 @@ export function WeightBadge({
|
|
|
94
96
|
testID="weight-badge-repmax"
|
|
95
97
|
>
|
|
96
98
|
{reps}RM
|
|
97
|
-
</
|
|
99
|
+
</Typography>
|
|
98
100
|
)}
|
|
99
101
|
{delta != null && (
|
|
100
|
-
<
|
|
102
|
+
<Typography
|
|
103
|
+
variant="body2"
|
|
104
|
+
color="inherit"
|
|
105
|
+
className="font-heading leading-[normal]"
|
|
101
106
|
style={{
|
|
102
|
-
fontFamily: '"Space Grotesk", sans-serif',
|
|
103
107
|
fontSize: config.fontSize,
|
|
104
108
|
marginLeft: 4,
|
|
105
|
-
color:
|
|
106
|
-
delta >= 0
|
|
107
|
-
? getSemanticColors('dark')['result-improve']
|
|
108
|
-
: getSemanticColors('dark')['result-degrade'],
|
|
109
|
+
color: resolveColor(delta >= 0 ? 'result-improve' : 'result-degrade'),
|
|
109
110
|
}}
|
|
110
111
|
testID="weight-badge-delta"
|
|
111
112
|
>
|
|
112
113
|
{delta >= 0 ? '+' : ''}
|
|
113
114
|
{delta}%
|
|
114
|
-
</
|
|
115
|
+
</Typography>
|
|
115
116
|
)}
|
|
116
117
|
</BaseBadge>
|
|
117
118
|
)
|
|
@@ -4,8 +4,13 @@ import { WorkoutCard } from './WorkoutCard'
|
|
|
4
4
|
import type { ExerciseCardProps } from './ExerciseCard'
|
|
5
5
|
|
|
6
6
|
const meta: Meta<typeof WorkoutCard> = {
|
|
7
|
-
title: 'Workout/WorkoutCard',
|
|
7
|
+
title: 'Custom/Workout/WorkoutCard',
|
|
8
8
|
component: WorkoutCard,
|
|
9
|
+
parameters: {
|
|
10
|
+
docs: {
|
|
11
|
+
description: { component: 'Composes **Card** · **ExerciseCard** · **MuscleGroupChip**.' },
|
|
12
|
+
},
|
|
13
|
+
},
|
|
9
14
|
tags: ['autodocs'],
|
|
10
15
|
argTypes: {
|
|
11
16
|
status: {
|
|
@@ -3,6 +3,7 @@ import { render, screen, fireEvent } from '@testing-library/react'
|
|
|
3
3
|
import { axe } from 'jest-axe'
|
|
4
4
|
import { WorkoutCard, type WorkoutCardProps } from './WorkoutCard'
|
|
5
5
|
import type { ExerciseCardProps } from './ExerciseCard'
|
|
6
|
+
import { resolveColor } from '../../../theme/resolve-color'
|
|
6
7
|
|
|
7
8
|
const baseProps: WorkoutCardProps = {
|
|
8
9
|
name: 'Upper A',
|
|
@@ -110,6 +111,23 @@ describe('WorkoutCard', () => {
|
|
|
110
111
|
opacity: 0.6,
|
|
111
112
|
})
|
|
112
113
|
})
|
|
114
|
+
|
|
115
|
+
it.each([
|
|
116
|
+
['completed', 'status-success'],
|
|
117
|
+
['today', 'brand-primary'],
|
|
118
|
+
['upcoming', 'hairline-default'],
|
|
119
|
+
] as const)('draws the %s accent stripe from the %s token', (status, token) => {
|
|
120
|
+
render(<WorkoutCard {...baseProps} status={status} />)
|
|
121
|
+
const style = screen.getByTestId('workout-card').getAttribute('style') ?? ''
|
|
122
|
+
expect(style).toContain(`border-left-color: ${resolveColor(token)}`)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it("washes today's card with the brand subtle token", () => {
|
|
126
|
+
render(<WorkoutCard {...baseProps} status="today" />)
|
|
127
|
+
expect(screen.getByTestId('workout-card')).toHaveStyle({
|
|
128
|
+
backgroundColor: resolveColor('brand-primary-subtle'),
|
|
129
|
+
})
|
|
130
|
+
})
|
|
113
131
|
})
|
|
114
132
|
|
|
115
133
|
describe('accessibility', () => {
|
|
@@ -1,25 +1,15 @@
|
|
|
1
1
|
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
|
-
import { View,
|
|
2
|
+
import { View, ScrollView, Pressable, type ViewProps } from 'react-native'
|
|
3
3
|
import { Card } from '../../ui/card'
|
|
4
4
|
import { ExerciseCard, type ExerciseCardProps } from './ExerciseCard'
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from './MuscleGroupChip'
|
|
9
|
-
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
10
|
-
|
|
11
|
-
const HAIRLINE_DEFAULT = getSemanticColors('dark')['hairline-default']
|
|
12
|
-
|
|
13
|
-
const t = getSemanticColors('dark')
|
|
5
|
+
import { MuscleGroupChip, type VolumeStatus } from './MuscleGroupChip'
|
|
6
|
+
import { Typography } from '../Typography'
|
|
7
|
+
import { resolveColor } from '../../../theme/resolve-color'
|
|
14
8
|
|
|
15
9
|
export type WorkoutStatus = 'completed' | 'today' | 'upcoming'
|
|
16
10
|
|
|
17
11
|
/** Spec volume-status vocabulary; mapped to MuscleGroupChip's VolumeStatus internally. */
|
|
18
|
-
export type WorkoutMuscleVolumeStatus =
|
|
19
|
-
| 'under'
|
|
20
|
-
| 'maintenance'
|
|
21
|
-
| 'productive'
|
|
22
|
-
| 'over'
|
|
12
|
+
export type WorkoutMuscleVolumeStatus = 'under' | 'maintenance' | 'productive' | 'over'
|
|
23
13
|
|
|
24
14
|
export interface WorkoutMuscleGroup {
|
|
25
15
|
/** Muscle group identifier (free-form to match plan data). */
|
|
@@ -47,17 +37,14 @@ export interface WorkoutCardProps extends ViewProps {
|
|
|
47
37
|
className?: string
|
|
48
38
|
}
|
|
49
39
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
completed: COLORS.statusSuccess,
|
|
59
|
-
today: COLORS.brandPrimary,
|
|
60
|
-
upcoming: COLORS.borderDefault,
|
|
40
|
+
/** Accent-stripe token per status, resolved per theme at render. */
|
|
41
|
+
const statusAccentToken: Record<
|
|
42
|
+
WorkoutStatus,
|
|
43
|
+
'status-success' | 'brand-primary' | 'hairline-default'
|
|
44
|
+
> = {
|
|
45
|
+
completed: 'status-success',
|
|
46
|
+
today: 'brand-primary',
|
|
47
|
+
upcoming: 'hairline-default',
|
|
61
48
|
}
|
|
62
49
|
|
|
63
50
|
/** Maps the spec's volume-status vocabulary onto MuscleGroupChip's enum. */
|
|
@@ -72,7 +59,7 @@ function formatStats(
|
|
|
72
59
|
totalSets: number,
|
|
73
60
|
totalVolume: number | undefined,
|
|
74
61
|
unit: 'lbs' | 'kg',
|
|
75
|
-
duration: string | undefined
|
|
62
|
+
duration: string | undefined
|
|
76
63
|
): string {
|
|
77
64
|
const parts = [`${totalSets} sets`]
|
|
78
65
|
if (totalVolume != null) parts.push(`${totalVolume} ${unit}`)
|
|
@@ -123,41 +110,36 @@ export function WorkoutCard({
|
|
|
123
110
|
const summary = (
|
|
124
111
|
<View style={{ padding: 14 }} testID="workout-card-body">
|
|
125
112
|
<View className="flex-row items-center" testID="workout-card-header">
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}}
|
|
113
|
+
{/* The card name was 15px/700 Space Grotesk — off the type scale between
|
|
114
|
+
`sm` and `base`. `h6` carries the heading face; `base` is the step up. */}
|
|
115
|
+
<Typography
|
|
116
|
+
variant="h6"
|
|
117
|
+
color="primary"
|
|
118
|
+
className="text-base font-bold leading-[normal]"
|
|
133
119
|
testID="workout-card-name"
|
|
134
120
|
>
|
|
135
121
|
{name}
|
|
136
|
-
</
|
|
122
|
+
</Typography>
|
|
137
123
|
<View className="flex-1" />
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}}
|
|
124
|
+
{/* 11px is off the scale too; `2xs` (10px) is the step below. */}
|
|
125
|
+
<Typography
|
|
126
|
+
variant="caption"
|
|
127
|
+
color="tertiary"
|
|
128
|
+
className="text-2xs leading-[normal]"
|
|
144
129
|
testID="workout-card-date"
|
|
145
130
|
>
|
|
146
131
|
{date}
|
|
147
|
-
</
|
|
132
|
+
</Typography>
|
|
148
133
|
</View>
|
|
149
134
|
|
|
150
|
-
<
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
fontSize: 12,
|
|
155
|
-
fontFamily: 'Inter, sans-serif',
|
|
156
|
-
}}
|
|
135
|
+
<Typography
|
|
136
|
+
variant="caption"
|
|
137
|
+
color="secondary"
|
|
138
|
+
className="mt-1 leading-[normal]"
|
|
157
139
|
testID="workout-card-stats"
|
|
158
140
|
>
|
|
159
141
|
{formatStats(totalSets, totalVolume, unit, duration)}
|
|
160
|
-
</
|
|
142
|
+
</Typography>
|
|
161
143
|
|
|
162
144
|
{muscleGroups.length > 0 && (
|
|
163
145
|
<ScrollView
|
|
@@ -171,11 +153,7 @@ export function WorkoutCard({
|
|
|
171
153
|
<MuscleGroupChip
|
|
172
154
|
key={muscle.group}
|
|
173
155
|
name={muscle.label}
|
|
174
|
-
volumeStatus={
|
|
175
|
-
muscle.volumeStatus
|
|
176
|
-
? volumeStatusMap[muscle.volumeStatus]
|
|
177
|
-
: undefined
|
|
178
|
-
}
|
|
156
|
+
volumeStatus={muscle.volumeStatus ? volumeStatusMap[muscle.volumeStatus] : undefined}
|
|
179
157
|
/>
|
|
180
158
|
))}
|
|
181
159
|
</ScrollView>
|
|
@@ -187,8 +165,8 @@ export function WorkoutCard({
|
|
|
187
165
|
<Card
|
|
188
166
|
variant="accent"
|
|
189
167
|
elevation={1}
|
|
190
|
-
accentColor={
|
|
191
|
-
bgColor={isToday ?
|
|
168
|
+
accentColor={resolveColor(statusAccentToken[status])}
|
|
169
|
+
bgColor={isToday ? resolveColor('brand-primary-subtle') : undefined}
|
|
192
170
|
className={className}
|
|
193
171
|
style={isUpcoming ? { opacity: 0.6 } : undefined}
|
|
194
172
|
testID="workout-card"
|
|
@@ -3,7 +3,7 @@ import { View } from 'react-native'
|
|
|
3
3
|
import { WorkoutPill } from './WorkoutPill'
|
|
4
4
|
|
|
5
5
|
const meta: Meta<typeof WorkoutPill> = {
|
|
6
|
-
title: 'Workout/WorkoutPill',
|
|
6
|
+
title: 'Custom/Workout/WorkoutPill',
|
|
7
7
|
component: WorkoutPill,
|
|
8
8
|
tags: ['autodocs'],
|
|
9
9
|
argTypes: {
|
|
@@ -14,7 +14,10 @@ const meta: Meta<typeof WorkoutPill> = {
|
|
|
14
14
|
description: 'Pill status',
|
|
15
15
|
},
|
|
16
16
|
pulse: { control: 'boolean', description: 'Pulsing animation (independent of status)' },
|
|
17
|
-
highlighted: {
|
|
17
|
+
highlighted: {
|
|
18
|
+
control: 'boolean',
|
|
19
|
+
description: 'Highlighted with thicker border and slight scale',
|
|
20
|
+
},
|
|
18
21
|
},
|
|
19
22
|
}
|
|
20
23
|
|
|
@@ -2,6 +2,7 @@ import { describe, it, expect, vi } from 'vitest'
|
|
|
2
2
|
import { render, screen, fireEvent } from '@testing-library/react'
|
|
3
3
|
import { axe } from 'jest-axe'
|
|
4
4
|
import { WorkoutPill } from './WorkoutPill'
|
|
5
|
+
import { resolveColor } from '../../../theme/resolve-color'
|
|
5
6
|
|
|
6
7
|
describe('WorkoutPill', () => {
|
|
7
8
|
it('renders the workout name', () => {
|
|
@@ -80,6 +81,41 @@ describe('WorkoutPill', () => {
|
|
|
80
81
|
}
|
|
81
82
|
})
|
|
82
83
|
|
|
84
|
+
// The rim is a whole rung above the wash. Asserting both rules out the
|
|
85
|
+
// flattening that collapses a two-step pill onto one `-subtle`.
|
|
86
|
+
describe('wash ladder', () => {
|
|
87
|
+
it.each([
|
|
88
|
+
['completed', 'status-success'],
|
|
89
|
+
['current', 'brand-primary'],
|
|
90
|
+
['missed', 'status-error'],
|
|
91
|
+
] as const)('washes the %s pill with %s -subtle inside a -muted rim', (status, role) => {
|
|
92
|
+
render(<WorkoutPill name="Upper A" status={status} />)
|
|
93
|
+
const pill = screen.getByLabelText(`Upper A workout, ${status}`)
|
|
94
|
+
expect(pill).toHaveStyle({ backgroundColor: resolveColor(`${role}-subtle`) })
|
|
95
|
+
expect(pill.style.borderTopColor).toBe(resolveColor(`${role}-muted`))
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('gives the washless next pill the -strong rim, a rung above current', () => {
|
|
99
|
+
render(<WorkoutPill name="Upper A" status="next" />)
|
|
100
|
+
const pill = screen.getByLabelText('Upper A workout, next')
|
|
101
|
+
// jsdom normalises the `transparent` keyword to its rgba() form.
|
|
102
|
+
expect(pill.style.backgroundColor).toBe('rgba(0, 0, 0, 0)')
|
|
103
|
+
expect(pill.style.borderTopColor).toBe(resolveColor('brand-primary-strong'))
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it.each([
|
|
107
|
+
['completed', 'status-success'],
|
|
108
|
+
['current', 'brand-primary'],
|
|
109
|
+
['upcoming', 'text-tertiary'],
|
|
110
|
+
['missed', 'status-error-dark'],
|
|
111
|
+
] as const)('labels the %s pill from the %s token', (status, token) => {
|
|
112
|
+
render(<WorkoutPill name="Upper A" status={status} />)
|
|
113
|
+
expect(screen.getByTestId('workout-pill-name')).toHaveStyle({
|
|
114
|
+
color: resolveColor(token),
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
|
|
83
119
|
describe('pulse', () => {
|
|
84
120
|
it('pulses by default on current status', () => {
|
|
85
121
|
render(<WorkoutPill name="Upper A" status="current" />)
|
|
@@ -128,16 +164,14 @@ describe('WorkoutPill', () => {
|
|
|
128
164
|
})
|
|
129
165
|
|
|
130
166
|
it('has no accessibility violations as static pill', async () => {
|
|
131
|
-
const { container } = render(
|
|
132
|
-
<WorkoutPill name="Lower B" status="upcoming" />,
|
|
133
|
-
)
|
|
167
|
+
const { container } = render(<WorkoutPill name="Lower B" status="upcoming" />)
|
|
134
168
|
const results = await axe(container)
|
|
135
169
|
expect(results).toHaveNoViolations()
|
|
136
170
|
})
|
|
137
171
|
|
|
138
172
|
it('has no accessibility violations for missed status', async () => {
|
|
139
173
|
const { container } = render(
|
|
140
|
-
<WorkoutPill name="Rest Day" status="missed" onPress={() => {}}
|
|
174
|
+
<WorkoutPill name="Rest Day" status="missed" onPress={() => {}} />
|
|
141
175
|
)
|
|
142
176
|
const results = await axe(container)
|
|
143
177
|
expect(results).toHaveNoViolations()
|
|
@@ -145,9 +179,7 @@ describe('WorkoutPill', () => {
|
|
|
145
179
|
|
|
146
180
|
it('pressable has correct accessibility label', () => {
|
|
147
181
|
render(<WorkoutPill name="Upper A" status="completed" onPress={() => {}} />)
|
|
148
|
-
expect(
|
|
149
|
-
screen.getByLabelText('Upper A workout, completed'),
|
|
150
|
-
).toBeInTheDocument()
|
|
182
|
+
expect(screen.getByLabelText('Upper A workout, completed')).toBeInTheDocument()
|
|
151
183
|
})
|
|
152
184
|
})
|
|
153
185
|
})
|