@pyreweb/fabric 1.2.6

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 (210) hide show
  1. package/README.md +119 -0
  2. package/dist/fabric.cjs.js +18109 -0
  3. package/dist/fabric.css +2180 -0
  4. package/dist/fabric.esm.js +18062 -0
  5. package/dist/fabric.min.js +18112 -0
  6. package/dist/types/components/atoms/FAvatar/FAvatar.test.d.ts +1 -0
  7. package/dist/types/components/atoms/FBadge/FBadge.test.d.ts +1 -0
  8. package/dist/types/components/atoms/FButton/FButton.test.d.ts +1 -0
  9. package/dist/types/components/atoms/FCheckbox/FCheckbox.test.d.ts +1 -0
  10. package/dist/types/components/atoms/FDivider/FDivider.test.d.ts +1 -0
  11. package/dist/types/components/atoms/FIcon/FIcon.test.d.ts +1 -0
  12. package/dist/types/components/atoms/FInput/FInput.test.d.ts +1 -0
  13. package/dist/types/components/atoms/FLoader/FLoader.test.d.ts +1 -0
  14. package/dist/types/components/atoms/FRadio/FRadio.test.d.ts +1 -0
  15. package/dist/types/components/atoms/FTextarea/FTextarea.test.d.ts +1 -0
  16. package/dist/types/components/atoms/FToggle/FToggle.test.d.ts +1 -0
  17. package/dist/types/components/atoms/FTypography/FTypography.test.d.ts +1 -0
  18. package/dist/types/components/atoms/index.d.ts +13 -0
  19. package/dist/types/components/molecules/FAccordionItem/FAccordionItem.test.d.ts +1 -0
  20. package/dist/types/components/molecules/FAlert/FAlert.test.d.ts +1 -0
  21. package/dist/types/components/molecules/FBreadcrumb/FBreadcrumb.test.d.ts +1 -0
  22. package/dist/types/components/molecules/FButtonGroup/FButtonGroup.test.d.ts +1 -0
  23. package/dist/types/components/molecules/FCard/FCard.test.d.ts +1 -0
  24. package/dist/types/components/molecules/FDatePicker/FDatePicker.test.d.ts +1 -0
  25. package/dist/types/components/molecules/FEmptyState/FEmptyState.test.d.ts +1 -0
  26. package/dist/types/components/molecules/FFilePreview/FFilePreview.test.d.ts +1 -0
  27. package/dist/types/components/molecules/FFormField/FFormField.test.d.ts +1 -0
  28. package/dist/types/components/molecules/FListItem/FListItem.test.d.ts +1 -0
  29. package/dist/types/components/molecules/FPagination/FPagination.test.d.ts +1 -0
  30. package/dist/types/components/molecules/FSearchBar/FSearchBar.test.d.ts +1 -0
  31. package/dist/types/components/molecules/FSelect/FSelect.test.d.ts +1 -0
  32. package/dist/types/components/molecules/FStatCard/FStatCard.test.d.ts +1 -0
  33. package/dist/types/components/molecules/FTabs/FTabs.test.d.ts +1 -0
  34. package/dist/types/components/molecules/FToast/FToast.test.d.ts +1 -0
  35. package/dist/types/components/molecules/index.d.ts +18 -0
  36. package/dist/types/components/organisms/FActivityFeed/FActivityFeed.test.d.ts +1 -0
  37. package/dist/types/components/organisms/FDataTable/FDataTable.test.d.ts +1 -0
  38. package/dist/types/components/organisms/FDrawer/FDrawer.test.d.ts +1 -0
  39. package/dist/types/components/organisms/FFileUpload/FFileUpload.test.d.ts +1 -0
  40. package/dist/types/components/organisms/FFilterSidebar/FFilterSidebar.test.d.ts +1 -0
  41. package/dist/types/components/organisms/FForm/FForm.test.d.ts +1 -0
  42. package/dist/types/components/organisms/FModal/FModal.test.d.ts +1 -0
  43. package/dist/types/components/organisms/FNavigationSidebar/FNavigationSidebar.test.d.ts +1 -0
  44. package/dist/types/components/organisms/FOnboardingStepper/FOnboardingStepper.test.d.ts +1 -0
  45. package/dist/types/components/organisms/FOnboardingStepper/FStepperProgress.test.d.ts +1 -0
  46. package/dist/types/components/organisms/FPageHeader/FPageHeader.test.d.ts +1 -0
  47. package/dist/types/components/organisms/FProfileSection/FProfileSection.test.d.ts +1 -0
  48. package/dist/types/components/organisms/FToastProvider/FToastProvider.test.d.ts +1 -0
  49. package/dist/types/components/organisms/FUserMenu/FUserMenu.test.d.ts +1 -0
  50. package/dist/types/components/organisms/index.d.ts +14 -0
  51. package/dist/types/components/utils/FThemeProvider.test.d.ts +1 -0
  52. package/dist/types/components/utils/index.d.ts +2 -0
  53. package/dist/types/components.d.ts +602 -0
  54. package/dist/types/composables/index.d.ts +12 -0
  55. package/dist/types/composables/useDataTableState.d.ts +106 -0
  56. package/dist/types/composables/useDataTableState.test.d.ts +1 -0
  57. package/dist/types/composables/useFormValidation.d.ts +49 -0
  58. package/dist/types/composables/useFormValidation.test.d.ts +1 -0
  59. package/dist/types/composables/useSidebarState.d.ts +65 -0
  60. package/dist/types/composables/useSidebarState.test.d.ts +1 -0
  61. package/dist/types/index.d.ts +19 -0
  62. package/dist/types/types.d.ts +529 -0
  63. package/package.json +100 -0
  64. package/src/components/atoms/FAvatar/FAvatar.stories.js +100 -0
  65. package/src/components/atoms/FAvatar/FAvatar.test.ts +95 -0
  66. package/src/components/atoms/FAvatar/FAvatar.vue +190 -0
  67. package/src/components/atoms/FBadge/FBadge.stories.js +129 -0
  68. package/src/components/atoms/FBadge/FBadge.test.ts +93 -0
  69. package/src/components/atoms/FBadge/FBadge.vue +103 -0
  70. package/src/components/atoms/FButton/FButton.stories.js +122 -0
  71. package/src/components/atoms/FButton/FButton.test.ts +98 -0
  72. package/src/components/atoms/FButton/FButton.vue +147 -0
  73. package/src/components/atoms/FCheckbox/FCheckbox.stories.js +96 -0
  74. package/src/components/atoms/FCheckbox/FCheckbox.test.ts +64 -0
  75. package/src/components/atoms/FCheckbox/FCheckbox.vue +76 -0
  76. package/src/components/atoms/FDivider/FDivider.stories.js +104 -0
  77. package/src/components/atoms/FDivider/FDivider.test.ts +80 -0
  78. package/src/components/atoms/FDivider/FDivider.vue +117 -0
  79. package/src/components/atoms/FIcon/FIcon.stories.js +189 -0
  80. package/src/components/atoms/FIcon/FIcon.test.ts +99 -0
  81. package/src/components/atoms/FIcon/FIcon.vue +192 -0
  82. package/src/components/atoms/FInput/FInput.stories.js +119 -0
  83. package/src/components/atoms/FInput/FInput.test.ts +79 -0
  84. package/src/components/atoms/FInput/FInput.vue +88 -0
  85. package/src/components/atoms/FLoader/FLoader.stories.js +109 -0
  86. package/src/components/atoms/FLoader/FLoader.test.ts +66 -0
  87. package/src/components/atoms/FLoader/FLoader.vue +97 -0
  88. package/src/components/atoms/FRadio/FRadio.stories.js +105 -0
  89. package/src/components/atoms/FRadio/FRadio.test.ts +75 -0
  90. package/src/components/atoms/FRadio/FRadio.vue +119 -0
  91. package/src/components/atoms/FTextarea/FTextarea.stories.js +126 -0
  92. package/src/components/atoms/FTextarea/FTextarea.test.ts +94 -0
  93. package/src/components/atoms/FTextarea/FTextarea.vue +156 -0
  94. package/src/components/atoms/FToggle/FToggle.stories.js +108 -0
  95. package/src/components/atoms/FToggle/FToggle.test.ts +96 -0
  96. package/src/components/atoms/FToggle/FToggle.vue +123 -0
  97. package/src/components/atoms/FTypography/FTypography.stories.js +127 -0
  98. package/src/components/atoms/FTypography/FTypography.test.ts +93 -0
  99. package/src/components/atoms/FTypography/FTypography.vue +78 -0
  100. package/src/components/atoms/index.ts +27 -0
  101. package/src/components/molecules/FAccordionItem/FAccordionItem.stories.js +71 -0
  102. package/src/components/molecules/FAccordionItem/FAccordionItem.test.ts +61 -0
  103. package/src/components/molecules/FAccordionItem/FAccordionItem.vue +105 -0
  104. package/src/components/molecules/FAlert/FAlert.stories.js +87 -0
  105. package/src/components/molecules/FAlert/FAlert.test.ts +59 -0
  106. package/src/components/molecules/FAlert/FAlert.vue +108 -0
  107. package/src/components/molecules/FBreadcrumb/FBreadcrumb.stories.js +90 -0
  108. package/src/components/molecules/FBreadcrumb/FBreadcrumb.test.ts +76 -0
  109. package/src/components/molecules/FBreadcrumb/FBreadcrumb.vue +117 -0
  110. package/src/components/molecules/FButtonGroup/FButtonGroup.stories.js +82 -0
  111. package/src/components/molecules/FButtonGroup/FButtonGroup.test.ts +44 -0
  112. package/src/components/molecules/FButtonGroup/FButtonGroup.vue +31 -0
  113. package/src/components/molecules/FCard/FCard.stories.js +136 -0
  114. package/src/components/molecules/FCard/FCard.test.ts +87 -0
  115. package/src/components/molecules/FCard/FCard.vue +75 -0
  116. package/src/components/molecules/FDatePicker/FDatePicker.stories.js +305 -0
  117. package/src/components/molecules/FDatePicker/FDatePicker.test.ts +282 -0
  118. package/src/components/molecules/FDatePicker/FDatePicker.vue +750 -0
  119. package/src/components/molecules/FEmptyState/FEmptyState.stories.js +98 -0
  120. package/src/components/molecules/FEmptyState/FEmptyState.test.ts +82 -0
  121. package/src/components/molecules/FEmptyState/FEmptyState.vue +89 -0
  122. package/src/components/molecules/FFilePreview/FFilePreview.stories.js +130 -0
  123. package/src/components/molecules/FFilePreview/FFilePreview.test.ts +70 -0
  124. package/src/components/molecules/FFilePreview/FFilePreview.vue +125 -0
  125. package/src/components/molecules/FFormField/FFormField.stories.js +149 -0
  126. package/src/components/molecules/FFormField/FFormField.test.ts +85 -0
  127. package/src/components/molecules/FFormField/FFormField.vue +107 -0
  128. package/src/components/molecules/FListItem/FListItem.stories.js +158 -0
  129. package/src/components/molecules/FListItem/FListItem.test.ts +93 -0
  130. package/src/components/molecules/FListItem/FListItem.vue +113 -0
  131. package/src/components/molecules/FPagination/FPagination.stories.js +132 -0
  132. package/src/components/molecules/FPagination/FPagination.test.ts +79 -0
  133. package/src/components/molecules/FPagination/FPagination.vue +206 -0
  134. package/src/components/molecules/FSearchBar/FSearchBar.stories.js +129 -0
  135. package/src/components/molecules/FSearchBar/FSearchBar.test.ts +81 -0
  136. package/src/components/molecules/FSearchBar/FSearchBar.vue +180 -0
  137. package/src/components/molecules/FSelect/FSelect.stories.js +333 -0
  138. package/src/components/molecules/FSelect/FSelect.test.ts +478 -0
  139. package/src/components/molecules/FSelect/FSelect.vue +551 -0
  140. package/src/components/molecules/FStatCard/FStatCard.stories.js +144 -0
  141. package/src/components/molecules/FStatCard/FStatCard.test.ts +78 -0
  142. package/src/components/molecules/FStatCard/FStatCard.vue +106 -0
  143. package/src/components/molecules/FTabs/FTab.vue +63 -0
  144. package/src/components/molecules/FTabs/FTabs.stories.js +277 -0
  145. package/src/components/molecules/FTabs/FTabs.test.ts +264 -0
  146. package/src/components/molecules/FTabs/FTabs.vue +273 -0
  147. package/src/components/molecules/FToast/FToast.stories.js +150 -0
  148. package/src/components/molecules/FToast/FToast.test.ts +157 -0
  149. package/src/components/molecules/FToast/FToast.vue +283 -0
  150. package/src/components/molecules/index.ts +37 -0
  151. package/src/components/organisms/FActivityFeed/FActivityFeed.stories.js +217 -0
  152. package/src/components/organisms/FActivityFeed/FActivityFeed.test.ts +134 -0
  153. package/src/components/organisms/FActivityFeed/FActivityFeed.vue +589 -0
  154. package/src/components/organisms/FDataTable/FDataTable.stories.js +370 -0
  155. package/src/components/organisms/FDataTable/FDataTable.test.ts +248 -0
  156. package/src/components/organisms/FDataTable/FDataTable.vue +808 -0
  157. package/src/components/organisms/FDrawer/FDrawer.stories.js +296 -0
  158. package/src/components/organisms/FDrawer/FDrawer.test.ts +142 -0
  159. package/src/components/organisms/FDrawer/FDrawer.vue +303 -0
  160. package/src/components/organisms/FFileUpload/FFileUpload.stories.js +162 -0
  161. package/src/components/organisms/FFileUpload/FFileUpload.test.ts +103 -0
  162. package/src/components/organisms/FFileUpload/FFileUpload.vue +616 -0
  163. package/src/components/organisms/FFilterSidebar/FFilterSidebar.stories.js +161 -0
  164. package/src/components/organisms/FFilterSidebar/FFilterSidebar.test.ts +92 -0
  165. package/src/components/organisms/FFilterSidebar/FFilterSidebar.vue +458 -0
  166. package/src/components/organisms/FForm/FForm.stories.js +270 -0
  167. package/src/components/organisms/FForm/FForm.test.ts +63 -0
  168. package/src/components/organisms/FForm/FForm.vue +19 -0
  169. package/src/components/organisms/FModal/FModal.stories.js +227 -0
  170. package/src/components/organisms/FModal/FModal.test.ts +181 -0
  171. package/src/components/organisms/FModal/FModal.vue +319 -0
  172. package/src/components/organisms/FNavigationSidebar/FNavigationSidebar.stories.js +176 -0
  173. package/src/components/organisms/FNavigationSidebar/FNavigationSidebar.test.ts +95 -0
  174. package/src/components/organisms/FNavigationSidebar/FNavigationSidebar.vue +577 -0
  175. package/src/components/organisms/FOnboardingStepper/FOnboardingStepper.stories.js +197 -0
  176. package/src/components/organisms/FOnboardingStepper/FOnboardingStepper.test.ts +114 -0
  177. package/src/components/organisms/FOnboardingStepper/FOnboardingStepper.vue +212 -0
  178. package/src/components/organisms/FOnboardingStepper/FStepperProgress.stories.js +122 -0
  179. package/src/components/organisms/FOnboardingStepper/FStepperProgress.test.ts +130 -0
  180. package/src/components/organisms/FOnboardingStepper/FStepperProgress.vue +146 -0
  181. package/src/components/organisms/FPageHeader/FPageHeader.stories.js +142 -0
  182. package/src/components/organisms/FPageHeader/FPageHeader.test.ts +83 -0
  183. package/src/components/organisms/FPageHeader/FPageHeader.vue +241 -0
  184. package/src/components/organisms/FProfileSection/FProfileSection.stories.js +190 -0
  185. package/src/components/organisms/FProfileSection/FProfileSection.test.ts +85 -0
  186. package/src/components/organisms/FProfileSection/FProfileSection.vue +562 -0
  187. package/src/components/organisms/FToastProvider/FToastProvider.stories.js +290 -0
  188. package/src/components/organisms/FToastProvider/FToastProvider.test.ts +215 -0
  189. package/src/components/organisms/FToastProvider/FToastProvider.vue +214 -0
  190. package/src/components/organisms/FUserMenu/FUserMenu.stories.js +170 -0
  191. package/src/components/organisms/FUserMenu/FUserMenu.test.ts +102 -0
  192. package/src/components/organisms/FUserMenu/FUserMenu.vue +407 -0
  193. package/src/components/organisms/index.ts +29 -0
  194. package/src/components/utils/FThemeProvider.stories.js +236 -0
  195. package/src/components/utils/FThemeProvider.test.ts +244 -0
  196. package/src/components/utils/FThemeProvider.vue +191 -0
  197. package/src/components/utils/index.ts +3 -0
  198. package/src/components.d.ts +602 -0
  199. package/src/composables/README.md +233 -0
  200. package/src/composables/index.ts +25 -0
  201. package/src/composables/useDataTableState.test.ts +378 -0
  202. package/src/composables/useDataTableState.ts +361 -0
  203. package/src/composables/useFormValidation.test.ts +198 -0
  204. package/src/composables/useFormValidation.ts +178 -0
  205. package/src/composables/useSidebarState.test.ts +307 -0
  206. package/src/composables/useSidebarState.ts +201 -0
  207. package/src/env.d.ts +14 -0
  208. package/src/index.ts +167 -0
  209. package/src/styles/tailwind.css +173 -0
  210. package/src/types.ts +740 -0
