@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.
- package/build.js +38 -75
- package/dist/README.md +243 -0
- package/dist/components/Drawer/Drawer.cjs +14 -17
- package/dist/components/Drawer/Drawer.cjs.map +1 -1
- package/dist/components/Drawer/Drawer.d.ts +8 -1
- package/dist/components/Drawer/Drawer.js +14 -17
- package/dist/components/Drawer/Drawer.js.map +1 -1
- package/dist/components/Input/Input.definitions.d.ts +1 -0
- package/dist/components/RadioGroup/RadioGroup.cjs +202 -0
- package/dist/components/RadioGroup/RadioGroup.cjs.map +1 -0
- package/dist/components/RadioGroup/RadioGroup.d.ts +53 -0
- package/dist/components/RadioGroup/RadioGroup.definitions.d.ts +6 -0
- package/dist/components/RadioGroup/RadioGroup.js +202 -0
- package/dist/components/RadioGroup/RadioGroup.js.map +1 -0
- package/dist/components/RadioGroup/RadioGroup.sx.d.ts +20 -0
- package/dist/components/RadioGroup/RadioGroup.types.d.ts +1 -0
- package/dist/components/RadioGroup/index.d.ts +2 -0
- package/dist/components/RadioGroup.d.ts +6 -0
- package/dist/components/Stepper/Stepper.cjs +136 -23
- package/dist/components/Stepper/Stepper.cjs.map +1 -1
- package/dist/components/Stepper/Stepper.js +137 -24
- package/dist/components/Stepper/Stepper.js.map +1 -1
- package/dist/components/Switch/Switch.cjs +181 -0
- package/dist/components/Switch/Switch.cjs.map +1 -0
- package/dist/components/Switch/Switch.d.ts +43 -0
- package/dist/components/Switch/Switch.definitions.d.ts +7 -0
- package/dist/components/Switch/Switch.js +181 -0
- package/dist/components/Switch/Switch.js.map +1 -0
- package/dist/components/Switch/Switch.sx.d.ts +22 -0
- package/dist/components/Switch/Switch.types.d.ts +1 -0
- package/dist/components/Switch/index.d.ts +2 -0
- package/dist/components/Switch.d.ts +6 -0
- package/dist/index.cjs +24 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +7 -1
- package/dist/mui.d.ts +1 -0
- package/dist/package.json +207 -0
- package/dist/theme/componentStyles.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/Drawer/Drawer.stories.tsx +168 -0
- package/src/components/Drawer/Drawer.tsx +26 -18
- package/src/components/Input/Input.definitions.ts +24 -0
- package/src/components/Input/Input.stories.tsx +29 -0
- package/src/components/RadioGroup/RadioGroup.definitions.ts +177 -0
- package/src/components/RadioGroup/RadioGroup.stories.tsx +231 -0
- package/src/components/RadioGroup/RadioGroup.sx.ts +75 -0
- package/src/components/RadioGroup/RadioGroup.tsx +196 -0
- package/src/components/RadioGroup/RadioGroup.types.ts +10 -0
- package/src/components/RadioGroup/index.ts +9 -0
- package/src/components/Stepper/Stepper.stories.tsx +72 -0
- package/src/components/Stepper/Stepper.tsx +139 -4
- package/src/components/Switch/Switch.definitions.ts +134 -0
- package/src/components/Switch/Switch.stories.tsx +213 -0
- package/src/components/Switch/Switch.sx.ts +81 -0
- package/src/components/Switch/Switch.tsx +172 -0
- package/src/components/Switch/Switch.types.ts +10 -0
- package/src/components/Switch/index.ts +9 -0
- package/src/mui.ts +10 -0
- package/src/theme/componentStyles.ts +3 -1
- package/storybook-static/addon-visual-tests-assets/visual-test-illustration.mp4 +0 -0
- package/storybook-static/assets/AccountCircle-BDZFsbTw.js +1 -0
- package/storybook-static/assets/ActionMenu-EynP8yU1.js +19 -0
- package/storybook-static/assets/ActionMenu.stories-DqSqRGix.js +185 -0
- package/storybook-static/assets/Alert-3zvTPc0p.js +1 -0
- package/storybook-static/assets/AppBar.stories-DcX3M5th.js +172 -0
- package/storybook-static/assets/Autocomplete.stories-CXJm8FOT.js +788 -0
- package/storybook-static/assets/Avatar-NbFfkZws.js +1 -0
- package/storybook-static/assets/Avatar.stories-CwOYCzqU.js +390 -0
- package/storybook-static/assets/Box-BnhEcfFm.js +1 -0
- package/storybook-static/assets/Button-D9h7OggD.js +1 -0
- package/storybook-static/assets/Button-DBpqmVB_.js +1 -0
- package/storybook-static/assets/Button.stories-F20dmnjq.js +320 -0
- package/storybook-static/assets/ButtonBase-qyaMEhe4.js +74 -0
- package/storybook-static/assets/Card.stories-B3NpAhO0.js +154 -0
- package/storybook-static/assets/CheckCircleOutline-CEj5mDsl.js +1 -0
- package/storybook-static/assets/Chip-C3vKPpzR.js +1 -0
- package/storybook-static/assets/Chip.stories-sxcfHVo9.js +333 -0
- package/storybook-static/assets/CircularProgress-DC7ZNWwl.js +28 -0
- package/storybook-static/assets/Clear-4kYcKvT3.js +1 -0
- package/storybook-static/assets/ClipBoard-DvLBdNHe.js +1 -0
- package/storybook-static/assets/ClipBoard.stories-BGUo47r6.js +108 -0
- package/storybook-static/assets/Close-CgHeRgmh.js +1 -0
- package/storybook-static/assets/Close-Cy8nELYU.js +1 -0
- package/storybook-static/assets/Color-AVL7NMMY-BJKvwERm.js +1 -0
- package/storybook-static/assets/ContentCopy-BfLTDb10.js +1 -0
- package/storybook-static/assets/DatePicker-Clkpr-Ku.js +1 -0
- package/storybook-static/assets/DatePicker.stories-EaUCMkh3.js +444 -0
- package/storybook-static/assets/DateRangePicker.stories-BMlkj-8K.js +390 -0
- package/storybook-static/assets/DateTimePicker.stories-B6gdzKq5.js +555 -0
- package/storybook-static/assets/DefaultPropsProvider-BGoQxtDa.js +16 -0
- package/storybook-static/assets/Delete-D2SMMmIA.js +1 -0
- package/storybook-static/assets/DialogContent-BeCDKgax.js +1 -0
- package/storybook-static/assets/Divider-BbCj9wT4.js +1 -0
- package/storybook-static/assets/DocsRenderer-PQXLIZUC-BebLK5Y_.js +1243 -0
- package/storybook-static/assets/Drawer-DcFwy73r.js +1 -0
- package/storybook-static/assets/Drawer.stories-C5AZkJBk.js +173 -0
- package/storybook-static/assets/EmptyTable-B-RKtgVs.png +0 -0
- package/storybook-static/assets/ErrorOutline-D9gM7ART.js +1 -0
- package/storybook-static/assets/Fade-Ll96CvH8.js +1 -0
- package/storybook-static/assets/Flyout.stories-Cf7z6MNw.js +163 -0
- package/storybook-static/assets/Gallery.stories-DdpWVTF6.js +127 -0
- package/storybook-static/assets/Grow-8y4FglGK.js +1 -0
- package/storybook-static/assets/Home-BRvJEp2L.js +1 -0
- package/storybook-static/assets/Icon.stories-D0mUiW_t.js +78 -0
- package/storybook-static/assets/IconButton-9OYSTH58.js +1 -0
- package/storybook-static/assets/Input-CjX0t4h-.js +1 -0
- package/storybook-static/assets/Input.stories-BRxekliy.js +650 -0
- package/storybook-static/assets/InputGroup.stories-DH6gUfmn.js +306 -0
- package/storybook-static/assets/KeyboardArrowRight-WO_attK2.js +1 -0
- package/storybook-static/assets/KeyboardArrowUp-DsyVef-i.js +1 -0
- package/storybook-static/assets/ListItem-D3O0103N.js +1 -0
- package/storybook-static/assets/ListItemIcon-hca6xN79.js +1 -0
- package/storybook-static/assets/ListItemText-BFLAwLdl.js +1 -0
- package/storybook-static/assets/Logout-gj-P3AfU.js +1 -0
- package/storybook-static/assets/Menu-ClzfjLc3.js +1 -0
- package/storybook-static/assets/MenuButton.stories-B-W_QVDt.js +162 -0
- package/storybook-static/assets/MenuItem-iU6tAqJI.js +1 -0
- package/storybook-static/assets/Modal-3okp9H2i.js +1 -0
- package/storybook-static/assets/Modal.stories-DIWzm4qR.js +468 -0
- package/storybook-static/assets/MoreVert-BoIVG4gh.js +1 -0
- package/storybook-static/assets/Notifications-DY_A-Sho.js +1 -0
- package/storybook-static/assets/PageLoader.stories-DmtO1mlm.js +158 -0
- package/storybook-static/assets/Paper-SwQBhqI7.js +1 -0
- package/storybook-static/assets/Person-CkQl-mpq.js +1 -0
- package/storybook-static/assets/PickersModalDialog-Tjnr_cu5.js +10 -0
- package/storybook-static/assets/PickersToolbarButton-Tt185-si.js +1 -0
- package/storybook-static/assets/Popper-CnCTYXxy.js +1 -0
- package/storybook-static/assets/Portal-Cj8XF9Lf.js +1 -0
- package/storybook-static/assets/ScrollTopButton.stories-BflQCwNP.js +90 -0
- package/storybook-static/assets/Select-CjcuMAY0.js +4 -0
- package/storybook-static/assets/Select-DJh2biEb.js +3 -0
- package/storybook-static/assets/Select.stories-DU1Gb3I2.js +1103 -0
- package/storybook-static/assets/Settings-BLKc1CnO.js +1 -0
- package/storybook-static/assets/Snackbar-BtVeKTw6.js +1 -0
- package/storybook-static/assets/Stack-D01OUIXi.js +1 -0
- package/storybook-static/assets/Stat.stories-Bn9-iuPT.js +60 -0
- package/storybook-static/assets/StatusMessage.stories-hnfX8FeU.js +73 -0
- package/storybook-static/assets/Stepper-BtKB5ykn.js +2 -0
- package/storybook-static/assets/Stepper.stories-CTEZbgPc.js +165 -0
- package/storybook-static/assets/Table.stories-CTn2Ktmn.js +1260 -0
- package/storybook-static/assets/TableContainer-CzLNaEU-.js +1 -0
- package/storybook-static/assets/TableRow-CS88-1HF.js +2 -0
- package/storybook-static/assets/Tabs-DLpDOu_n.js +1 -0
- package/storybook-static/assets/Tabs.stories-BFVuFy_5.js +159 -0
- package/storybook-static/assets/TextField-22T-xHBm.js +1 -0
- package/storybook-static/assets/Timeline.stories-DJU_U2Hv.js +97 -0
- package/storybook-static/assets/Tooltip-DbnHUxNj.js +1 -0
- package/storybook-static/assets/Tooltip.stories-B7tA3dnV.js +66 -0
- package/storybook-static/assets/Typography-BgntX2Ep.js +1 -0
- package/storybook-static/assets/Wizard.stories-CVrJLK_D.js +23 -0
- package/storybook-static/assets/createSimplePaletteValueFilter-bm0fmN_7.js +1 -0
- package/storybook-static/assets/createSvgIcon-D_Gca4vA.js +1 -0
- package/storybook-static/assets/debounce-Be36O1Ab.js +1 -0
- package/storybook-static/assets/emotion-react.browser.esm--g-C9cX9.js +8 -0
- package/storybook-static/assets/extendSxProp-CEpa30hT.js +1 -0
- package/storybook-static/assets/formField.sx-DMCmZIAa.js +1 -0
- package/storybook-static/assets/getReactElementRef-BQ3ANZdy.js +1 -0
- package/storybook-static/assets/iframe-BAJnc_4n.js +1079 -0
- package/storybook-static/assets/index-B1tlhOpe.js +240 -0
- package/storybook-static/assets/index-BF3FAXTk.js +9 -0
- package/storybook-static/assets/index-CIeucmOB.js +2 -0
- package/storybook-static/assets/index-CY7j4a7o.js +1 -0
- package/storybook-static/assets/index-CxkKctw5.js +1 -0
- package/storybook-static/assets/isFocusVisible-B8k4qzLc.js +1 -0
- package/storybook-static/assets/isMuiElement-CTZSFcY5.js +1 -0
- package/storybook-static/assets/jsx-runtime-D_zvdyIk.js +9 -0
- package/storybook-static/assets/listItemTextClasses-CC_rwJam.js +1 -0
- package/storybook-static/assets/mergeSlotProps-B0UBKBMe.js +1 -0
- package/storybook-static/assets/ownerDocument-DW-IO8s5.js +1 -0
- package/storybook-static/assets/ownerWindow-HkKU3E4x.js +1 -0
- package/storybook-static/assets/preload-helper-PPVm8Dsz.js +1 -0
- package/storybook-static/assets/react-18-BUJ64QCV.js +25 -0
- package/storybook-static/assets/resolvePreset-CN2aOJJr.js +1 -0
- package/storybook-static/assets/useControlled-DsVh1a5j.js +1 -0
- package/storybook-static/assets/useForkRef-0ANIrxcF.js +1 -0
- package/storybook-static/assets/useId-b4fZxjOL.js +1 -0
- package/storybook-static/assets/useMobilePicker-DK-c8xbD.js +1 -0
- package/storybook-static/assets/usePreviousProps-WR0rG4aR.js +1 -0
- package/storybook-static/assets/useSlot-b6pXgp5_.js +1 -0
- package/storybook-static/assets/useSlotProps-C0uMfuBt.js +1 -0
- package/storybook-static/assets/useTheme-BmOJK7ra.js +1 -0
- package/storybook-static/assets/useThemeProps-DYtxXiUU.js +1 -0
- package/storybook-static/assets/useThemeProps-U4yXiZ_5.js +1 -0
- package/storybook-static/assets/useTimeout-DNjRaOWc.js +1 -0
- package/storybook-static/assets/visuallyHidden-Dan1xhjv.js +1 -0
- package/storybook-static/favicon-wrapper.svg +46 -0
- package/storybook-static/favicon.svg +1 -0
- package/storybook-static/iframe.html +686 -0
- package/storybook-static/index.html +160 -0
- package/storybook-static/index.json +1 -0
- package/storybook-static/nunito-sans-bold-italic.woff2 +0 -0
- package/storybook-static/nunito-sans-bold.woff2 +0 -0
- package/storybook-static/nunito-sans-italic.woff2 +0 -0
- package/storybook-static/nunito-sans-regular.woff2 +0 -0
- package/storybook-static/project.json +1 -0
- package/storybook-static/sb-addons/chromatic-com-storybook-2/manager-bundle.js +356 -0
- package/storybook-static/sb-addons/chromatic-com-storybook-2/manager-bundle.js.LEGAL.txt +40 -0
- package/storybook-static/sb-addons/docs-4/manager-bundle.js +151 -0
- package/storybook-static/sb-addons/onboarding-1/manager-bundle.js +127 -0
- package/storybook-static/sb-addons/storybook-core-server-presets-0/common-manager-bundle.js +971 -0
- package/storybook-static/sb-addons/vitest-3/manager-bundle.js +3 -0
- package/storybook-static/sb-common-assets/favicon-wrapper.svg +46 -0
- package/storybook-static/sb-common-assets/favicon.svg +1 -0
- package/storybook-static/sb-common-assets/nunito-sans-bold-italic.woff2 +0 -0
- package/storybook-static/sb-common-assets/nunito-sans-bold.woff2 +0 -0
- package/storybook-static/sb-common-assets/nunito-sans-italic.woff2 +0 -0
- package/storybook-static/sb-common-assets/nunito-sans-regular.woff2 +0 -0
- package/storybook-static/sb-manager/globals-module-info.js +797 -0
- package/storybook-static/sb-manager/globals-runtime.js +69679 -0
- package/storybook-static/sb-manager/globals.js +34 -0
- package/storybook-static/sb-manager/runtime.js +13195 -0
- 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';
|
|
@@ -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);
|