@titan-design/react-ui 0.1.0 → 0.2.5

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 (236) hide show
  1. package/README.md +47 -1
  2. package/dist/{chunk-MGW37MPO.mjs → chunk-UXVRPOME.mjs} +173 -3
  3. package/dist/chunk-UXVRPOME.mjs.map +1 -0
  4. package/dist/index.d.mts +101 -12
  5. package/dist/index.d.ts +101 -12
  6. package/dist/index.js +1267 -468
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +734 -112
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/theme/index.d.mts +86 -1
  11. package/dist/theme/index.d.ts +86 -1
  12. package/dist/theme/index.js +166 -0
  13. package/dist/theme/index.js.map +1 -1
  14. package/dist/theme/index.mjs +1 -1
  15. package/package.json +22 -6
  16. package/src/components/custom/DateTime/DateTime.stories.tsx +180 -0
  17. package/src/components/custom/DateTime/DateTime.test.tsx +152 -0
  18. package/src/components/custom/DateTime/DateTime.tsx +235 -0
  19. package/src/components/custom/DateTime/index.ts +2 -0
  20. package/src/components/custom/EmptyState/EmptyState.stories.tsx +99 -0
  21. package/src/components/custom/EmptyState/EmptyState.test.tsx +106 -0
  22. package/src/components/custom/EmptyState/EmptyState.tsx +68 -0
  23. package/src/components/custom/EmptyState/index.ts +2 -0
  24. package/src/components/custom/Metric/Metric.stories.tsx +133 -0
  25. package/src/components/custom/Metric/Metric.test.tsx +120 -0
  26. package/src/components/custom/Metric/Metric.tsx +92 -0
  27. package/src/components/custom/Metric/index.ts +2 -0
  28. package/src/components/custom/Sidebar/Sidebar.stories.tsx +227 -0
  29. package/src/components/custom/Sidebar/Sidebar.test.tsx +185 -0
  30. package/src/components/custom/Sidebar/Sidebar.tsx +257 -0
  31. package/src/components/custom/Sidebar/index.ts +18 -0
  32. package/src/components/custom/Table/Table.stories.tsx +481 -0
  33. package/src/components/custom/Table/Table.test.tsx +531 -0
  34. package/src/components/custom/Table/Table.tsx +696 -0
  35. package/src/components/custom/Table/index.ts +22 -0
  36. package/src/components/custom/Typography/Typography.stories.tsx +153 -0
  37. package/src/components/custom/Typography/Typography.test.tsx +107 -0
  38. package/src/components/custom/Typography/Typography.tsx +218 -0
  39. package/src/components/custom/Typography/index.ts +19 -0
  40. package/src/components/custom/Workout/RestTimer.stories.tsx +71 -0
  41. package/src/components/custom/Workout/RestTimer.test.tsx +145 -0
  42. package/src/components/custom/Workout/RestTimer.tsx +172 -0
  43. package/src/components/custom/Workout/SupersetWrapper.stories.tsx +144 -0
  44. package/src/components/custom/Workout/SupersetWrapper.test.tsx +130 -0
  45. package/src/components/custom/Workout/SupersetWrapper.tsx +63 -0
  46. package/src/components/custom/Workout/index.ts +2 -0
  47. package/src/components/custom/index.ts +9 -0
  48. package/src/components/custom/stepper/Stepper.stories.tsx +245 -0
  49. package/src/components/custom/stepper/Stepper.test.tsx +230 -0
  50. package/src/components/custom/stepper/Stepper.tsx +250 -0
  51. package/src/components/custom/stepper/index.ts +10 -0
  52. package/src/components/index.ts +5 -0
  53. package/src/components/ui/alert/Alert.stories.tsx +154 -0
  54. package/src/components/ui/alert/Alert.test.tsx +202 -0
  55. package/src/components/ui/alert/Alert.tsx +170 -0
  56. package/src/components/ui/alert/index.ts +8 -0
  57. package/src/components/ui/autocomplete/Autocomplete.stories.tsx +299 -0
  58. package/src/components/ui/autocomplete/Autocomplete.test.tsx +247 -0
  59. package/src/components/ui/autocomplete/Autocomplete.tsx +332 -0
  60. package/src/components/ui/autocomplete/index.ts +2 -0
  61. package/src/components/ui/avatar/Avatar.stories.tsx +105 -0
  62. package/src/components/ui/avatar/Avatar.test.tsx +152 -0
  63. package/src/components/ui/avatar/Avatar.tsx +176 -0
  64. package/src/components/ui/avatar/index.ts +2 -0
  65. package/src/components/ui/badge/Badge.stories.tsx +100 -0
  66. package/src/components/ui/badge/Badge.test.tsx +103 -0
  67. package/src/components/ui/badge/Badge.tsx +119 -0
  68. package/src/components/ui/badge/index.ts +3 -0
  69. package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +70 -0
  70. package/src/components/ui/breadcrumbs/Breadcrumbs.test.tsx +156 -0
  71. package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +127 -0
  72. package/src/components/ui/breadcrumbs/index.ts +2 -0
  73. package/src/components/ui/button/Button.stories.tsx +216 -0
  74. package/src/components/ui/button/Button.test.tsx +132 -0
  75. package/src/components/ui/button/Button.tsx +297 -0
  76. package/src/components/ui/button/index.ts +10 -0
  77. package/src/components/ui/card/Card.stories.tsx +443 -0
  78. package/src/components/ui/card/Card.test.tsx +294 -0
  79. package/src/components/ui/card/Card.tsx +401 -0
  80. package/src/components/ui/card/index.ts +20 -0
  81. package/src/components/ui/checkbox/Checkbox.stories.tsx +126 -0
  82. package/src/components/ui/checkbox/Checkbox.test.tsx +152 -0
  83. package/src/components/ui/checkbox/Checkbox.tsx +163 -0
  84. package/src/components/ui/checkbox/index.ts +2 -0
  85. package/src/components/ui/chip/Chip.stories.tsx +112 -0
  86. package/src/components/ui/chip/Chip.test.tsx +119 -0
  87. package/src/components/ui/chip/Chip.tsx +151 -0
  88. package/src/components/ui/chip/index.ts +2 -0
  89. package/src/components/ui/collapse/Collapse.stories.tsx +313 -0
  90. package/src/components/ui/collapse/Collapse.test.tsx +298 -0
  91. package/src/components/ui/collapse/Collapse.tsx +275 -0
  92. package/src/components/ui/collapse/index.ts +18 -0
  93. package/src/components/ui/data-row/DataRow.stories.tsx +78 -0
  94. package/src/components/ui/data-row/DataRow.test.tsx +78 -0
  95. package/src/components/ui/data-row/DataRow.tsx +29 -0
  96. package/src/components/ui/data-row/index.ts +2 -0
  97. package/src/components/ui/divider/Divider.stories.tsx +71 -0
  98. package/src/components/ui/divider/Divider.test.tsx +55 -0
  99. package/src/components/ui/divider/Divider.tsx +37 -0
  100. package/src/components/ui/divider/index.ts +2 -0
  101. package/src/components/ui/drawer/Drawer.stories.tsx +228 -0
  102. package/src/components/ui/drawer/Drawer.test.tsx +142 -0
  103. package/src/components/ui/drawer/Drawer.tsx +228 -0
  104. package/src/components/ui/drawer/index.ts +9 -0
  105. package/src/components/ui/form-field/FormField.stories.tsx +260 -0
  106. package/src/components/ui/form-field/FormField.test.tsx +280 -0
  107. package/src/components/ui/form-field/FormField.tsx +308 -0
  108. package/src/components/ui/form-field/index.ts +2 -0
  109. package/src/components/ui/help-tip/HelpTip.stories.tsx +250 -0
  110. package/src/components/ui/help-tip/HelpTip.test.tsx +198 -0
  111. package/src/components/ui/help-tip/HelpTip.tsx +229 -0
  112. package/src/components/ui/help-tip/index.ts +2 -0
  113. package/src/components/ui/icon-box/IconBox.stories.tsx +139 -0
  114. package/src/components/ui/icon-box/IconBox.test.tsx +120 -0
  115. package/src/components/ui/icon-box/IconBox.tsx +57 -0
  116. package/src/components/ui/icon-box/index.ts +1 -0
  117. package/src/components/ui/index.ts +38 -0
  118. package/src/components/ui/indicator/Indicator.stories.tsx +74 -0
  119. package/src/components/ui/indicator/Indicator.test.tsx +55 -0
  120. package/src/components/ui/indicator/Indicator.tsx +73 -0
  121. package/src/components/ui/indicator/index.ts +2 -0
  122. package/src/components/ui/input/Input.stories.tsx +192 -0
  123. package/src/components/ui/input/Input.test.tsx +86 -0
  124. package/src/components/ui/input/Input.tsx +273 -0
  125. package/src/components/ui/input/index.ts +2 -0
  126. package/src/components/ui/link/Link.stories.tsx +78 -0
  127. package/src/components/ui/link/Link.test.tsx +94 -0
  128. package/src/components/ui/link/Link.tsx +86 -0
  129. package/src/components/ui/link/index.ts +2 -0
  130. package/src/components/ui/list-item/ListItem.stories.tsx +146 -0
  131. package/src/components/ui/list-item/ListItem.test.tsx +128 -0
  132. package/src/components/ui/list-item/ListItem.tsx +89 -0
  133. package/src/components/ui/list-item/index.ts +14 -0
  134. package/src/components/ui/menu/Menu.stories.tsx +180 -0
  135. package/src/components/ui/menu/Menu.test.tsx +353 -0
  136. package/src/components/ui/menu/Menu.tsx +222 -0
  137. package/src/components/ui/menu/index.ts +2 -0
  138. package/src/components/ui/modal/Modal.stories.tsx +350 -0
  139. package/src/components/ui/modal/Modal.test.tsx +159 -0
  140. package/src/components/ui/modal/Modal.tsx +254 -0
  141. package/src/components/ui/modal/index.ts +19 -0
  142. package/src/components/ui/pill/Pill.stories.tsx +71 -0
  143. package/src/components/ui/pill/Pill.test.tsx +69 -0
  144. package/src/components/ui/pill/Pill.tsx +104 -0
  145. package/src/components/ui/pill/index.ts +2 -0
  146. package/src/components/ui/popover/Popover.stories.tsx +190 -0
  147. package/src/components/ui/popover/Popover.test.tsx +419 -0
  148. package/src/components/ui/popover/Popover.tsx +237 -0
  149. package/src/components/ui/popover/index.ts +2 -0
  150. package/src/components/ui/progress/Progress.stories.tsx +190 -0
  151. package/src/components/ui/progress/Progress.test.tsx +210 -0
  152. package/src/components/ui/progress/Progress.tsx +339 -0
  153. package/src/components/ui/progress/index.ts +9 -0
  154. package/src/components/ui/radio/Radio.stories.tsx +199 -0
  155. package/src/components/ui/radio/Radio.test.tsx +166 -0
  156. package/src/components/ui/radio/Radio.tsx +200 -0
  157. package/src/components/ui/radio/index.ts +2 -0
  158. package/src/components/ui/section/Section.stories.tsx +111 -0
  159. package/src/components/ui/section/Section.test.tsx +121 -0
  160. package/src/components/ui/section/Section.tsx +52 -0
  161. package/src/components/ui/section/index.ts +6 -0
  162. package/src/components/ui/select/Select.stories.tsx +286 -0
  163. package/src/components/ui/select/Select.test.tsx +200 -0
  164. package/src/components/ui/select/Select.tsx +243 -0
  165. package/src/components/ui/select/index.ts +2 -0
  166. package/src/components/ui/skeleton/Skeleton.stories.tsx +153 -0
  167. package/src/components/ui/skeleton/Skeleton.test.tsx +162 -0
  168. package/src/components/ui/skeleton/Skeleton.tsx +229 -0
  169. package/src/components/ui/skeleton/index.ts +16 -0
  170. package/src/components/ui/spinner/Spinner.stories.tsx +67 -0
  171. package/src/components/ui/spinner/Spinner.test.tsx +67 -0
  172. package/src/components/ui/spinner/Spinner.tsx +72 -0
  173. package/src/components/ui/spinner/index.ts +2 -0
  174. package/src/components/ui/stack/Stack.stories.tsx +127 -0
  175. package/src/components/ui/stack/Stack.test.tsx +184 -0
  176. package/src/components/ui/stack/Stack.tsx +76 -0
  177. package/src/components/ui/stack/index.ts +2 -0
  178. package/src/components/ui/surface/Surface.stories.tsx +91 -0
  179. package/src/components/ui/surface/Surface.test.tsx +63 -0
  180. package/src/components/ui/surface/Surface.tsx +51 -0
  181. package/src/components/ui/surface/index.ts +1 -0
  182. package/src/components/ui/switch/Switch.stories.tsx +111 -0
  183. package/src/components/ui/switch/Switch.test.tsx +112 -0
  184. package/src/components/ui/switch/Switch.tsx +103 -0
  185. package/src/components/ui/switch/index.ts +2 -0
  186. package/src/components/ui/tabs/Tabs.stories.tsx +213 -0
  187. package/src/components/ui/tabs/Tabs.test.tsx +192 -0
  188. package/src/components/ui/tabs/Tabs.tsx +253 -0
  189. package/src/components/ui/tabs/index.ts +16 -0
  190. package/src/components/ui/toast/Toast.stories.tsx +255 -0
  191. package/src/components/ui/toast/Toast.test.tsx +207 -0
  192. package/src/components/ui/toast/Toast.tsx +327 -0
  193. package/src/components/ui/toast/index.ts +9 -0
  194. package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +415 -0
  195. package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +224 -0
  196. package/src/components/ui/toolbar-button/ToolbarButton.tsx +307 -0
  197. package/src/components/ui/toolbar-button/index.ts +7 -0
  198. package/src/components/ui/tooltip/Tooltip.stories.tsx +126 -0
  199. package/src/components/ui/tooltip/Tooltip.test.tsx +262 -0
  200. package/src/components/ui/tooltip/Tooltip.tsx +234 -0
  201. package/src/components/ui/tooltip/index.ts +2 -0
  202. package/src/index.ts +10 -0
  203. package/src/stories/PresetShowcase.stories.tsx +462 -0
  204. package/src/stories/ThemePresets.stories.tsx +85 -0
  205. package/src/test/setup.ts +19 -0
  206. package/src/theme/Colors.stories.tsx +258 -0
  207. package/src/theme/config.ts +193 -0
  208. package/src/theme/elevation.stories.tsx +346 -0
  209. package/src/theme/elevation.test.ts +27 -0
  210. package/src/theme/elevation.ts +567 -0
  211. package/src/theme/global.css +129 -1
  212. package/src/theme/index.ts +40 -0
  213. package/src/theme/presets/apply.ts +97 -0
  214. package/src/theme/presets/audiobook.ts +93 -0
  215. package/src/theme/presets/default.ts +6 -0
  216. package/src/theme/presets/index.ts +4 -0
  217. package/src/theme/presets/presets.test.ts +51 -0
  218. package/src/theme/presets/types.ts +76 -0
  219. package/src/theme/shadows.stories.tsx +523 -0
  220. package/src/theme/shadows.ts +439 -0
  221. package/src/theme/tokens/index.ts +2 -0
  222. package/src/theme/tokens/primitives.ts +354 -0
  223. package/src/theme/tokens/semantic.ts +381 -0
  224. package/src/types/jest-axe.d.ts +16 -0
  225. package/src/types/nativewind.d.ts +54 -0
  226. package/src/utils/avatar-color.test.ts +30 -0
  227. package/src/utils/avatar-color.ts +20 -0
  228. package/src/utils/cn.ts +14 -0
  229. package/src/utils/colors.ts +140 -0
  230. package/src/utils/form.ts +188 -0
  231. package/src/utils/index.ts +29 -0
  232. package/src/utils/useTheme.ts +72 -0
  233. package/src/web-jsx/jsx-dev-runtime.ts +28 -0
  234. package/src/web-jsx/jsx-runtime.ts +41 -0
  235. package/tailwind.config.js +53 -3
  236. package/dist/chunk-MGW37MPO.mjs.map +0 -1
