@soyfri/shared-library 2.0.0-beta.1 → 2.0.0-beta.10

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 (212) hide show
  1. package/build.js +38 -75
  2. package/dist/README.md +243 -0
  3. package/dist/components/Drawer/Drawer.cjs +14 -17
  4. package/dist/components/Drawer/Drawer.cjs.map +1 -1
  5. package/dist/components/Drawer/Drawer.d.ts +8 -1
  6. package/dist/components/Drawer/Drawer.js +14 -17
  7. package/dist/components/Drawer/Drawer.js.map +1 -1
  8. package/dist/components/Input/Input.definitions.d.ts +1 -0
  9. package/dist/components/RadioGroup/RadioGroup.cjs +202 -0
  10. package/dist/components/RadioGroup/RadioGroup.cjs.map +1 -0
  11. package/dist/components/RadioGroup/RadioGroup.d.ts +53 -0
  12. package/dist/components/RadioGroup/RadioGroup.definitions.d.ts +6 -0
  13. package/dist/components/RadioGroup/RadioGroup.js +202 -0
  14. package/dist/components/RadioGroup/RadioGroup.js.map +1 -0
  15. package/dist/components/RadioGroup/RadioGroup.sx.d.ts +20 -0
  16. package/dist/components/RadioGroup/RadioGroup.types.d.ts +1 -0
  17. package/dist/components/RadioGroup/index.d.ts +2 -0
  18. package/dist/components/RadioGroup.d.ts +6 -0
  19. package/dist/components/Stepper/Stepper.cjs +136 -23
  20. package/dist/components/Stepper/Stepper.cjs.map +1 -1
  21. package/dist/components/Stepper/Stepper.js +137 -24
  22. package/dist/components/Stepper/Stepper.js.map +1 -1
  23. package/dist/components/Switch/Switch.cjs +181 -0
  24. package/dist/components/Switch/Switch.cjs.map +1 -0
  25. package/dist/components/Switch/Switch.d.ts +43 -0
  26. package/dist/components/Switch/Switch.definitions.d.ts +7 -0
  27. package/dist/components/Switch/Switch.js +181 -0
  28. package/dist/components/Switch/Switch.js.map +1 -0
  29. package/dist/components/Switch/Switch.sx.d.ts +22 -0
  30. package/dist/components/Switch/Switch.types.d.ts +1 -0
  31. package/dist/components/Switch/index.d.ts +2 -0
  32. package/dist/components/Switch.d.ts +6 -0
  33. package/dist/index.cjs +24 -0
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.js +7 -1
  36. package/dist/mui.d.ts +1 -0
  37. package/dist/package.json +207 -0
  38. package/dist/theme/componentStyles.d.ts +1 -1
  39. package/package.json +1 -1
  40. package/src/components/Drawer/Drawer.stories.tsx +168 -0
  41. package/src/components/Drawer/Drawer.tsx +26 -18
  42. package/src/components/Input/Input.definitions.ts +24 -0
  43. package/src/components/Input/Input.stories.tsx +29 -0
  44. package/src/components/RadioGroup/RadioGroup.definitions.ts +177 -0
  45. package/src/components/RadioGroup/RadioGroup.stories.tsx +231 -0
  46. package/src/components/RadioGroup/RadioGroup.sx.ts +75 -0
  47. package/src/components/RadioGroup/RadioGroup.tsx +196 -0
  48. package/src/components/RadioGroup/RadioGroup.types.ts +10 -0
  49. package/src/components/RadioGroup/index.ts +9 -0
  50. package/src/components/Stepper/Stepper.stories.tsx +72 -0
  51. package/src/components/Stepper/Stepper.tsx +139 -4
  52. package/src/components/Switch/Switch.definitions.ts +134 -0
  53. package/src/components/Switch/Switch.stories.tsx +213 -0
  54. package/src/components/Switch/Switch.sx.ts +81 -0
  55. package/src/components/Switch/Switch.tsx +172 -0
  56. package/src/components/Switch/Switch.types.ts +10 -0
  57. package/src/components/Switch/index.ts +9 -0
  58. package/src/mui.ts +10 -0
  59. package/src/theme/componentStyles.ts +3 -1
  60. package/storybook-static/addon-visual-tests-assets/visual-test-illustration.mp4 +0 -0
  61. package/storybook-static/assets/AccountCircle-BDZFsbTw.js +1 -0
  62. package/storybook-static/assets/ActionMenu-EynP8yU1.js +19 -0
  63. package/storybook-static/assets/ActionMenu.stories-DqSqRGix.js +185 -0
  64. package/storybook-static/assets/Alert-3zvTPc0p.js +1 -0
  65. package/storybook-static/assets/AppBar.stories-DcX3M5th.js +172 -0
  66. package/storybook-static/assets/Autocomplete.stories-CXJm8FOT.js +788 -0
  67. package/storybook-static/assets/Avatar-NbFfkZws.js +1 -0
  68. package/storybook-static/assets/Avatar.stories-CwOYCzqU.js +390 -0
  69. package/storybook-static/assets/Box-BnhEcfFm.js +1 -0
  70. package/storybook-static/assets/Button-D9h7OggD.js +1 -0
  71. package/storybook-static/assets/Button-DBpqmVB_.js +1 -0
  72. package/storybook-static/assets/Button.stories-F20dmnjq.js +320 -0
  73. package/storybook-static/assets/ButtonBase-qyaMEhe4.js +74 -0
  74. package/storybook-static/assets/Card.stories-B3NpAhO0.js +154 -0
  75. package/storybook-static/assets/CheckCircleOutline-CEj5mDsl.js +1 -0
  76. package/storybook-static/assets/Chip-C3vKPpzR.js +1 -0
  77. package/storybook-static/assets/Chip.stories-sxcfHVo9.js +333 -0
  78. package/storybook-static/assets/CircularProgress-DC7ZNWwl.js +28 -0
  79. package/storybook-static/assets/Clear-4kYcKvT3.js +1 -0
  80. package/storybook-static/assets/ClipBoard-DvLBdNHe.js +1 -0
  81. package/storybook-static/assets/ClipBoard.stories-BGUo47r6.js +108 -0
  82. package/storybook-static/assets/Close-CgHeRgmh.js +1 -0
  83. package/storybook-static/assets/Close-Cy8nELYU.js +1 -0
  84. package/storybook-static/assets/Color-AVL7NMMY-BJKvwERm.js +1 -0
  85. package/storybook-static/assets/ContentCopy-BfLTDb10.js +1 -0
  86. package/storybook-static/assets/DatePicker-Clkpr-Ku.js +1 -0
  87. package/storybook-static/assets/DatePicker.stories-EaUCMkh3.js +444 -0
  88. package/storybook-static/assets/DateRangePicker.stories-BMlkj-8K.js +390 -0
  89. package/storybook-static/assets/DateTimePicker.stories-B6gdzKq5.js +555 -0
  90. package/storybook-static/assets/DefaultPropsProvider-BGoQxtDa.js +16 -0
  91. package/storybook-static/assets/Delete-D2SMMmIA.js +1 -0
  92. package/storybook-static/assets/DialogContent-BeCDKgax.js +1 -0
  93. package/storybook-static/assets/Divider-BbCj9wT4.js +1 -0
  94. package/storybook-static/assets/DocsRenderer-PQXLIZUC-BebLK5Y_.js +1243 -0
  95. package/storybook-static/assets/Drawer-DcFwy73r.js +1 -0
  96. package/storybook-static/assets/Drawer.stories-C5AZkJBk.js +173 -0
  97. package/storybook-static/assets/EmptyTable-B-RKtgVs.png +0 -0
  98. package/storybook-static/assets/ErrorOutline-D9gM7ART.js +1 -0
  99. package/storybook-static/assets/Fade-Ll96CvH8.js +1 -0
  100. package/storybook-static/assets/Flyout.stories-Cf7z6MNw.js +163 -0
  101. package/storybook-static/assets/Gallery.stories-DdpWVTF6.js +127 -0
  102. package/storybook-static/assets/Grow-8y4FglGK.js +1 -0
  103. package/storybook-static/assets/Home-BRvJEp2L.js +1 -0
  104. package/storybook-static/assets/Icon.stories-D0mUiW_t.js +78 -0
  105. package/storybook-static/assets/IconButton-9OYSTH58.js +1 -0
  106. package/storybook-static/assets/Input-CjX0t4h-.js +1 -0
  107. package/storybook-static/assets/Input.stories-BRxekliy.js +650 -0
  108. package/storybook-static/assets/InputGroup.stories-DH6gUfmn.js +306 -0
  109. package/storybook-static/assets/KeyboardArrowRight-WO_attK2.js +1 -0
  110. package/storybook-static/assets/KeyboardArrowUp-DsyVef-i.js +1 -0
  111. package/storybook-static/assets/ListItem-D3O0103N.js +1 -0
  112. package/storybook-static/assets/ListItemIcon-hca6xN79.js +1 -0
  113. package/storybook-static/assets/ListItemText-BFLAwLdl.js +1 -0
  114. package/storybook-static/assets/Logout-gj-P3AfU.js +1 -0
  115. package/storybook-static/assets/Menu-ClzfjLc3.js +1 -0
  116. package/storybook-static/assets/MenuButton.stories-B-W_QVDt.js +162 -0
  117. package/storybook-static/assets/MenuItem-iU6tAqJI.js +1 -0
  118. package/storybook-static/assets/Modal-3okp9H2i.js +1 -0
  119. package/storybook-static/assets/Modal.stories-DIWzm4qR.js +468 -0
  120. package/storybook-static/assets/MoreVert-BoIVG4gh.js +1 -0
  121. package/storybook-static/assets/Notifications-DY_A-Sho.js +1 -0
  122. package/storybook-static/assets/PageLoader.stories-DmtO1mlm.js +158 -0
  123. package/storybook-static/assets/Paper-SwQBhqI7.js +1 -0
  124. package/storybook-static/assets/Person-CkQl-mpq.js +1 -0
  125. package/storybook-static/assets/PickersModalDialog-Tjnr_cu5.js +10 -0
  126. package/storybook-static/assets/PickersToolbarButton-Tt185-si.js +1 -0
  127. package/storybook-static/assets/Popper-CnCTYXxy.js +1 -0
  128. package/storybook-static/assets/Portal-Cj8XF9Lf.js +1 -0
  129. package/storybook-static/assets/ScrollTopButton.stories-BflQCwNP.js +90 -0
  130. package/storybook-static/assets/Select-CjcuMAY0.js +4 -0
  131. package/storybook-static/assets/Select-DJh2biEb.js +3 -0
  132. package/storybook-static/assets/Select.stories-DU1Gb3I2.js +1103 -0
  133. package/storybook-static/assets/Settings-BLKc1CnO.js +1 -0
  134. package/storybook-static/assets/Snackbar-BtVeKTw6.js +1 -0
  135. package/storybook-static/assets/Stack-D01OUIXi.js +1 -0
  136. package/storybook-static/assets/Stat.stories-Bn9-iuPT.js +60 -0
  137. package/storybook-static/assets/StatusMessage.stories-hnfX8FeU.js +73 -0
  138. package/storybook-static/assets/Stepper-BtKB5ykn.js +2 -0
  139. package/storybook-static/assets/Stepper.stories-CTEZbgPc.js +165 -0
  140. package/storybook-static/assets/Table.stories-CTn2Ktmn.js +1260 -0
  141. package/storybook-static/assets/TableContainer-CzLNaEU-.js +1 -0
  142. package/storybook-static/assets/TableRow-CS88-1HF.js +2 -0
  143. package/storybook-static/assets/Tabs-DLpDOu_n.js +1 -0
  144. package/storybook-static/assets/Tabs.stories-BFVuFy_5.js +159 -0
  145. package/storybook-static/assets/TextField-22T-xHBm.js +1 -0
  146. package/storybook-static/assets/Timeline.stories-DJU_U2Hv.js +97 -0
  147. package/storybook-static/assets/Tooltip-DbnHUxNj.js +1 -0
  148. package/storybook-static/assets/Tooltip.stories-B7tA3dnV.js +66 -0
  149. package/storybook-static/assets/Typography-BgntX2Ep.js +1 -0
  150. package/storybook-static/assets/Wizard.stories-CVrJLK_D.js +23 -0
  151. package/storybook-static/assets/createSimplePaletteValueFilter-bm0fmN_7.js +1 -0
  152. package/storybook-static/assets/createSvgIcon-D_Gca4vA.js +1 -0
  153. package/storybook-static/assets/debounce-Be36O1Ab.js +1 -0
  154. package/storybook-static/assets/emotion-react.browser.esm--g-C9cX9.js +8 -0
  155. package/storybook-static/assets/extendSxProp-CEpa30hT.js +1 -0
  156. package/storybook-static/assets/formField.sx-DMCmZIAa.js +1 -0
  157. package/storybook-static/assets/getReactElementRef-BQ3ANZdy.js +1 -0
  158. package/storybook-static/assets/iframe-BAJnc_4n.js +1079 -0
  159. package/storybook-static/assets/index-B1tlhOpe.js +240 -0
  160. package/storybook-static/assets/index-BF3FAXTk.js +9 -0
  161. package/storybook-static/assets/index-CIeucmOB.js +2 -0
  162. package/storybook-static/assets/index-CY7j4a7o.js +1 -0
  163. package/storybook-static/assets/index-CxkKctw5.js +1 -0
  164. package/storybook-static/assets/isFocusVisible-B8k4qzLc.js +1 -0
  165. package/storybook-static/assets/isMuiElement-CTZSFcY5.js +1 -0
  166. package/storybook-static/assets/jsx-runtime-D_zvdyIk.js +9 -0
  167. package/storybook-static/assets/listItemTextClasses-CC_rwJam.js +1 -0
  168. package/storybook-static/assets/mergeSlotProps-B0UBKBMe.js +1 -0
  169. package/storybook-static/assets/ownerDocument-DW-IO8s5.js +1 -0
  170. package/storybook-static/assets/ownerWindow-HkKU3E4x.js +1 -0
  171. package/storybook-static/assets/preload-helper-PPVm8Dsz.js +1 -0
  172. package/storybook-static/assets/react-18-BUJ64QCV.js +25 -0
  173. package/storybook-static/assets/resolvePreset-CN2aOJJr.js +1 -0
  174. package/storybook-static/assets/useControlled-DsVh1a5j.js +1 -0
  175. package/storybook-static/assets/useForkRef-0ANIrxcF.js +1 -0
  176. package/storybook-static/assets/useId-b4fZxjOL.js +1 -0
  177. package/storybook-static/assets/useMobilePicker-DK-c8xbD.js +1 -0
  178. package/storybook-static/assets/usePreviousProps-WR0rG4aR.js +1 -0
  179. package/storybook-static/assets/useSlot-b6pXgp5_.js +1 -0
  180. package/storybook-static/assets/useSlotProps-C0uMfuBt.js +1 -0
  181. package/storybook-static/assets/useTheme-BmOJK7ra.js +1 -0
  182. package/storybook-static/assets/useThemeProps-DYtxXiUU.js +1 -0
  183. package/storybook-static/assets/useThemeProps-U4yXiZ_5.js +1 -0
  184. package/storybook-static/assets/useTimeout-DNjRaOWc.js +1 -0
  185. package/storybook-static/assets/visuallyHidden-Dan1xhjv.js +1 -0
  186. package/storybook-static/favicon-wrapper.svg +46 -0
  187. package/storybook-static/favicon.svg +1 -0
  188. package/storybook-static/iframe.html +686 -0
  189. package/storybook-static/index.html +160 -0
  190. package/storybook-static/index.json +1 -0
  191. package/storybook-static/nunito-sans-bold-italic.woff2 +0 -0
  192. package/storybook-static/nunito-sans-bold.woff2 +0 -0
  193. package/storybook-static/nunito-sans-italic.woff2 +0 -0
  194. package/storybook-static/nunito-sans-regular.woff2 +0 -0
  195. package/storybook-static/project.json +1 -0
  196. package/storybook-static/sb-addons/chromatic-com-storybook-2/manager-bundle.js +356 -0
  197. package/storybook-static/sb-addons/chromatic-com-storybook-2/manager-bundle.js.LEGAL.txt +40 -0
  198. package/storybook-static/sb-addons/docs-4/manager-bundle.js +151 -0
  199. package/storybook-static/sb-addons/onboarding-1/manager-bundle.js +127 -0
  200. package/storybook-static/sb-addons/storybook-core-server-presets-0/common-manager-bundle.js +971 -0
  201. package/storybook-static/sb-addons/vitest-3/manager-bundle.js +3 -0
  202. package/storybook-static/sb-common-assets/favicon-wrapper.svg +46 -0
  203. package/storybook-static/sb-common-assets/favicon.svg +1 -0
  204. package/storybook-static/sb-common-assets/nunito-sans-bold-italic.woff2 +0 -0
  205. package/storybook-static/sb-common-assets/nunito-sans-bold.woff2 +0 -0
  206. package/storybook-static/sb-common-assets/nunito-sans-italic.woff2 +0 -0
  207. package/storybook-static/sb-common-assets/nunito-sans-regular.woff2 +0 -0
  208. package/storybook-static/sb-manager/globals-module-info.js +797 -0
  209. package/storybook-static/sb-manager/globals-runtime.js +69679 -0
  210. package/storybook-static/sb-manager/globals.js +34 -0
  211. package/storybook-static/sb-manager/runtime.js +13195 -0
  212. package/storybook-static/vite-inject-mocker-entry.js +18 -0
