@titan-design/react-ui 0.6.0 → 0.7.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.
Files changed (248) hide show
  1. package/dist/bodymap.js +12 -7
  2. package/dist/bodymap.js.map +1 -1
  3. package/dist/bodymap.mjs +12 -7
  4. package/dist/bodymap.mjs.map +1 -1
  5. package/dist/index.d.mts +855 -124
  6. package/dist/index.d.ts +855 -124
  7. package/dist/index.js +2276 -801
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +2230 -802
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/{pages-MO0JCySL.d.mts → pages-8u_4WbL-.d.mts} +280 -57
  12. package/dist/{pages-D7Jlssvp.d.ts → pages-DaWiBOGa.d.ts} +280 -57
  13. package/dist/pages.d.mts +1 -1
  14. package/dist/pages.d.ts +1 -1
  15. package/dist/pages.js +1255 -798
  16. package/dist/pages.js.map +1 -1
  17. package/dist/pages.mjs +1255 -798
  18. package/dist/pages.mjs.map +1 -1
  19. package/dist/theme/index.d.mts +16 -82
  20. package/dist/theme/index.d.ts +16 -82
  21. package/dist/theme/index.js +13 -115
  22. package/dist/theme/index.js.map +1 -1
  23. package/dist/theme/index.mjs +13 -115
  24. package/dist/theme/index.mjs.map +1 -1
  25. package/dist/theme/tokens-css.js +14 -6
  26. package/dist/theme/tokens-css.js.map +1 -1
  27. package/dist/theme/tokens-css.mjs +14 -6
  28. package/dist/theme/tokens-css.mjs.map +1 -1
  29. package/package.json +1 -1
  30. package/src/arch/Architecture.stories.tsx +870 -0
  31. package/src/arch/annotations.json +48 -0
  32. package/src/arch/arch-graph.json +4301 -0
  33. package/src/components/custom/DateTime/DateTime.stories.tsx +1 -1
  34. package/src/components/custom/EmptyState/EmptyState.stories.tsx +1 -1
  35. package/src/components/custom/Gauge/Gauge.stories.tsx +1 -1
  36. package/src/components/custom/Metric/Metric.stories.tsx +1 -1
  37. package/src/components/custom/Scatter/Scatter.stories.tsx +1 -1
  38. package/src/components/custom/Sidebar/Sidebar.stories.tsx +1 -1
  39. package/src/components/custom/Table/Table.stories.tsx +1 -1
  40. package/src/components/custom/TimerReadout/TimerReadout.stories.tsx +62 -0
  41. package/src/components/custom/TimerReadout/TimerReadout.test.tsx +72 -0
  42. package/src/components/custom/TimerReadout/TimerReadout.tsx +75 -0
  43. package/src/components/custom/TimerReadout/index.ts +1 -0
  44. package/src/components/custom/Treemap/Treemap.stories.tsx +1 -1
  45. package/src/components/custom/Typography/Typography.stories.tsx +1 -1
  46. package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +21 -13
  47. package/src/components/custom/Workout/ActiveWorkoutPage.test.tsx +16 -13
  48. package/src/components/custom/Workout/ActiveWorkoutPage.tsx +9 -15
  49. package/src/components/custom/Workout/BaseBadge.stories.tsx +1 -1
  50. package/src/components/custom/Workout/BodyMap.stories.tsx +1 -1
  51. package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +1 -1
  52. package/src/components/custom/Workout/CapacityBandChart.stories.tsx +1 -1
  53. package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
  54. package/src/components/custom/Workout/ExerciseCard.stories.tsx +51 -66
  55. package/src/components/custom/Workout/ExerciseCard.test.tsx +146 -143
  56. package/src/components/custom/Workout/ExerciseCard.tsx +176 -198
  57. package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +99 -0
  58. package/src/components/custom/Workout/ExerciseCardHeading.test.tsx +72 -0
  59. package/src/components/custom/Workout/ExerciseCardHeading.tsx +43 -0
  60. package/src/components/custom/Workout/ExerciseCardUnified.stories.tsx +151 -0
  61. package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +5 -3
  62. package/src/components/custom/Workout/ExerciseDetailPage.test.tsx +4 -4
  63. package/src/components/custom/Workout/ExerciseDetailPage.tsx +4 -5
  64. package/src/components/custom/Workout/ExerciseHeading.stories.tsx +81 -0
  65. package/src/components/custom/Workout/ExerciseHeading.test.tsx +77 -0
  66. package/src/components/custom/Workout/ExerciseHeading.tsx +85 -0
  67. package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +110 -0
  68. package/src/components/custom/Workout/ExerciseIndicator.test.tsx +93 -0
  69. package/src/components/custom/Workout/ExerciseIndicator.tsx +120 -0
  70. package/src/components/custom/Workout/FatigueMeter.stories.tsx +51 -0
  71. package/src/components/custom/Workout/FatigueMeter.test.tsx +73 -0
  72. package/src/components/custom/Workout/FatigueMeter.tsx +82 -0
  73. package/src/components/custom/Workout/InputBar.stories.tsx +1 -1
  74. package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
  75. package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +153 -0
  76. package/src/components/custom/Workout/LiveAuraFrame.test.tsx +74 -0
  77. package/src/components/custom/Workout/LiveAuraFrame.tsx +105 -0
  78. package/src/components/custom/Workout/MesoCard.stories.tsx +1 -1
  79. package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
  80. package/src/components/custom/Workout/MesoStatusCard.stories.tsx +1 -1
  81. package/src/components/custom/Workout/MetricTiles.stories.tsx +60 -0
  82. package/src/components/custom/Workout/MetricTiles.test.tsx +58 -0
  83. package/src/components/custom/Workout/MetricTiles.tsx +48 -0
  84. package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +1 -1
  85. package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +1 -1
  86. package/src/components/custom/Workout/PrBadge.stories.tsx +1 -1
  87. package/src/components/custom/Workout/PrHistoryModal.stories.tsx +1 -1
  88. package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +4 -16
  89. package/src/components/custom/Workout/ProgramPlanningPage.tsx +8 -6
  90. package/src/components/custom/Workout/README.md +146 -4
  91. package/src/components/custom/Workout/ReadinessCheck.stories.tsx +1 -1
  92. package/src/components/custom/Workout/RestTimer.stories.tsx +1 -1
  93. package/src/components/custom/Workout/RestTimer.tsx +15 -10
  94. package/src/components/custom/Workout/RowInventory.stories.tsx +405 -0
  95. package/src/components/custom/Workout/S3FullRail.stories.tsx +34 -0
  96. package/src/components/custom/Workout/S3SessionPace.stories.tsx +95 -0
  97. package/src/components/custom/Workout/S3SessionRailHeading.stories.tsx +61 -0
  98. package/src/components/custom/Workout/S3SetTypes.stories.tsx +176 -0
  99. package/src/components/custom/Workout/S3WorkoutExpansion.stories.tsx +240 -0
  100. package/src/components/custom/Workout/ScheduleTiles.stories.tsx +40 -0
  101. package/src/components/custom/Workout/ScheduleTiles.test.tsx +34 -0
  102. package/src/components/custom/Workout/ScheduleTiles.tsx +70 -0
  103. package/src/components/custom/Workout/SegmentedBar.stories.tsx +99 -0
  104. package/src/components/custom/Workout/SegmentedBar.test.tsx +77 -0
  105. package/src/components/custom/Workout/SegmentedBar.tsx +172 -0
  106. package/src/components/custom/Workout/SegmentedProgressBar.stories.tsx +50 -0
  107. package/src/components/custom/Workout/SegmentedProgressBar.test.tsx +80 -0
  108. package/src/components/custom/Workout/SegmentedProgressBar.tsx +67 -0
  109. package/src/components/custom/Workout/SessionHeader.stories.tsx +121 -0
  110. package/src/components/custom/Workout/SessionHeader.test.tsx +122 -0
  111. package/src/components/custom/Workout/SessionHeader.tsx +152 -0
  112. package/src/components/custom/Workout/SessionRail.stories.tsx +153 -0
  113. package/src/components/custom/Workout/SessionRail.test.tsx +86 -0
  114. package/src/components/custom/Workout/SessionRail.tsx +126 -0
  115. package/src/components/custom/Workout/SetBar.stories.tsx +124 -0
  116. package/src/components/custom/Workout/SetBar.test.tsx +123 -0
  117. package/src/components/custom/Workout/SetBar.tsx +171 -0
  118. package/src/components/custom/Workout/SetRow.stories.tsx +50 -100
  119. package/src/components/custom/Workout/SetRow.test.tsx +94 -166
  120. package/src/components/custom/Workout/SetRow.tsx +169 -232
  121. package/src/components/custom/Workout/SetStrip.stories.tsx +154 -0
  122. package/src/components/custom/Workout/SetStrip.test.tsx +109 -0
  123. package/src/components/custom/Workout/SetStrip.tsx +71 -0
  124. package/src/components/custom/Workout/SetTableHeader.stories.tsx +66 -0
  125. package/src/components/custom/Workout/SetTableHeader.test.tsx +47 -0
  126. package/src/components/custom/Workout/SetTableHeader.tsx +88 -0
  127. package/src/components/custom/Workout/SetsRepsLoad.stories.tsx +47 -0
  128. package/src/components/custom/Workout/SetsRepsLoad.test.tsx +44 -0
  129. package/src/components/custom/Workout/SetsRepsLoad.tsx +52 -0
  130. package/src/components/custom/Workout/Sparkline.stories.tsx +1 -1
  131. package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
  132. package/src/components/custom/Workout/StatusPill.stories.tsx +59 -0
  133. package/src/components/custom/Workout/StatusPill.test.tsx +63 -0
  134. package/src/components/custom/Workout/StatusPill.tsx +88 -0
  135. package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +1 -1
  136. package/src/components/custom/Workout/SupersetWrapper.stories.tsx +1 -1
  137. package/src/components/custom/Workout/TempoBar.stories.tsx +1 -1
  138. package/src/components/custom/Workout/TempoDisplay.stories.tsx +12 -1
  139. package/src/components/custom/Workout/TempoDisplay.tsx +23 -33
  140. package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +1 -1
  141. package/src/components/custom/Workout/VelocityStrip.modalities.stories.tsx +254 -0
  142. package/src/components/custom/Workout/VelocityStrip.responsive.stories.tsx +145 -0
  143. package/src/components/custom/Workout/VelocityStrip.stories.tsx +129 -66
  144. package/src/components/custom/Workout/VelocityStrip.test.tsx +376 -141
  145. package/src/components/custom/Workout/VelocityStrip.tsx +508 -120
  146. package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +73 -0
  147. package/src/components/custom/Workout/VolumeLandmarkBar.test.tsx +119 -0
  148. package/src/components/custom/Workout/VolumeLandmarkBar.tsx +154 -0
  149. package/src/components/custom/Workout/WeekRow.stories.tsx +1 -1
  150. package/src/components/custom/Workout/WeightBadge.stories.tsx +1 -1
  151. package/src/components/custom/Workout/WorkoutCard.stories.tsx +2 -6
  152. package/src/components/custom/Workout/WorkoutCard.test.tsx +13 -46
  153. package/src/components/custom/Workout/WorkoutPill.stories.tsx +1 -1
  154. package/src/components/custom/Workout/ZoneTrack.stories.tsx +140 -0
  155. package/src/components/custom/Workout/ZoneTrack.test.tsx +200 -0
  156. package/src/components/custom/Workout/ZoneTrack.tsx +315 -0
  157. package/src/components/custom/Workout/index.ts +55 -2
  158. package/src/components/custom/Workout/metricText.stories.tsx +69 -0
  159. package/src/components/custom/Workout/metricText.tsx +34 -0
  160. package/src/components/custom/Workout/paceTone.test.ts +29 -0
  161. package/src/components/custom/Workout/paceTone.ts +27 -0
  162. package/src/components/custom/Workout/setHeadingKit.tsx +177 -0
  163. package/src/components/custom/index.ts +1 -0
  164. package/src/components/custom/stepper/Stepper.stories.tsx +1 -1
  165. package/src/components/icons/icons.stories.tsx +67 -2
  166. package/src/components/icons/icons.test.tsx +32 -2
  167. package/src/components/icons/icons.tsx +108 -0
  168. package/src/components/shell/BrandLockup.stories.tsx +3 -3
  169. package/src/components/shell/DashboardShell.stories.tsx +65 -0
  170. package/src/components/shell/DashboardShell.test.tsx +42 -0
  171. package/src/components/shell/DashboardShell.tsx +74 -0
  172. package/src/components/shell/DeviceIndicator.stories.tsx +2 -2
  173. package/src/components/shell/DeviceMenu.stories.tsx +5 -5
  174. package/src/components/shell/DeviceRow.stories.tsx +3 -3
  175. package/src/components/shell/NavItem.stories.tsx +52 -0
  176. package/src/components/shell/NavItem.test.tsx +35 -0
  177. package/src/components/shell/NavItem.tsx +75 -0
  178. package/src/components/shell/README.md +30 -0
  179. package/src/components/shell/SessionRailSpecimen.stories.tsx +753 -0
  180. package/src/components/shell/SessionStatePill.stories.tsx +3 -3
  181. package/src/components/shell/SessionStatePill.tsx +1 -1
  182. package/src/components/shell/SideNav.stories.tsx +81 -0
  183. package/src/components/shell/SideNav.test.tsx +51 -0
  184. package/src/components/shell/SideNav.tsx +74 -0
  185. package/src/components/shell/TopBar.stories.tsx +5 -5
  186. package/src/components/shell/index.ts +5 -0
  187. package/src/components/ui/alert/Alert.stories.tsx +1 -1
  188. package/src/components/ui/autocomplete/Autocomplete.stories.tsx +1 -1
  189. package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
  190. package/src/components/ui/badge/Badge.stories.tsx +1 -1
  191. package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
  192. package/src/components/ui/button/Button.stories.tsx +1 -1
  193. package/src/components/ui/card/Card.stories.tsx +1 -1
  194. package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
  195. package/src/components/ui/chip/Chip.stories.tsx +1 -1
  196. package/src/components/ui/collapse/Collapse.stories.tsx +1 -1
  197. package/src/components/ui/data-row/DataRow.stories.tsx +1 -1
  198. package/src/components/ui/divider/Divider.stories.tsx +1 -1
  199. package/src/components/ui/drawer/Drawer.stories.tsx +1 -1
  200. package/src/components/ui/form-field/FormField.stories.tsx +1 -1
  201. package/src/components/ui/help-tip/HelpTip.stories.tsx +1 -1
  202. package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
  203. package/src/components/ui/index.ts +1 -0
  204. package/src/components/ui/indicator/Indicator.stories.tsx +2 -2
  205. package/src/components/ui/indicator/Indicator.test.tsx +1 -0
  206. package/src/components/ui/indicator/Indicator.tsx +6 -1
  207. package/src/components/ui/input/Input.stories.tsx +1 -1
  208. package/src/components/ui/link/Link.stories.tsx +1 -1
  209. package/src/components/ui/list-item/ListItem.stories.tsx +1 -1
  210. package/src/components/ui/menu/Menu.stories.tsx +1 -1
  211. package/src/components/ui/modal/Modal.stories.tsx +1 -1
  212. package/src/components/ui/pill/Pill.stories.tsx +1 -1
  213. package/src/components/ui/popover/Popover.stories.tsx +1 -1
  214. package/src/components/ui/progress/Progress.stories.tsx +1 -1
  215. package/src/components/ui/radio/Radio.stories.tsx +1 -1
  216. package/src/components/ui/section/Section.stories.tsx +1 -1
  217. package/src/components/ui/select/Select.stories.tsx +1 -1
  218. package/src/components/ui/skeleton/Skeleton.stories.tsx +1 -1
  219. package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
  220. package/src/components/ui/stack/Stack.stories.tsx +1 -1
  221. package/src/components/ui/surface/Surface.stories.tsx +1 -1
  222. package/src/components/ui/switch/Switch.stories.tsx +1 -1
  223. package/src/components/ui/tabs/Tabs.stories.tsx +1 -1
  224. package/src/components/ui/tile/Tile.stories.tsx +79 -0
  225. package/src/components/ui/tile/Tile.test.tsx +48 -0
  226. package/src/components/ui/tile/Tile.tsx +64 -0
  227. package/src/components/ui/tile/index.ts +2 -0
  228. package/src/components/ui/toast/Toast.stories.tsx +1 -1
  229. package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +1 -1
  230. package/src/components/ui/tooltip/Tooltip.stories.tsx +1 -1
  231. package/src/examples/react-hook-form/ReactHookForm.stories.tsx +1 -1
  232. package/src/hooks/index.ts +2 -0
  233. package/src/hooks/useTimer.test.ts +150 -0
  234. package/src/hooks/useTimer.ts +77 -0
  235. package/src/index.ts +3 -0
  236. package/src/pages.ts +1 -1
  237. package/src/stories/PresetShowcase.stories.tsx +1 -1
  238. package/src/stories/ThemePresets.stories.tsx +1 -1
  239. package/src/theme/ColorSystem.stories.tsx +1 -1
  240. package/src/theme/Colors.stories.tsx +5 -8
  241. package/src/theme/config.ts +4 -0
  242. package/src/theme/elevation.stories.tsx +1 -1
  243. package/src/theme/global.css +5 -1
  244. package/src/theme/shadows.stories.tsx +1 -1
  245. package/src/theme/tokens/primitives.ts +11 -107
  246. package/src/theme/tokens/semantic.ts +8 -0
  247. package/src/theme/workout-tokens.ts +11 -7
  248. package/tailwind.config.js +5 -0