@@ -0,0 +1,197 @@
1
+ import FOnboardingStepper from './FOnboardingStepper.vue';
2
+ import FFormField from '../../molecules/FFormField/FFormField.vue';
3
+
4
+ export default {
5
+ title: 'Organisms/FOnboardingStepper',
6
+ component: FOnboardingStepper,
7
+ tags: ['autodocs'],
8
+ argTypes: {
9
+ steps: {
10
+ control: 'object',
11
+ description: 'Configuration des étapes'
12
+ },
13
+ value: {
14
+ control: 'number',
15
+ description: 'Étape actuelle'
16
+ },
17
+ showStepNumbers: {
18
+ control: 'boolean',
19
+ description: 'Afficher les numéros des étapes'
20
+ },
21
+ linear: {
22
+ control: 'boolean',
23
+ description: 'Navigation linéaire uniquement'
24
+ }
25
+ }
26
+ };
27
+
28
+ const sampleSteps = [
29
+ {
30
+ title: 'Informations personnelles',
31
+ description: 'Vos informations de base'
32
+ },
33
+ {
34
+ title: 'Préférences',
35
+ description: 'Personnalisez votre expérience'
36
+ },
37
+ {
38
+ title: 'Confirmation',
39
+ description: 'Vérifiez vos informations'
40
+ }
41
+ ];
42
+
43
+ const Template = (args, { argTypes }) => ({
44
+ components: { FOnboardingStepper },
45
+ props: Object.keys(argTypes),
46
+ data() {
47
+ return { currentStep: args.value || 0 };
48
+ },
49
+ template: '<FOnboardingStepper v-bind="$props" v-model="currentStep" />'
50
+ });
51
+
52
+ export const Default = Template.bind({});
53
+ Default.args = {
54
+ steps: sampleSteps
55
+ };
56
+
57
+ export const SecondStep = Template.bind({});
58
+ SecondStep.args = {
59
+ steps: sampleSteps,
60
+ value: 1
61
+ };
62
+
63
+ export const LastStep = Template.bind({});
64
+ LastStep.args = {
65
+ steps: sampleSteps,
66
+ value: 2
67
+ };
68
+
69
+ export const WithStepNumbers = Template.bind({});
70
+ WithStepNumbers.args = {
71
+ steps: sampleSteps,
72
+ showStepNumbers: true
73
+ };
74
+
75
+ export const WithContent = () => ({
76
+ components: { FOnboardingStepper, FFormField },
77
+ data() {
78
+ return {
79
+ currentStep: 0,
80
+ steps: sampleSteps,
81
+ form: {
82
+ name: '',
83
+ email: '',
84
+ theme: 'light',
85
+ notifications: true
86
+ }
87
+ };
88
+ },
89
+ methods: {
90
+ handleComplete() {
91
+ alert('Onboarding terminé !\n' + JSON.stringify(this.form, null, 2));
92
+ }
93
+ },
94
+ template: `
95
+ <FOnboardingStepper
96
+ v-model="currentStep"
97
+ :steps="steps"
98
+ @complete="handleComplete"
99
+ >
100
+ <template #step-0>
101
+ <div class="flex flex-col gap-4">
102
+ <FFormField v-model="form.name" label="Nom complet" required />
103
+ <FFormField v-model="form.email" label="Email" type="email" required />
104
+ </div>
105
+ </template>
106
+ <template #step-1>
107
+ <div class="flex flex-col gap-4">
108
+ <div>
109
+ <label class="block text-sm font-medium mb-2">Thème</label>
110
+ <select v-model="form.theme" class="w-full border rounded p-2">
111
+ <option value="light">Clair</option>
112
+ <option value="dark">Sombre</option>
113
+ <option value="system">Système</option>
114
+ </select>
115
+ </div>
116
+ <div class="flex items-center gap-2">
117
+ <input type="checkbox" v-model="form.notifications" id="notif" />
118
+ <label for="notif" class="text-sm">Recevoir les notifications</label>
119
+ </div>
120
+ </div>
121
+ </template>
122
+ <template #step-2>
123
+ <div class="bg-neutral-50 p-4 rounded-lg">
124
+ <h4 class="font-medium mb-3">Récapitulatif</h4>
125
+ <dl class="space-y-2 text-sm">
126
+ <div class="flex justify-between">
127
+ <dt class="text-neutral-500">Nom:</dt>
128
+ <dd>{{ form.name || '-' }}</dd>
129
+ </div>
130
+ <div class="flex justify-between">
131
+ <dt class="text-neutral-500">Email:</dt>
132
+ <dd>{{ form.email || '-' }}</dd>
133
+ </div>
134
+ <div class="flex justify-between">
135
+ <dt class="text-neutral-500">Thème:</dt>
136
+ <dd>{{ form.theme }}</dd>
137
+ </div>
138
+ <div class="flex justify-between">
139
+ <dt class="text-neutral-500">Notifications:</dt>
140
+ <dd>{{ form.notifications ? 'Oui' : 'Non' }}</dd>
141
+ </div>
142
+ </dl>
143
+ </div>
144
+ </template>
145
+ </FOnboardingStepper>
146
+ `
147
+ });
148
+
149
+ export const ManySteps = Template.bind({});
150
+ ManySteps.args = {
151
+ steps: [
152
+ { title: 'Étape 1', description: 'Première étape' },
153
+ { title: 'Étape 2', description: 'Deuxième étape' },
154
+ { title: 'Étape 3', description: 'Troisième étape' },
155
+ { title: 'Étape 4', description: 'Quatrième étape' },
156
+ { title: 'Étape 5', description: 'Cinquième étape' }
157
+ ],
158
+ showStepNumbers: true
159
+ };
160
+
161
+ export const Interactive = () => ({
162
+ components: { FOnboardingStepper },
163
+ data() {
164
+ return {
165
+ currentStep: 0,
166
+ steps: sampleSteps
167
+ };
168
+ },
169
+ template: `
170
+ <div class="max-w-2xl mx-auto">
171
+ <FOnboardingStepper
172
+ v-model="currentStep"
173
+ :steps="steps"
174
+ showStepNumbers
175
+ >
176
+ <template #step-0>
177
+ <div class="text-center py-8">
178
+ <h3 class="text-xl font-semibold mb-2">Bienvenue !</h3>
179
+ <p class="text-neutral-600">Commençons par configurer votre profil.</p>
180
+ </div>
181
+ </template>
182
+ <template #step-1>
183
+ <div class="text-center py-8">
184
+ <h3 class="text-xl font-semibold mb-2">Personnalisation</h3>
185
+ <p class="text-neutral-600">Ajustez les paramètres selon vos préférences.</p>
186
+ </div>
187
+ </template>
188
+ <template #step-2>
189
+ <div class="text-center py-8">
190
+ <h3 class="text-xl font-semibold mb-2">Prêt !</h3>
191
+ <p class="text-neutral-600">Tout est configuré. Cliquez sur Terminer.</p>
192
+ </div>
193
+ </template>
194
+ </FOnboardingStepper>
195
+ </div>
196
+ `
197
+ });
@@ -0,0 +1,114 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { mount } from '@vue/test-utils';
3
+ import FOnboardingStepper from './FOnboardingStepper.vue';
4
+
5
+ describe('FOnboardingStepper', () => {
6
+ const steps = [
7
+ { title: 'Step 1', description: 'First step' },
8
+ { title: 'Step 2', description: 'Second step' },
9
+ { title: 'Step 3', description: 'Third step' }
10
+ ];
11
+
12
+ it('renders correctly with required props', () => {
13
+ const wrapper = mount(FOnboardingStepper, {
14
+ propsData: { steps }
15
+ });
16
+ expect(wrapper.exists()).toBe(true);
17
+ });
18
+
19
+ it('displays step titles', () => {
20
+ const wrapper = mount(FOnboardingStepper, {
21
+ propsData: { steps }
22
+ });
23
+ expect(wrapper.text()).toContain('Step 1');
24
+ expect(wrapper.text()).toContain('Step 2');
25
+ });
26
+
27
+ it('starts at step 0 by default', () => {
28
+ const wrapper = mount(FOnboardingStepper, {
29
+ propsData: { steps }
30
+ });
31
+ expect(wrapper.vm.currentStep || 0).toBe(0);
32
+ });
33
+
34
+ it('respects value prop for current step', () => {
35
+ const wrapper = mount(FOnboardingStepper, {
36
+ propsData: { steps, value: 1 }
37
+ });
38
+ expect(wrapper.exists()).toBe(true);
39
+ });
40
+
41
+ it('shows navigation buttons', () => {
42
+ const wrapper = mount(FOnboardingStepper, {
43
+ propsData: { steps }
44
+ });
45
+ const buttons = wrapper.findAllComponents({ name: 'FButton' });
46
+ expect(buttons.length).toBeGreaterThan(0);
47
+ });
48
+
49
+ it('disables previous button on first step', () => {
50
+ const wrapper = mount(FOnboardingStepper, {
51
+ propsData: { steps, value: 0 }
52
+ });
53
+ const buttons = wrapper.findAllComponents({ name: 'FButton' });
54
+ const prevButton = buttons.filter(
55
+ (b) =>
56
+ b.text().toLowerCase().includes('précédent') ||
57
+ b.text().toLowerCase().includes('retour')
58
+ )[0];
59
+ if (prevButton) {
60
+ expect(prevButton.props('disabled')).toBe(true);
61
+ }
62
+ });
63
+
64
+ it('shows finish button on last step', () => {
65
+ const wrapper = mount(FOnboardingStepper, {
66
+ propsData: { steps, value: 2 }
67
+ });
68
+ expect(wrapper.text()).toMatch(/terminer|finish|valider/i);
69
+ });
70
+
71
+ it('emits input when step changes', async () => {
72
+ const wrapper = mount(FOnboardingStepper, {
73
+ propsData: { steps, value: 0 }
74
+ });
75
+ const nextButton = wrapper
76
+ .findAllComponents({ name: 'FButton' })
77
+ .filter(
78
+ (b) =>
79
+ b.text().toLowerCase().includes('suivant') ||
80
+ b.text().toLowerCase().includes('next')
81
+ )[0];
82
+ if (nextButton) {
83
+ await nextButton.trigger('click');
84
+ expect(wrapper.emitted('input')).toBeTruthy();
85
+ }
86
+ });
87
+
88
+ it('emits complete on last step', async () => {
89
+ const wrapper = mount(FOnboardingStepper, {
90
+ propsData: { steps, value: 2 }
91
+ });
92
+ const finishButton = wrapper
93
+ .findAllComponents({ name: 'FButton' })
94
+ .filter(
95
+ (b) =>
96
+ b.text().toLowerCase().includes('terminer') ||
97
+ b.text().toLowerCase().includes('valider')
98
+ )[0];
99
+ if (finishButton) {
100
+ await finishButton.trigger('click');
101
+ expect(wrapper.emitted('complete')).toBeTruthy();
102
+ }
103
+ });
104
+
105
+ it('renders step content slot', () => {
106
+ const wrapper = mount(FOnboardingStepper, {
107
+ propsData: { steps },
108
+ scopedSlots: {
109
+ 'step-0': '<p>Custom content</p>'
110
+ }
111
+ });
112
+ expect(wrapper.exists()).toBe(true);
113
+ });
114
+ });
@@ -0,0 +1,212 @@
1
+ <template>
2
+ <div class="f-onboarding-stepper">
3
+ <!-- Step Progress Indicator -->
4
+ <div class="mb-6">
5
+ <f-stepper-progress
6
+ :steps="stepTitles"
7
+ :current-step="currentStepIndex"
8
+ />
9
+ </div>
10
+
11
+ <!-- Step Content -->
12
+ <f-card :bordered="bordered">
13
+ <div class="min-h-[200px]">
14
+ <slot :name="`step-${currentStepIndex}`">
15
+ <slot />
16
+ </slot>
17
+ </div>
18
+
19
+ <!-- Navigation Actions -->
20
+ <template #actions>
21
+ <div class="flex w-full justify-between">
22
+ <div>
23
+ <f-button
24
+ v-if="currentStepIndex > 0"
25
+ variant="outline"
26
+ @click="handlePrevious"
27
+ >
28
+ <template #iconLeft>
29
+ <f-icon name="chevron-left" size="sm" />
30
+ </template>
31
+ {{ previousLabel }}
32
+ </f-button>
33
+ </div>
34
+ <div>
35
+ <f-button
36
+ v-if="!isLastStep"
37
+ variant="primary"
38
+ :disabled="!canProceed"
39
+ @click="handleNext"
40
+ >
41
+ {{ nextLabel }}
42
+ <template #iconRight>
43
+ <f-icon name="chevron-right" size="sm" />
44
+ </template>
45
+ </f-button>
46
+ <f-button
47
+ v-else
48
+ variant="success"
49
+ :disabled="!canProceed"
50
+ @click="handleComplete"
51
+ >
52
+ {{ completeLabel }}
53
+ <template #iconRight>
54
+ <f-icon name="check" size="sm" />
55
+ </template>
56
+ </f-button>
57
+ </div>
58
+ </div>
59
+ </template>
60
+ </f-card>
61
+ </div>
62
+ </template>
63
+
64
+ <script>
65
+ import FCard from '../../molecules/FCard/FCard.vue';
66
+ import FButton from '../../atoms/FButton/FButton.vue';
67
+ import FIcon from '../../atoms/FIcon/FIcon.vue';
68
+ import FStepperProgress from './FStepperProgress.vue';
69
+
70
+ export default {
71
+ name: 'FOnboardingStepper',
72
+ components: {
73
+ FCard,
74
+ FButton,
75
+ FIcon,
76
+ FStepperProgress
77
+ },
78
+ props: {
79
+ /**
80
+ * Array of step objects containing title and optional validation state.
81
+ * Each step: { title: string, valid?: boolean }
82
+ */
83
+ steps: {
84
+ type: Array,
85
+ required: true,
86
+ validator: (value) => {
87
+ return value.every((step) => typeof step.title === 'string');
88
+ }
89
+ },
90
+ /**
91
+ * Current step index (0-based).
92
+ * Use v-model or .sync for two-way binding.
93
+ */
94
+ value: {
95
+ type: Number,
96
+ default: 0
97
+ },
98
+ /**
99
+ * Whether the current step is valid and the user can proceed.
100
+ * When false, the "Next" or "Complete" button is disabled.
101
+ */
102
+ canProceed: {
103
+ type: Boolean,
104
+ default: true
105
+ },
106
+ /**
107
+ * Label for the "Previous" button
108
+ */
109
+ previousLabel: {
110
+ type: String,
111
+ default: 'Précédent'
112
+ },
113
+ /**
114
+ * Label for the "Next" button
115
+ */
116
+ nextLabel: {
117
+ type: String,
118
+ default: 'Suivant'
119
+ },
120
+ /**
121
+ * Label for the "Complete" button (shown on last step)
122
+ */
123
+ completeLabel: {
124
+ type: String,
125
+ default: 'Terminer'
126
+ },
127
+ /**
128
+ * Whether the card has a border
129
+ */
130
+ bordered: {
131
+ type: Boolean,
132
+ default: true
133
+ }
134
+ },
135
+ computed: {
136
+ /**
137
+ * Current step index with v-model support
138
+ */
139
+ currentStepIndex: {
140
+ get() {
141
+ return this.value;
142
+ },
143
+ set(val) {
144
+ this.$emit('input', val);
145
+ }
146
+ },
147
+ /**
148
+ * Extract step titles from the steps array
149
+ */
150
+ stepTitles() {
151
+ return this.steps.map((step) => step.title);
152
+ },
153
+ /**
154
+ * Total number of steps
155
+ */
156
+ totalSteps() {
157
+ return this.steps.length;
158
+ },
159
+ /**
160
+ * Check if current step is the last step
161
+ */
162
+ isLastStep() {
163
+ return this.currentStepIndex === this.totalSteps - 1;
164
+ },
165
+ /**
166
+ * Check if current step is the first step
167
+ */
168
+ isFirstStep() {
169
+ return this.currentStepIndex === 0;
170
+ }
171
+ },
172
+ methods: {
173
+ /**
174
+ * Navigate to the previous step
175
+ */
176
+ handlePrevious() {
177
+ if (this.currentStepIndex > 0) {
178
+ this.currentStepIndex = this.currentStepIndex - 1;
179
+ this.$emit('previous', this.currentStepIndex);
180
+ this.$emit('step-change', this.currentStepIndex);
181
+ }
182
+ },
183
+ /**
184
+ * Navigate to the next step
185
+ */
186
+ handleNext() {
187
+ if (this.canProceed && !this.isLastStep) {
188
+ this.currentStepIndex = this.currentStepIndex + 1;
189
+ this.$emit('next', this.currentStepIndex);
190
+ this.$emit('step-change', this.currentStepIndex);
191
+ }
192
+ },
193
+ /**
194
+ * Complete the stepper workflow
195
+ */
196
+ handleComplete() {
197
+ if (this.canProceed && this.isLastStep) {
198
+ this.$emit('complete');
199
+ }
200
+ },
201
+ /**
202
+ * Programmatically go to a specific step
203
+ */
204
+ goToStep(index) {
205
+ if (index >= 0 && index < this.totalSteps) {
206
+ this.currentStepIndex = index;
207
+ this.$emit('step-change', index);
208
+ }
209
+ }
210
+ }
211
+ };
212
+ </script>
@@ -0,0 +1,122 @@
1
+ import FStepperProgress from './FStepperProgress.vue';
2
+
3
+ export default {
4
+ title: 'Organisms/FStepperProgress',
5
+ component: FStepperProgress,
6
+ tags: ['autodocs'],
7
+ argTypes: {
8
+ steps: {
9
+ control: 'object',
10
+ description: 'Liste des titres des étapes'
11
+ },
12
+ currentStep: {
13
+ control: { type: 'number', min: 0 },
14
+ description: "Index de l'étape actuelle (base 0)"
15
+ }
16
+ }
17
+ };
18
+
19
+ const defaultSteps = ['Informations', 'Préférences', 'Confirmation'];
20
+
21
+ const Template = (args, { argTypes }) => ({
22
+ components: { FStepperProgress },
23
+ props: Object.keys(argTypes),
24
+ template: '<FStepperProgress v-bind="$props" />'
25
+ });
26
+
27
+ export const Default = Template.bind({});
28
+ Default.args = {
29
+ steps: defaultSteps,
30
+ currentStep: 0
31
+ };
32
+
33
+ export const SecondStep = Template.bind({});
34
+ SecondStep.args = {
35
+ steps: defaultSteps,
36
+ currentStep: 1
37
+ };
38
+
39
+ export const LastStep = Template.bind({});
40
+ LastStep.args = {
41
+ steps: defaultSteps,
42
+ currentStep: 2
43
+ };
44
+
45
+ export const AllCompleted = Template.bind({});
46
+ AllCompleted.args = {
47
+ steps: defaultSteps,
48
+ currentStep: 3
49
+ };
50
+
51
+ export const ManySteps = Template.bind({});
52
+ ManySteps.args = {
53
+ steps: ['Étape 1', 'Étape 2', 'Étape 3', 'Étape 4', 'Étape 5'],
54
+ currentStep: 2
55
+ };
56
+
57
+ export const TwoSteps = Template.bind({});
58
+ TwoSteps.args = {
59
+ steps: ['Début', 'Fin'],
60
+ currentStep: 0
61
+ };
62
+
63
+ export const SingleStep = Template.bind({});
64
+ SingleStep.args = {
65
+ steps: ['Unique'],
66
+ currentStep: 0
67
+ };
68
+
69
+ export const Interactive = () => ({
70
+ components: { FStepperProgress },
71
+ data() {
72
+ return {
73
+ currentStep: 0,
74
+ steps: ['Informations personnelles', 'Configuration', 'Validation']
75
+ };
76
+ },
77
+ methods: {
78
+ next() {
79
+ if (this.currentStep < this.steps.length - 1) {
80
+ this.currentStep++;
81
+ }
82
+ },
83
+ prev() {
84
+ if (this.currentStep > 0) {
85
+ this.currentStep--;
86
+ }
87
+ },
88
+ reset() {
89
+ this.currentStep = 0;
90
+ }
91
+ },
92
+ template: `
93
+ <div class="space-y-6">
94
+ <FStepperProgress :steps="steps" :currentStep="currentStep" />
95
+ <div class="flex gap-2 justify-center">
96
+ <button
97
+ @click="prev"
98
+ :disabled="currentStep === 0"
99
+ class="px-4 py-2 border rounded disabled:opacity-50"
100
+ >
101
+ Précédent
102
+ </button>
103
+ <button
104
+ @click="next"
105
+ :disabled="currentStep >= steps.length - 1"
106
+ class="px-4 py-2 bg-primary-600 text-white rounded disabled:opacity-50"
107
+ >
108
+ Suivant
109
+ </button>
110
+ <button
111
+ @click="reset"
112
+ class="px-4 py-2 border rounded"
113
+ >
114
+ Réinitialiser
115
+ </button>
116
+ </div>
117
+ <p class="text-center text-sm text-neutral-500">
118
+ Étape actuelle: {{ currentStep + 1 }} / {{ steps.length }}
119
+ </p>
120
+ </div>
121
+ `
122
+ });