@@ -0,0 +1,231 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import React, { useState } from 'react';
3
+ import { Box, Button, Typography } from '@mui/material';
4
+ import { useForm } from 'react-hook-form';
5
+
6
+ import { RadioGroup } from './RadioGroup';
7
+ import {
8
+ BasicRadioGroupDefinition,
9
+ ColumnRadioGroupDefinition,
10
+ RadioGroupWithErrorDefinition,
11
+ DisabledRadioGroupDefinition,
12
+ UnborderedRadioGroupDefinition,
13
+ RHFRadioGroupDefinition,
14
+ } from './RadioGroup.definitions';
15
+
16
+ // =============================================================================
17
+ // Meta
18
+ // =============================================================================
19
+ const meta: Meta<typeof RadioGroup> = {
20
+ title: 'Components/RadioGroup',
21
+ component: RadioGroup,
22
+ tags: ['autodocs'],
23
+ parameters: {
24
+ layout: 'centered',
25
+ docs: {
26
+ description: {
27
+ component:
28
+ 'Un grupo de radio buttons basado en MUI `RadioGroup` + `Radio` + `FormControlLabel`. ' +
29
+ 'Soporta modo controlado (`value` + `onChange`) y modo react-hook-form (`name` + `control`). ' +
30
+ 'Mantiene compatibilidad visual con el legacy `FormRadioGroup` mediante la prop `bordered` (default: true).',
31
+ },
32
+ },
33
+ },
34
+ argTypes: {
35
+ label: { control: 'text', description: 'Label superior del grupo.' },
36
+ options: { control: 'object', description: 'Lista de opciones `{ value, label, disabled?, id? }`.' },
37
+ row: { control: 'boolean', description: 'Distribuir radios horizontalmente. Default: true.' },
38
+ size: { control: 'radio', options: ['small', 'medium'], description: 'Tamaño del radio.' },
39
+ bordered: { control: 'boolean', description: 'Renderizar contenedor con borde estilo "form field". Default: true.' },
40
+ borderRadius: { control: { type: 'number' }, description: 'Border radius del contenedor cuando `bordered`. Default: 10.' },
41
+ disabled: { control: 'boolean', description: 'Deshabilitar el grupo entero.' },
42
+ error: { control: 'boolean', description: 'Estado de error visual.' },
43
+ helperText: { control: 'text', description: 'Texto auxiliar debajo del grupo.' },
44
+ },
45
+ };
46
+
47
+ export default meta;
48
+ type Story = StoryObj<typeof RadioGroup>;
49
+
50
+ // =============================================================================
51
+ // Stories
52
+ // =============================================================================
53
+
54
+ const baseOptions = [
55
+ { value: 'a', label: 'Opción A' },
56
+ { value: 'b', label: 'Opción B' },
57
+ { value: 'c', label: 'Opción C' },
58
+ ];
59
+
60
+ export const BasicRadioGroup: Story = {
61
+ render: () => {
62
+ const [value, setValue] = useState('a');
63
+ return (
64
+ <Box sx={{ width: 360 }}>
65
+ <RadioGroup
66
+ label="Selecciona una opción"
67
+ options={baseOptions}
68
+ value={value}
69
+ onChange={setValue}
70
+ />
71
+ <Typography sx={{ mt: 2 }}>Valor actual: {value}</Typography>
72
+ </Box>
73
+ );
74
+ },
75
+ parameters: {
76
+ docs: {
77
+ description: { story: 'Uso básico controlado: `value` + `onChange`. Distribución horizontal por defecto.' },
78
+ source: { code: BasicRadioGroupDefinition.trim() },
79
+ },
80
+ },
81
+ };
82
+
83
+ export const ColumnRadioGroup: Story = {
84
+ render: () => {
85
+ const [value, setValue] = useState('md');
86
+ return (
87
+ <Box sx={{ width: 360 }}>
88
+ <RadioGroup
89
+ label="Tamaño"
90
+ options={[
91
+ { value: 'sm', label: 'Pequeño' },
92
+ { value: 'md', label: 'Mediano' },
93
+ { value: 'lg', label: 'Grande' },
94
+ ]}
95
+ value={value}
96
+ onChange={setValue}
97
+ row={false}
98
+ />
99
+ </Box>
100
+ );
101
+ },
102
+ parameters: {
103
+ docs: {
104
+ description: { story: 'Distribución vertical con `row={false}`.' },
105
+ source: { code: ColumnRadioGroupDefinition.trim() },
106
+ },
107
+ },
108
+ };
109
+
110
+ export const RadioGroupWithError: Story = {
111
+ render: () => {
112
+ const [value, setValue] = useState('');
113
+ const hasError = !value;
114
+ return (
115
+ <Box sx={{ width: 360 }}>
116
+ <RadioGroup
117
+ label="¿Aceptas los términos?"
118
+ options={[
119
+ { value: 'yes', label: 'Sí' },
120
+ { value: 'no', label: 'No' },
121
+ ]}
122
+ value={value}
123
+ onChange={setValue}
124
+ error={hasError}
125
+ helperText={hasError ? 'Este campo es obligatorio' : ''}
126
+ />
127
+ </Box>
128
+ );
129
+ },
130
+ parameters: {
131
+ docs: {
132
+ description: { story: 'Estado de error con `error` + `helperText`. Equivalente a `errorRequired` + `messageError` del legacy.' },
133
+ source: { code: RadioGroupWithErrorDefinition.trim() },
134
+ },
135
+ },
136
+ };
137
+
138
+ export const DisabledRadioGroup: Story = {
139
+ render: () => {
140
+ const [value, setValue] = useState('a');
141
+ const opts = [
142
+ { value: 'a', label: 'Opción A' },
143
+ { value: 'b', label: 'Opción B', disabled: true },
144
+ { value: 'c', label: 'Opción C' },
145
+ ];
146
+ return (
147
+ <Box sx={{ width: 360, display: 'flex', flexDirection: 'column', gap: 3 }}>
148
+ <RadioGroup
149
+ label="Grupo deshabilitado"
150
+ options={opts}
151
+ value={value}
152
+ onChange={setValue}
153
+ disabled
154
+ />
155
+ <RadioGroup
156
+ label="Opción B deshabilitada"
157
+ options={opts}
158
+ value={value}
159
+ onChange={setValue}
160
+ />
161
+ </Box>
162
+ );
163
+ },
164
+ parameters: {
165
+ docs: {
166
+ description: { story: 'Bloqueo total con `disabled`, o por opción individual con `option.disabled`.' },
167
+ source: { code: DisabledRadioGroupDefinition.trim() },
168
+ },
169
+ },
170
+ };
171
+
172
+ export const UnborderedRadioGroup: Story = {
173
+ render: () => {
174
+ const [value, setValue] = useState('a');
175
+ return (
176
+ <Box sx={{ width: 360 }}>
177
+ <RadioGroup
178
+ options={baseOptions}
179
+ value={value}
180
+ onChange={setValue}
181
+ bordered={false}
182
+ />
183
+ </Box>
184
+ );
185
+ },
186
+ parameters: {
187
+ docs: {
188
+ description: { story: 'Sin contenedor con borde — útil para agrupar inline con otros controles.' },
189
+ source: { code: UnborderedRadioGroupDefinition.trim() },
190
+ },
191
+ },
192
+ };
193
+
194
+ export const RHFRadioGroup: Story = {
195
+ render: () => {
196
+ const { control, handleSubmit, watch } = useForm({
197
+ defaultValues: { paymentMethod: 'card' },
198
+ });
199
+ const value = watch('paymentMethod');
200
+ return (
201
+ <Box
202
+ sx={{ width: 360 }}
203
+ component="form"
204
+ onSubmit={handleSubmit((data) => console.log(data))}
205
+ >
206
+ <RadioGroup
207
+ label="Método de pago"
208
+ options={[
209
+ { value: 'paypal', label: 'PayPal' },
210
+ { value: 'card', label: 'Tarjeta' },
211
+ { value: 'transfer', label: 'Transferencia' },
212
+ ]}
213
+ name="paymentMethod"
214
+ control={control}
215
+ validation={{ required: 'Selecciona un método' }}
216
+ />
217
+ <Typography sx={{ mt: 2 }}>Valor en el form: {value}</Typography>
218
+ <Button type="submit" sx={{ mt: 1 }}>Enviar</Button>
219
+ </Box>
220
+ );
221
+ },
222
+ parameters: {
223
+ docs: {
224
+ description: {
225
+ story:
226
+ 'Modo react-hook-form con `name` + `control`. El error y `helperText` se conectan automáticamente vía el `Controller`.',
227
+ },
228
+ source: { code: RHFRadioGroupDefinition.trim() },
229
+ },
230
+ },
231
+ };
@@ -0,0 +1,75 @@
1
+ import type { SxProps, Theme } from '@mui/material/styles';
2
+
3
+ export interface BuildRadioGroupSxOptions {
4
+ bordered: boolean;
5
+ borderRadius: number | string;
6
+ error: boolean;
7
+ }
8
+
9
+ const toRadius = (borderRadius: number | string) =>
10
+ typeof borderRadius === 'number' ? `${borderRadius}px` : borderRadius;
11
+
12
+ /**
13
+ * Transición compartida (matchea el InputGroup).
14
+ */
15
+ const FOCUS_TRANSITION =
16
+ 'border-color 200ms cubic-bezier(0.0, 0, 0.2, 1) 0ms, ' +
17
+ 'box-shadow 200ms cubic-bezier(0.0, 0, 0.2, 1) 0ms';
18
+
19
+ /**
20
+ * Builder de sx para RadioGroup. Reproduce el estilo del antiguo
21
+ * `FormRadioGroup` legacy: contenedor con borde + padding vertical.
22
+ *
23
+ * Cuando `bordered=true`, el contenedor gana:
24
+ * - `:focus-within` → borde + shadow primario (mismo patrón que `InputGroup`).
25
+ * - `error=true` → borde + shadow de error (palette.error.main).
26
+ *
27
+ * El shadow es de 1px, así que no hay shift de layout — sólo se intensifica
28
+ * visualmente el borde sin empujar nada alrededor.
29
+ *
30
+ * Si `bordered=false`, devuelve un sx mínimo (sólo helper text reset).
31
+ */
32
+ export const buildRadioGroupSx = ({
33
+ bordered,
34
+ borderRadius,
35
+ error,
36
+ }: BuildRadioGroupSxOptions): SxProps<Theme> => {
37
+ const radius = toRadius(borderRadius);
38
+
39
+ if (!bordered) {
40
+ return {
41
+ '& .MuiFormHelperText-root': { marginLeft: 0 },
42
+ };
43
+ }
44
+
45
+ return (theme) => ({
46
+ '& .MuiRadioGroup-root': {
47
+ paddingTop: 1.25,
48
+ paddingBottom: 1.25,
49
+ paddingX: 1.5,
50
+ border: `1px solid ${
51
+ error ? theme.palette.error.main : theme.palette.divider
52
+ }`,
53
+ borderRadius: radius,
54
+ transition: FOCUS_TRANSITION,
55
+ ...(error && {
56
+ boxShadow: `0 0 0 1px ${theme.palette.error.main}`,
57
+ }),
58
+ // Focus del usuario sobre cualquier radio interno.
59
+ '&:focus-within': {
60
+ borderColor: error
61
+ ? theme.palette.error.main
62
+ : theme.palette.primary.main,
63
+ boxShadow: `0 0 0 1px ${
64
+ error ? theme.palette.error.main : theme.palette.primary.main
65
+ }`,
66
+ },
67
+ },
68
+ '& .MuiFormHelperText-root': {
69
+ marginLeft: 0,
70
+ },
71
+ '& .MuiFormLabel-root': {
72
+ marginBottom: 0.5,
73
+ },
74
+ });
75
+ };
@@ -0,0 +1,196 @@
1
+ import { useTheme } from '@mui/material/styles';
2
+ import {
3
+ FormControl,
4
+ FormControlLabel,
5
+ FormHelperText,
6
+ FormLabel,
7
+ Radio,
8
+ RadioGroup as MuiRadioGroup,
9
+ type RadioGroupProps as MuiRadioGroupProps,
10
+ type SxProps,
11
+ type Theme,
12
+ } from '@mui/material';
13
+ import { Controller, type Control, type RegisterOptions } from 'react-hook-form';
14
+
15
+ import { buildRadioGroupSx } from './RadioGroup.sx';
16
+ import { resolvePreset } from '../_shared/resolvePreset';
17
+
18
+ // ── Tipos de dominio ─────────────────────────────────────────────────────
19
+ export interface RadioOption {
20
+ /** Valor que se emite cuando se selecciona la opción. */
21
+ value: string;
22
+ /** Texto mostrado al lado del radio. */
23
+ label: string;
24
+ /** Deshabilitar opción individualmente. */
25
+ disabled?: boolean;
26
+ /** ID custom del input (default: prefijo + value). */
27
+ id?: string;
28
+ }
29
+
30
+ export type RadioGroupSize = 'small' | 'medium';
31
+
32
+ // ── Props base (todo lo común entre RHF y controlado) ────────────────────
33
+ export interface BaseRadioGroupProps
34
+ extends Omit<MuiRadioGroupProps, 'value' | 'onChange' | 'defaultValue'> {
35
+ options: RadioOption[];
36
+ /** Label superior del grupo. */
37
+ label?: string;
38
+ /** Distribución horizontal (default: true — replica el comportamiento legacy). */
39
+ row?: boolean;
40
+ size?: RadioGroupSize;
41
+ disabled?: boolean;
42
+ error?: boolean;
43
+ helperText?: string;
44
+ /** Renderiza un contenedor con borde estilo "form field". Default: true. */
45
+ bordered?: boolean;
46
+ /** Border radius del contenedor (cuando bordered). Default: 10. */
47
+ borderRadius?: number | string;
48
+ defaultValue?: string;
49
+ /**
50
+ * Nombre del preset de estilo registrado en `theme.styles.RadioGroup`.
51
+ * - `"default"` (o ausente) = estilo built-in del paquete.
52
+ * - Cualquier otro string = mergea el preset encima del estilo built-in.
53
+ */
54
+ preset?: string;
55
+ sx?: SxProps<Theme>;
56
+ }
57
+
58
+ // ── Variantes discriminadas (RHF vs controlado) ──────────────────────────
59
+ export interface RHFRadioGroupProps extends BaseRadioGroupProps {
60
+ name: string;
61
+ control: Control<any>;
62
+ validation?: RegisterOptions;
63
+ value?: never;
64
+ onChange?: never;
65
+ }
66
+
67
+ export interface ControlledRadioGroupProps extends BaseRadioGroupProps {
68
+ name?: string;
69
+ control?: never;
70
+ validation?: never;
71
+ value: string;
72
+ onChange: (value: string) => void;
73
+ }
74
+
75
+ // ── API pública final ────────────────────────────────────────────────────
76
+ export type RadioGroupProps = RHFRadioGroupProps | ControlledRadioGroupProps;
77
+
78
+ export const RadioGroup: React.FC<RadioGroupProps> = (props) => {
79
+ const {
80
+ options,
81
+ label,
82
+ row = true,
83
+ size = 'small',
84
+ disabled = false,
85
+ error = false,
86
+ helperText,
87
+ bordered = true,
88
+ borderRadius = 10,
89
+ defaultValue,
90
+ preset,
91
+ sx,
92
+ ...rest
93
+ } = props as ControlledRadioGroupProps & {
94
+ control?: Control<any>;
95
+ validation?: RegisterOptions;
96
+ };
97
+
98
+ const theme = useTheme();
99
+ const presetSx = resolvePreset('RadioGroup', preset, theme);
100
+
101
+ // Construye el sx con el estado de error efectivo; se llama por rama
102
+ // (RHF usa el error del Controller, controlado usa el prop `error`).
103
+ const buildSx = (effectiveError: boolean) => [
104
+ buildRadioGroupSx({ bordered, borderRadius, error: effectiveError }),
105
+ ...(presetSx ? [presetSx] : []),
106
+ ...(Array.isArray(sx) ? sx : [sx]),
107
+ ];
108
+
109
+ const renderOptions = (
110
+ currentValue: string | undefined,
111
+ onChange: (value: string) => void,
112
+ fieldName?: string,
113
+ ) =>
114
+ options.map((opt) => {
115
+ const inputId = opt.id ?? `${fieldName ?? 'radio'}-${opt.value}`;
116
+ return (
117
+ <FormControlLabel
118
+ key={inputId}
119
+ value={opt.value}
120
+ control={
121
+ <Radio
122
+ id={inputId}
123
+ size={size}
124
+ disabled={disabled || opt.disabled}
125
+ />
126
+ }
127
+ label={opt.label}
128
+ checked={currentValue === opt.value}
129
+ disabled={disabled || opt.disabled}
130
+ />
131
+ );
132
+ });
133
+
134
+ // ── RHF mode ──────────────────────────────────────────────────────────
135
+ if ('control' in props && props.control) {
136
+ const { name, control, validation } = props as RHFRadioGroupProps;
137
+ return (
138
+ <Controller
139
+ name={name}
140
+ control={control}
141
+ rules={validation}
142
+ defaultValue={defaultValue}
143
+ render={({ field, fieldState: { error: fieldError } }) => {
144
+ const finalError = !!fieldError || error;
145
+ const finalHelperText = fieldError?.message ?? helperText;
146
+ return (
147
+ <FormControl
148
+ error={finalError}
149
+ disabled={disabled}
150
+ sx={buildSx(finalError)}
151
+ fullWidth
152
+ >
153
+ {label && <FormLabel>{label}</FormLabel>}
154
+ <MuiRadioGroup
155
+ {...rest}
156
+ row={row}
157
+ name={field.name}
158
+ value={field.value ?? ''}
159
+ onBlur={field.onBlur}
160
+ onChange={(_e, value) => field.onChange(value)}
161
+ >
162
+ {renderOptions(field.value, field.onChange, field.name)}
163
+ </MuiRadioGroup>
164
+ {finalHelperText && <FormHelperText>{finalHelperText}</FormHelperText>}
165
+ </FormControl>
166
+ );
167
+ }}
168
+ />
169
+ );
170
+ }
171
+
172
+ // ── Controlled mode ───────────────────────────────────────────────────
173
+ const { value, onChange, name } = props as ControlledRadioGroupProps;
174
+ return (
175
+ <FormControl
176
+ error={error}
177
+ disabled={disabled}
178
+ sx={buildSx(error)}
179
+ fullWidth
180
+ >
181
+ {label && <FormLabel>{label}</FormLabel>}
182
+ <MuiRadioGroup
183
+ {...rest}
184
+ row={row}
185
+ name={name}
186
+ value={value ?? ''}
187
+ onChange={(_e, next) => onChange(next)}
188
+ >
189
+ {renderOptions(value, onChange, name)}
190
+ </MuiRadioGroup>
191
+ {helperText && <FormHelperText>{helperText}</FormHelperText>}
192
+ </FormControl>
193
+ );
194
+ };
195
+
196
+ export default RadioGroup;
@@ -0,0 +1,10 @@
1
+ // Re-export barrel para compatibilidad con imports antiguos.
2
+ // Los tipos viven dentro de RadioGroup.tsx.
3
+ export type {
4
+ RadioOption,
5
+ RadioGroupSize,
6
+ BaseRadioGroupProps,
7
+ RHFRadioGroupProps,
8
+ ControlledRadioGroupProps,
9
+ RadioGroupProps,
10
+ } from './RadioGroup';
@@ -0,0 +1,9 @@
1
+ export { RadioGroup, default } from './RadioGroup';
2
+ export type {
3
+ RadioOption,
4
+ RadioGroupSize,
5
+ BaseRadioGroupProps,
6
+ RHFRadioGroupProps,
7
+ ControlledRadioGroupProps,
8
+ RadioGroupProps,
9
+ } from './RadioGroup.types';
@@ -67,6 +67,78 @@ export const DefaultStepper: Story = {
67
67
  };