@@ -0,0 +1,245 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { View, Text } from 'react-native'
3
+ import { Stepper, Step, StepIndicator, StepLabel, StepContent } from './Stepper'
4
+
5
+ const meta: Meta<typeof Stepper> = {
6
+ title: 'Custom/Stepper',
7
+ component: Stepper,
8
+ tags: ['autodocs'],
9
+ argTypes: {
10
+ activeStep: {
11
+ control: { type: 'number', min: 0, max: 4 },
12
+ description: 'Current active step (0-indexed)',
13
+ },
14
+ orientation: {
15
+ control: 'select',
16
+ options: ['horizontal', 'vertical'],
17
+ description: 'Stepper orientation',
18
+ },
19
+ },
20
+ }
21
+
22
+ export default meta
23
+ type Story = StoryObj<typeof Stepper>
24
+
25
+ export const Default: Story = {
26
+ args: {
27
+ activeStep: 1,
28
+ orientation: 'horizontal',
29
+ },
30
+ render: (args) => (
31
+ <Stepper {...args}>
32
+ <Step>
33
+ <StepIndicator />
34
+ <StepLabel>Account</StepLabel>
35
+ </Step>
36
+ <Step>
37
+ <StepIndicator />
38
+ <StepLabel>Profile</StepLabel>
39
+ </Step>
40
+ <Step>
41
+ <StepIndicator />
42
+ <StepLabel>Review</StepLabel>
43
+ </Step>
44
+ </Stepper>
45
+ ),
46
+ }
47
+
48
+ export const Horizontal: Story = {
49
+ render: () => (
50
+ <View style={{ gap: 32 }}>
51
+ <View>
52
+ <Text className="text-text-secondary text-xs mb-4">Step 1 of 4</Text>
53
+ <Stepper activeStep={0} orientation="horizontal">
54
+ <Step>
55
+ <StepIndicator />
56
+ <StepLabel>Details</StepLabel>
57
+ </Step>
58
+ <Step>
59
+ <StepIndicator />
60
+ <StepLabel>Address</StepLabel>
61
+ </Step>
62
+ <Step>
63
+ <StepIndicator />
64
+ <StepLabel>Payment</StepLabel>
65
+ </Step>
66
+ <Step>
67
+ <StepIndicator />
68
+ <StepLabel>Confirm</StepLabel>
69
+ </Step>
70
+ </Stepper>
71
+ </View>
72
+ <View>
73
+ <Text className="text-text-secondary text-xs mb-4">Step 3 of 4</Text>
74
+ <Stepper activeStep={2} orientation="horizontal">
75
+ <Step>
76
+ <StepIndicator />
77
+ <StepLabel>Details</StepLabel>
78
+ </Step>
79
+ <Step>
80
+ <StepIndicator />
81
+ <StepLabel>Address</StepLabel>
82
+ </Step>
83
+ <Step>
84
+ <StepIndicator />
85
+ <StepLabel>Payment</StepLabel>
86
+ </Step>
87
+ <Step>
88
+ <StepIndicator />
89
+ <StepLabel>Confirm</StepLabel>
90
+ </Step>
91
+ </Stepper>
92
+ </View>
93
+ </View>
94
+ ),
95
+ }
96
+
97
+ export const Vertical: Story = {
98
+ render: () => (
99
+ <Stepper activeStep={1} orientation="vertical">
100
+ <Step>
101
+ <StepIndicator />
102
+ <StepLabel>Create Account</StepLabel>
103
+ </Step>
104
+ <Step>
105
+ <StepIndicator />
106
+ <StepLabel>Set Up Profile</StepLabel>
107
+ </Step>
108
+ <Step>
109
+ <StepIndicator />
110
+ <StepLabel>Configure Settings</StepLabel>
111
+ </Step>
112
+ <Step>
113
+ <StepIndicator />
114
+ <StepLabel>Get Started</StepLabel>
115
+ </Step>
116
+ </Stepper>
117
+ ),
118
+ }
119
+
120
+ export const VerticalWithContent: Story = {
121
+ render: () => (
122
+ <Stepper activeStep={1} orientation="vertical">
123
+ <Step>
124
+ <StepIndicator />
125
+ <StepLabel>Create Account</StepLabel>
126
+ <StepContent>
127
+ <Text className="text-text-secondary text-sm">
128
+ Account creation is complete.
129
+ </Text>
130
+ </StepContent>
131
+ </Step>
132
+ <Step>
133
+ <StepIndicator />
134
+ <StepLabel>Set Up Profile</StepLabel>
135
+ <StepContent>
136
+ <View style={{ gap: 8 }}>
137
+ <Text className="text-text-secondary text-sm">
138
+ Fill in your profile details to continue.
139
+ </Text>
140
+ <View className="bg-surface-inset border border-border-default rounded-md px-3 py-2">
141
+ <Text className="text-text-secondary text-sm">Display Name</Text>
142
+ </View>
143
+ </View>
144
+ </StepContent>
145
+ </Step>
146
+ <Step>
147
+ <StepIndicator />
148
+ <StepLabel>Configure Settings</StepLabel>
149
+ <StepContent>
150
+ <Text className="text-text-secondary text-sm">
151
+ Adjust your preferences.
152
+ </Text>
153
+ </StepContent>
154
+ </Step>
155
+ </Stepper>
156
+ ),
157
+ }
158
+
159
+ export const StepStates: Story = {
160
+ render: () => (
161
+ <View style={{ gap: 24 }}>
162
+ <View>
163
+ <Text className="text-text-secondary text-xs mb-4">All steps upcoming</Text>
164
+ <Stepper activeStep={0} orientation="horizontal">
165
+ <Step>
166
+ <StepIndicator />
167
+ <StepLabel>Step 1</StepLabel>
168
+ </Step>
169
+ <Step>
170
+ <StepIndicator />
171
+ <StepLabel>Step 2</StepLabel>
172
+ </Step>
173
+ <Step>
174
+ <StepIndicator />
175
+ <StepLabel>Step 3</StepLabel>
176
+ </Step>
177
+ </Stepper>
178
+ </View>
179
+ <View>
180
+ <Text className="text-text-secondary text-xs mb-4">All steps completed</Text>
181
+ <Stepper activeStep={3} orientation="horizontal">
182
+ <Step>
183
+ <StepIndicator />
184
+ <StepLabel>Step 1</StepLabel>
185
+ </Step>
186
+ <Step>
187
+ <StepIndicator />
188
+ <StepLabel>Step 2</StepLabel>
189
+ </Step>
190
+ <Step>
191
+ <StepIndicator />
192
+ <StepLabel>Step 3</StepLabel>
193
+ </Step>
194
+ </Stepper>
195
+ </View>
196
+ </View>
197
+ ),
198
+ }
199
+
200
+ export const WithErrorState: Story = {
201
+ render: () => (
202
+ <Stepper activeStep={2} orientation="horizontal">
203
+ <Step>
204
+ <StepIndicator />
205
+ <StepLabel>Account</StepLabel>
206
+ </Step>
207
+ <Step status="error">
208
+ <StepIndicator />
209
+ <StepLabel>Verification</StepLabel>
210
+ </Step>
211
+ <Step>
212
+ <StepIndicator />
213
+ <StepLabel>Complete</StepLabel>
214
+ </Step>
215
+ </Stepper>
216
+ ),
217
+ }
218
+
219
+ export const ActiveStepProgression: Story = {
220
+ render: () => (
221
+ <View style={{ gap: 24 }}>
222
+ {[0, 1, 2, 3].map((activeStep) => (
223
+ <View key={activeStep}>
224
+ <Text className="text-text-secondary text-xs mb-2">
225
+ Active step: {activeStep}
226
+ </Text>
227
+ <Stepper activeStep={activeStep} orientation="horizontal">
228
+ <Step>
229
+ <StepIndicator />
230
+ <StepLabel>Account</StepLabel>
231
+ </Step>
232
+ <Step>
233
+ <StepIndicator />
234
+ <StepLabel>Profile</StepLabel>
235
+ </Step>
236
+ <Step>
237
+ <StepIndicator />
238
+ <StepLabel>Review</StepLabel>
239
+ </Step>
240
+ </Stepper>
241
+ </View>
242
+ ))}
243
+ </View>
244
+ ),
245
+ }
@@ -0,0 +1,230 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { render, screen } from '@testing-library/react'
3
+ import { axe } from 'jest-axe'
4
+ import {
5
+ Stepper,
6
+ Step,
7
+ StepIndicator,
8
+ StepLabel,
9
+ StepContent,
10
+ } from './Stepper'
11
+
12
+ function renderStepper(activeStep = 1) {
13
+ return render(
14
+ <Stepper activeStep={activeStep}>
15
+ <Step>
16
+ <StepIndicator />
17
+ <StepLabel>Account</StepLabel>
18
+ </Step>
19
+ <Step>
20
+ <StepIndicator />
21
+ <StepLabel>Profile</StepLabel>
22
+ </Step>
23
+ <Step>
24
+ <StepIndicator />
25
+ <StepLabel>Review</StepLabel>
26
+ </Step>
27
+ </Stepper>
28
+ )
29
+ }
30
+
31
+ describe('Stepper', () => {
32
+ it('renders all steps', () => {
33
+ renderStepper(0)
34
+ expect(screen.getByText('Account')).toBeInTheDocument()
35
+ expect(screen.getByText('Profile')).toBeInTheDocument()
36
+ expect(screen.getByText('Review')).toBeInTheDocument()
37
+ })
38
+
39
+ it('renders step numbers', () => {
40
+ renderStepper(0)
41
+ expect(screen.getByText('1')).toBeInTheDocument()
42
+ expect(screen.getByText('2')).toBeInTheDocument()
43
+ expect(screen.getByText('3')).toBeInTheDocument()
44
+ })
45
+
46
+ describe('active step', () => {
47
+ it('shows first step as active when activeStep is 0', () => {
48
+ renderStepper(0)
49
+ expect(screen.getByText('Account')).toBeInTheDocument()
50
+ expect(screen.getByText('1')).toBeInTheDocument()
51
+ })
52
+
53
+ it('shows checkmark for completed steps', () => {
54
+ renderStepper(2)
55
+ // Steps 0 and 1 should be completed, showing checkmarks
56
+ const checkmarks = screen.getAllByText('\u2713')
57
+ expect(checkmarks.length).toBe(2)
58
+ })
59
+
60
+ it('shows number for upcoming steps', () => {
61
+ renderStepper(0)
62
+ expect(screen.getByText('2')).toBeInTheDocument()
63
+ expect(screen.getByText('3')).toBeInTheDocument()
64
+ })
65
+ })
66
+
67
+ describe('orientation', () => {
68
+ it('renders horizontally by default', () => {
69
+ renderStepper(1)
70
+ expect(screen.getByText('Account')).toBeInTheDocument()
71
+ })
72
+
73
+ it('renders vertically', () => {
74
+ render(
75
+ <Stepper activeStep={1} orientation="vertical">
76
+ <Step>
77
+ <StepIndicator />
78
+ <StepLabel>Step 1</StepLabel>
79
+ </Step>
80
+ <Step>
81
+ <StepIndicator />
82
+ <StepLabel>Step 2</StepLabel>
83
+ </Step>
84
+ </Stepper>
85
+ )
86
+ expect(screen.getByText('Step 1')).toBeInTheDocument()
87
+ expect(screen.getByText('Step 2')).toBeInTheDocument()
88
+ })
89
+ })
90
+
91
+ describe('Step status override', () => {
92
+ it('allows explicit error status on a step', () => {
93
+ render(
94
+ <Stepper activeStep={1}>
95
+ <Step status="error">
96
+ <StepIndicator />
97
+ <StepLabel>Failed Step</StepLabel>
98
+ </Step>
99
+ <Step>
100
+ <StepIndicator />
101
+ <StepLabel>Next Step</StepLabel>
102
+ </Step>
103
+ </Stepper>
104
+ )
105
+ expect(screen.getByText('Failed Step')).toBeInTheDocument()
106
+ })
107
+
108
+ it('allows explicit completed status override', () => {
109
+ render(
110
+ <Stepper activeStep={0}>
111
+ <Step status="completed">
112
+ <StepIndicator />
113
+ <StepLabel>Done Step</StepLabel>
114
+ </Step>
115
+ <Step>
116
+ <StepIndicator />
117
+ <StepLabel>Current</StepLabel>
118
+ </Step>
119
+ </Stepper>
120
+ )
121
+ // The overridden step should show checkmark despite being at index 0 with activeStep 0
122
+ expect(screen.getByText('\u2713')).toBeInTheDocument()
123
+ })
124
+ })
125
+
126
+ describe('StepIndicator', () => {
127
+ it('renders custom icon', () => {
128
+ render(
129
+ <Stepper activeStep={0}>
130
+ <Step>
131
+ <StepIndicator icon={<span data-testid="custom-icon">★</span>} />
132
+ <StepLabel>Custom</StepLabel>
133
+ </Step>
134
+ </Stepper>
135
+ )
136
+ expect(screen.getByTestId('custom-icon')).toBeInTheDocument()
137
+ })
138
+
139
+ it('renders step number for upcoming steps', () => {
140
+ render(
141
+ <Stepper activeStep={0}>
142
+ <Step>
143
+ <StepIndicator />
144
+ <StepLabel>First</StepLabel>
145
+ </Step>
146
+ <Step>
147
+ <StepIndicator />
148
+ <StepLabel>Second</StepLabel>
149
+ </Step>
150
+ </Stepper>
151
+ )
152
+ expect(screen.getByText('2')).toBeInTheDocument()
153
+ })
154
+ })
155
+
156
+ describe('StepContent', () => {
157
+ it('renders content only for active step', () => {
158
+ render(
159
+ <Stepper activeStep={1} orientation="vertical">
160
+ <Step>
161
+ <StepIndicator />
162
+ <StepLabel>Step 1</StepLabel>
163
+ <StepContent>Step 1 content</StepContent>
164
+ </Step>
165
+ <Step>
166
+ <StepIndicator />
167
+ <StepLabel>Step 2</StepLabel>
168
+ <StepContent>Step 2 content</StepContent>
169
+ </Step>
170
+ </Stepper>
171
+ )
172
+ expect(screen.queryByText('Step 1 content')).not.toBeInTheDocument()
173
+ expect(screen.getByText('Step 2 content')).toBeInTheDocument()
174
+ })
175
+
176
+ it('hides content for non-active steps', () => {
177
+ render(
178
+ <Stepper activeStep={0} orientation="vertical">
179
+ <Step>
180
+ <StepIndicator />
181
+ <StepLabel>Step 1</StepLabel>
182
+ <StepContent>Active content</StepContent>
183
+ </Step>
184
+ <Step>
185
+ <StepIndicator />
186
+ <StepLabel>Step 2</StepLabel>
187
+ <StepContent>Hidden content</StepContent>
188
+ </Step>
189
+ </Stepper>
190
+ )
191
+ expect(screen.getByText('Active content')).toBeInTheDocument()
192
+ expect(screen.queryByText('Hidden content')).not.toBeInTheDocument()
193
+ })
194
+ })
195
+
196
+ describe('connectors', () => {
197
+ it('renders connectors between steps', () => {
198
+ renderStepper(1)
199
+ // Connectors exist between steps; just verify all labels render (connectors are visual)
200
+ expect(screen.getByText('Account')).toBeInTheDocument()
201
+ expect(screen.getByText('Profile')).toBeInTheDocument()
202
+ expect(screen.getByText('Review')).toBeInTheDocument()
203
+ })
204
+ })
205
+
206
+ describe('accessibility', () => {
207
+ it('has no accessibility violations', async () => {
208
+ const { container } = renderStepper(1)
209
+ const results = await axe(container)
210
+ expect(results).toHaveNoViolations()
211
+ })
212
+
213
+ it('has no accessibility violations in vertical orientation', async () => {
214
+ const { container } = render(
215
+ <Stepper activeStep={0} orientation="vertical">
216
+ <Step>
217
+ <StepIndicator />
218
+ <StepLabel>Step 1</StepLabel>
219
+ </Step>
220
+ <Step>
221
+ <StepIndicator />
222
+ <StepLabel>Step 2</StepLabel>
223
+ </Step>
224
+ </Stepper>
225
+ )
226
+ const results = await axe(container)
227
+ expect(results).toHaveNoViolations()
228
+ })
229
+ })
230
+ })