@@ -1,219 +1,147 @@
1
- import { describe, it, expect, vi } from 'vitest'
2
- import { render, screen, fireEvent } from '@testing-library/react'
1
+ import { describe, it, expect } from 'vitest'
2
+ import { render, screen } from '@testing-library/react'
3
3
  import { axe } from 'jest-axe'
4
- import { SetRow } from './SetRow'
4
+ import { View } from 'react-native'
5
+ import { SetRow, type SetRowProps } from './SetRow'
5
6
 
6
- const baseProps = {
7
- mode: 'completed' as const,
7
+ // Literal-hex text treatments (dark theme neutral 100 / 400).
8
+ const T_ACTIVE = '#F3F4F6'
9
+ const T_MUTED = '#9CA3AF'
10
+
11
+ const doneRow: SetRowProps = {
12
+ state: 'done',
8
13
  setNumber: 1,
14
+ unit: 'lbs',
9
15
  reps: 8,
10
16
  weight: 135,
11
- unit: 'lbs' as const,
17
+ rpe: 7.5,
18
+ velocities: [1.1, 0.95, 0.82],
19
+ }
20
+
21
+ const liveRow: SetRowProps = {
22
+ state: 'live',
23
+ setNumber: 2,
24
+ unit: 'lbs',
25
+ target: { reps: 10, weight: 150 },
26
+ reps: 4,
27
+ weight: 150,
28
+ velocities: [0.95, 0.9],
29
+ }
30
+
31
+ const todoRow: SetRowProps = {
32
+ state: 'todo',
33
+ setNumber: 3,
34
+ unit: 'lbs',
35
+ target: { reps: 10, weight: 150 },
12
36
  }
13
37
 
14
38
  describe('SetRow', () => {
15
- describe('completed mode', () => {
16
- it('renders set number, reps, weight', () => {
17
- render(<SetRow {...baseProps} />)
39
+ describe('done state', () => {
40
+ it('renders set number, recorded reps and weight', () => {
41
+ render(<SetRow {...doneRow} />)
18
42
  expect(screen.getByTestId('set-row')).toBeInTheDocument()
19
43
  expect(screen.getByTestId('set-row-set-number')).toHaveTextContent('1')
20
44
  expect(screen.getByTestId('set-row-reps')).toHaveTextContent('8')
21
45
  expect(screen.getByTestId('set-row-weight')).toHaveTextContent('135')
22
46
  })
23
47
 
24
- it('displays previous data when provided', () => {
25
- render(<SetRow {...baseProps} previous={{ reps: 6, weight: 130 }} />)
26
- expect(screen.getByTestId('set-row-previous')).toHaveTextContent(
27
- '6 x 130',
28
- )
29
- })
30
-
31
- it('displays dash when no previous data', () => {
32
- render(<SetRow {...baseProps} />)
33
- expect(screen.getByTestId('set-row-previous')).toHaveTextContent('\u2014')
34
- })
35
-
36
- it('applies reduced opacity for non-next completed sets', () => {
37
- render(<SetRow {...baseProps} />)
38
- const row = screen.getByTestId('set-row')
39
- expect(row).toHaveStyle({ opacity: 0.55 })
48
+ it('renders the RPE value', () => {
49
+ render(<SetRow {...doneRow} rpe={9.5} />)
50
+ expect(screen.getByTestId('set-row-rpe')).toHaveTextContent('9.5')
40
51
  })
41
- })
42
52
 
43
- describe('active mode', () => {
44
- it('renders target values when reps/weight are null', () => {
45
- render(
46
- <SetRow
47
- mode="active"
48
- setNumber={2}
49
- reps={null}
50
- weight={null}
51
- unit="lbs"
52
- targets={{ reps: 10, weight: 150 }}
53
- />,
54
- )
55
- expect(screen.getByTestId('set-row-target-reps')).toHaveTextContent('10')
56
- expect(screen.getByTestId('set-row-target-weight')).toHaveTextContent(
57
- '150',
58
- )
53
+ it('is muted (done + upcoming share one treatment)', () => {
54
+ render(<SetRow {...doneRow} />)
55
+ expect(screen.getByText('8')).toHaveStyle({ color: T_MUTED })
59
56
  })
60
57
 
61
- it('renders dashes when no targets and values are null', () => {
62
- render(
63
- <SetRow mode="active" setNumber={2} reps={null} weight={null} unit="lbs" />,
64
- )
65
- expect(screen.getByTestId('set-row-reps')).toHaveTextContent('\u2014')
66
- expect(screen.getByTestId('set-row-weight')).toHaveTextContent('\u2014')
58
+ it('renders a flat mini strip (done reps, no todo stubs)', () => {
59
+ render(<SetRow {...doneRow} />)
60
+ expect(screen.getByTestId('velocity-strip-mini')).toBeInTheDocument()
61
+ expect(screen.getAllByTestId(/^velocity-bar-\d+$/)).toHaveLength(3)
62
+ expect(screen.queryByTestId('velocity-slot-todo')).not.toBeInTheDocument()
67
63
  })
68
64
  })
69
65
 
70
- describe('isNextSet highlighting', () => {
71
- it('applies highlight styles for active isNextSet', () => {
72
- render(
73
- <SetRow
74
- mode="active"
75
- setNumber={1}
76
- reps={null}
77
- weight={null}
78
- unit="lbs"
79
- isNextSet
80
- />,
81
- )
82
- const row = screen.getByTestId('set-row')
83
- const style = row.getAttribute('style') ?? ''
84
- expect(style).toContain('background-color')
85
- expect(style).toContain('border')
66
+ describe('live state', () => {
67
+ it('shows the TARGET reps and weight (no "reps-done/target")', () => {
68
+ render(<SetRow {...liveRow} />)
69
+ expect(screen.getByTestId('set-row-reps')).toHaveTextContent('10')
70
+ expect(screen.getByTestId('set-row-weight')).toHaveTextContent('150')
71
+ expect(screen.getByTestId('set-row-reps')).not.toHaveTextContent('4/10')
86
72
  })
87
73
 
88
- it('does not apply highlight for non-next active sets', () => {
89
- render(
90
- <SetRow mode="active" setNumber={1} reps={null} weight={null} unit="lbs" />,
91
- )
92
- const row = screen.getByTestId('set-row')
93
- expect(row).not.toHaveStyle({
94
- backgroundColor: 'rgba(255,121,0,0.06)',
95
- })
74
+ it('stands out by brightness (neutral 100)', () => {
75
+ render(<SetRow {...liveRow} />)
76
+ expect(screen.getByText('2')).toHaveStyle({ color: T_ACTIVE })
96
77
  })
97
- })
98
78
 
99
- describe('history mode', () => {
100
- it('renders like completed mode', () => {
101
- render(
102
- <SetRow mode="history" setNumber={3} reps={5} weight={200} unit="kg" />,
103
- )
104
- expect(screen.getByTestId('set-row-reps')).toHaveTextContent('5')
105
- expect(screen.getByTestId('set-row-weight')).toHaveTextContent('200')
79
+ it('renders the compact velocity-height spotlight strip', () => {
80
+ render(<SetRow {...liveRow} />)
81
+ expect(screen.getByTestId('velocity-strip-compact')).toBeInTheDocument()
82
+ // 2 performed reps + grey stubs to the target (10).
83
+ expect(screen.getAllByTestId(/^velocity-bar-\d+$/)).toHaveLength(2)
84
+ expect(screen.getAllByTestId('velocity-slot-todo')).toHaveLength(8)
106
85
  })
107
86
  })
108
87
 
109
- describe('velocity strip', () => {
110
- it('renders velocity strip when velocities provided', () => {
111
- render(
112
- <SetRow {...baseProps} velocities={[1.1, 0.95, 0.82]} />,
113
- )
114
- expect(screen.getByTestId('set-row-velocity-strip')).toBeInTheDocument()
88
+ describe('todo state', () => {
89
+ it('shows the target and a dash for RPE', () => {
90
+ render(<SetRow {...todoRow} />)
91
+ expect(screen.getByTestId('set-row-reps')).toHaveTextContent('10')
92
+ expect(screen.getByTestId('set-row-weight')).toHaveTextContent('150')
93
+ expect(screen.getByTestId('set-row-rpe')).toHaveTextContent('—')
115
94
  })
116
95
 
117
- it('does not render velocity strip when no velocities', () => {
118
- render(<SetRow {...baseProps} />)
119
- expect(
120
- screen.queryByTestId('set-row-velocity-strip'),
121
- ).not.toBeInTheDocument()
96
+ it('is muted and renders an all-grey mini stub strip', () => {
97
+ render(<SetRow {...todoRow} />)
98
+ expect(screen.getByText('3')).toHaveStyle({ color: T_MUTED })
99
+ expect(screen.getByTestId('velocity-strip-mini')).toBeInTheDocument()
100
+ expect(screen.getAllByTestId('velocity-slot-todo')).toHaveLength(10)
101
+ expect(screen.queryByTestId(/^velocity-bar-\d+$/)).not.toBeInTheDocument()
122
102
  })
123
103
 
124
- it('passes onVelocityToggle to velocity strip', () => {
125
- const onToggle = vi.fn()
126
- render(
127
- <SetRow
128
- {...baseProps}
129
- velocities={[1.1, 0.95]}
130
- onVelocityToggle={onToggle}
131
- />,
132
- )
133
- const pressable = screen.getByTestId('velocity-strip-pressable')
134
- fireEvent.click(pressable)
135
- expect(onToggle).toHaveBeenCalledOnce()
104
+ it('honors an explicit planned count for the stubs', () => {
105
+ render(<SetRow {...todoRow} planned={6} />)
106
+ expect(screen.getAllByTestId('velocity-slot-todo')).toHaveLength(6)
136
107
  })
137
108
  })
138
109
 
139
110
  describe('set type badge', () => {
140
- it('renders set type badge instead of set number', () => {
141
- render(<SetRow {...baseProps} setType="W" />)
111
+ it('renders the set-type badge instead of the set number', () => {
112
+ render(<SetRow {...doneRow} setType="W" />)
142
113
  expect(screen.getByTestId('set-row-type-badge')).toHaveTextContent('W')
143
114
  })
144
115
 
145
- it('does not render type badge when setType is not provided', () => {
146
- render(<SetRow {...baseProps} />)
147
- expect(
148
- screen.queryByTestId('set-row-type-badge'),
149
- ).not.toBeInTheDocument()
150
- })
151
- })
152
-
153
- describe('PR badges', () => {
154
- it('renders PR badges when provided', () => {
155
- render(
156
- <SetRow
157
- {...baseProps}
158
- prBadges={[{ type: 'e1rm', label: 'PR e1RM' }]}
159
- />,
160
- )
161
- expect(screen.getByTestId('set-row-pr-badges')).toBeInTheDocument()
162
- })
163
-
164
- it('does not render PR badges container when no badges', () => {
165
- render(<SetRow {...baseProps} />)
166
- expect(
167
- screen.queryByTestId('set-row-pr-badges'),
168
- ).not.toBeInTheDocument()
169
- })
170
- })
171
-
172
- describe('RPE', () => {
173
- it('displays RPE value with color coding', () => {
174
- render(<SetRow {...baseProps} rpe={9.5} />)
175
- expect(screen.getByTestId('set-row-rpe')).toHaveTextContent('9.5')
176
- })
177
-
178
- it('displays dash when RPE is null', () => {
179
- render(<SetRow {...baseProps} rpe={null} />)
180
- expect(screen.getByTestId('set-row-rpe')).toHaveTextContent('\u2014')
116
+ it('does not render the type badge when setType is absent', () => {
117
+ render(<SetRow {...doneRow} />)
118
+ expect(screen.queryByTestId('set-row-type-badge')).not.toBeInTheDocument()
181
119
  })
182
120
  })
183
121
 
184
122
  describe('accessibility', () => {
185
- it('has correct accessibility label', () => {
186
- render(<SetRow {...baseProps} />)
187
- expect(
188
- screen.getByLabelText('Set 1: 8 reps at 135 lbs'),
189
- ).toBeInTheDocument()
123
+ it('labels a done row with its recorded values', () => {
124
+ render(<SetRow {...doneRow} />)
125
+ expect(screen.getByLabelText('Set 1: 8 reps at 135 lbs')).toBeInTheDocument()
190
126
  })
191
127
 
192
- it('has correct accessibility label with null values', () => {
193
- render(
194
- <SetRow mode="active" setNumber={2} reps={null} weight={null} unit="kg" />,
195
- )
196
- expect(
197
- screen.getByLabelText('Set 2: no reps'),
198
- ).toBeInTheDocument()
128
+ it('labels a live row as in progress against its target', () => {
129
+ render(<SetRow {...liveRow} />)
130
+ expect(screen.getByLabelText('Set 2: 10 reps at 150 lbs, in progress')).toBeInTheDocument()
199
131
  })
200
132
 
201
- it('has no accessibility violations', async () => {
202
- const { container } = render(<SetRow {...baseProps} />)
203
- const results = await axe(container)
204
- expect(results).toHaveNoViolations()
133
+ it('labels a todo row as upcoming', () => {
134
+ render(<SetRow {...todoRow} />)
135
+ expect(screen.getByLabelText('Set 3: 10 reps at 150 lbs, upcoming')).toBeInTheDocument()
205
136
  })
206
137
 
207
- it('has no accessibility violations with all features', async () => {
138
+ it('has no accessibility violations across states', async () => {
208
139
  const { container } = render(
209
- <SetRow
210
- {...baseProps}
211
- rpe={8}
212
- velocities={[1.1, 0.95]}
213
- setType="W"
214
- prBadges={[{ type: 'e1rm', label: 'PR e1RM' }]}
215
- previous={{ reps: 6, weight: 130 }}
216
- />,
140
+ <View>
141
+ <SetRow {...doneRow} />
142
+ <SetRow {...liveRow} />
143
+ <SetRow {...todoRow} />
144
+ </View>
217
145
  )
218
146
  const results = await axe(container)
219
147
  expect(results).toHaveNoViolations()