68
68
 
69
69
 
70
+ export const ScrollableHorizontal: Story = {
71
+ render: (args) => {
72
+ const [currentStep, setCurrentStep] = useState(0);
73
+ const totalSteps = 7;
74
+ return (
75
+ <Box>
76
+ <Box
77
+ sx={{
78
+ // Simulamos un viewport angosto (mobile) para ver el scroll horizontal
79
+ // sin romper el layout del contenedor padre.
80
+ width: 360,
81
+ maxWidth: "100%",
82
+ border: "1px dashed",
83
+ borderColor: "divider",
84
+ p: 2,
85
+ }}
86
+ >
87
+ <Stepper currentStep={currentStep} {...args}>
88
+ <Step label="Datos del cupón">
89
+ <div>Contenido del paso 1</div>
90
+ </Step>
91
+ <Step label="Monto">
92
+ <div>Contenido del paso 2</div>
93
+ </Step>
94
+ <Step label="Restricciones">
95
+ <div>Contenido del paso 3</div>
96
+ </Step>
97
+ <Step label="Recursos visuales">
98
+ <div>Contenido del paso 4</div>
99
+ </Step>
100
+ <Step label="Vigencia">
101
+ <div>Contenido del paso 5</div>
102
+ </Step>
103
+ <Step label="Usuarios dirigidos">
104
+ <div>Contenido del paso 6</div>
105
+ </Step>
106
+ <Step label="Confirmación">
107
+ <div>Contenido del paso 7</div>
108
+ </Step>
109
+ </Stepper>
110
+ </Box>
111
+ <Button
112
+ type="button"
113
+ onClick={() => setCurrentStep((prev) => Math.max(prev - 1, 0))}
114
+ sx={{ marginTop: 16, marginRight: 8 }}
115
+ >
116
+ Atras
117
+ </Button>
118
+ <Button
119
+ type="button"
120
+ onClick={() => setCurrentStep((prev) => Math.min(prev + 1, totalSteps - 1))}
121
+ sx={{ marginTop: 16 }}
122
+ >
123
+ Siguiente
124
+ </Button>
125
+ </Box>
126
+ );
127
+ },
128
+ parameters: {
129
+ docs: {
130
+ description: {
131
+ story:
132
+ "Stepper horizontal dentro de un contenedor angosto (simula mobile). " +
133
+ "Cuando los steps no caben, el header muestra scroll horizontal en lugar " +
134
+ "de desbordar el contenedor. El cuerpo (contenido del step activo) mantiene " +
135
+ "su ancho normal."
136
+ }
137
+ }
138
+ }
139
+ };
140
+
141
+
70
142
  export const ColorStepper: Story = {
71
143
  render: (args) => {
72
144
  const [currentStep, setCurrentStep] = useState(0);