@soyfri/shared-library 2.0.0-beta.2 → 2.0.0-beta.4
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/.dockerignore +8 -0
- package/.github/workflows/publish.yml +107 -0
- package/.prettierrc +3 -0
- package/.storybook/main.ts +19 -0
- package/.storybook/preview.ts +14 -0
- package/.storybook/vitest.setup.ts +9 -0
- package/Dockerfile +37 -0
- package/build.js +102 -0
- package/chromatic.config.json +5 -0
- package/cleanDirectories.js +40 -0
- package/dist/README.md +243 -0
- package/dist/components/Icon/Icon.js +1 -1
- package/dist/components/Table/Table.js +1 -1
- 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 +197 -0
- package/package.json +4 -32
- package/rollup.config.cjs +87 -0
- package/src/components/ActionMenu/ActionMenu.stories.tsx +230 -0
- package/src/components/ActionMenu/ActionMenu.tsx +174 -0
- package/src/components/ActionMenu/index.ts +2 -0
- package/src/components/AppBar/AppBar.stories.tsx +272 -0
- package/src/components/AppBar/AppBar.sx.ts +32 -0
- package/src/components/AppBar/AppBar.tsx +123 -0
- package/src/components/AppBar/AppBarBrand.tsx +120 -0
- package/src/components/AppBar/AppBarContext.ts +25 -0
- package/src/components/AppBar/AppBarMenuToggle.tsx +90 -0
- package/src/components/AppBar/AppBarUserMenu.tsx +217 -0
- package/src/components/AppBar/index.ts +25 -0
- package/src/components/Autocomplete/Autocomplete.definitions.ts +477 -0
- package/src/components/Autocomplete/Autocomplete.helpers.ts +60 -0
- package/src/components/Autocomplete/Autocomplete.stories.tsx +748 -0
- package/src/components/Autocomplete/Autocomplete.sx.ts +30 -0
- package/src/components/Autocomplete/Autocomplete.tsx +361 -0
- package/src/components/Autocomplete/Autocomplete.types.ts +13 -0
- package/src/components/Autocomplete/_parts/AutocompleteChips.tsx +55 -0
- package/src/components/Autocomplete/_parts/AutocompleteLoader.tsx +17 -0
- package/src/components/Autocomplete/_parts/AutocompleteOption.tsx +31 -0
- package/src/components/Autocomplete/index.ts +12 -0
- package/src/components/Avatar/Avatar.definitions.ts +162 -0
- package/src/components/Avatar/Avatar.stories.tsx +258 -0
- package/src/components/Avatar/Avatar.tsx +206 -0
- package/src/components/Avatar/index.ts +1 -0
- package/src/components/Button/Button.definition.ts +97 -0
- package/src/components/Button/Button.stories.tsx +285 -0
- package/src/components/Button/Button.tsx +67 -0
- package/src/components/Button/index.ts +1 -0
- package/src/components/Card/Card.definition.ts +5 -0
- package/src/components/Card/Card.stories.tsx +221 -0
- package/src/components/Card/Card.sx.ts +104 -0
- package/src/components/Card/Card.tsx +200 -0
- package/src/components/Card/index.ts +9 -0
- package/src/components/Chip/Chip.definitions.ts +167 -0
- package/src/components/Chip/Chip.stories.tsx +265 -0
- package/src/components/Chip/Chip.tsx +61 -0
- package/src/components/Chip/index.ts +1 -0
- package/src/components/Column/Column.tsx +29 -0
- package/src/components/Column/index.ts +1 -0
- package/src/components/DatePicker/DatePicker.definitions.ts +228 -0
- package/src/components/DatePicker/DatePicker.helpers.ts +24 -0
- package/src/components/DatePicker/DatePicker.stories.tsx +309 -0
- package/src/components/DatePicker/DatePicker.sx.ts +33 -0
- package/src/components/DatePicker/DatePicker.tsx +189 -0
- package/src/components/DatePicker/DatePicker.types.ts +10 -0
- package/src/components/DatePicker/index.ts +9 -0
- package/src/components/DateRangePicker/DateRangePicker.definitions.ts +191 -0
- package/src/components/DateRangePicker/DateRangePicker.stories.tsx +252 -0
- package/src/components/DateRangePicker/DateRangePicker.tsx +56 -0
- package/src/components/DateRangePicker/index.ts +1 -0
- package/src/components/DateTimePicker/DateTimePicker.definitions.ts +256 -0
- package/src/components/DateTimePicker/DateTimePicker.helpers.ts +38 -0
- package/src/components/DateTimePicker/DateTimePicker.stories.tsx +418 -0
- package/src/components/DateTimePicker/DateTimePicker.sx.ts +30 -0
- package/src/components/DateTimePicker/DateTimePicker.tsx +225 -0
- package/src/components/DateTimePicker/DateTimePicker.types.ts +10 -0
- package/src/components/DateTimePicker/index.ts +9 -0
- package/src/components/Drawer/Drawer.stories.tsx +270 -0
- package/src/components/Drawer/Drawer.sx.ts +106 -0
- package/src/components/Drawer/Drawer.tsx +214 -0
- package/src/components/Drawer/DrawerContext.ts +26 -0
- package/src/components/Drawer/DrawerItem.tsx +110 -0
- package/src/components/Drawer/index.ts +10 -0
- package/src/components/Flyout/Flyout.stories.tsx +282 -0
- package/src/components/Flyout/Flyout.tsx +122 -0
- package/src/components/Flyout/index.ts +1 -0
- package/src/components/Gallery/Gallery.definition.tsx +37 -0
- package/src/components/Gallery/Gallery.stories.tsx +82 -0
- package/src/components/Gallery/Gallery.tsx +118 -0
- package/src/components/Gallery/GalleryLightbox.tsx +170 -0
- package/src/components/Gallery/GalleryMain.tsx +84 -0
- package/src/components/Gallery/GalleryThumbnails.tsx +106 -0
- package/src/components/Gallery/index.ts +1 -0
- package/src/components/Icon/Icon.stories.tsx +121 -0
- package/src/components/Icon/Icon.tsx +175 -0
- package/src/components/Icon/index.ts +2 -0
- package/src/components/Input/Input.definitions.ts +324 -0
- package/src/components/Input/Input.helpers.ts +49 -0
- package/src/components/Input/Input.stories.tsx +499 -0
- package/src/components/Input/Input.sx.ts +42 -0
- package/src/components/Input/Input.tsx +141 -0
- package/src/components/Input/Input.types.ts +10 -0
- package/src/components/Input/index.ts +9 -0
- package/src/components/InputGroup/InputGroup.definitions.ts +158 -0
- package/src/components/InputGroup/InputGroup.stories.tsx +267 -0
- package/src/components/InputGroup/InputGroup.tsx +179 -0
- package/src/components/InputGroup/index.ts +1 -0
- package/src/components/MenuButton/MenuButton.stories.tsx +197 -0
- package/src/components/MenuButton/MenuButton.tsx +100 -0
- package/src/components/MenuButton/index.ts +1 -0
- package/src/components/Modal/Modal.stories.tsx +721 -0
- package/src/components/Modal/Modal.tsx +355 -0
- package/src/components/Modal/ModalBody.tsx +16 -0
- package/src/components/Modal/ModalFooter.tsx +71 -0
- package/src/components/Modal/ModalHeader.tsx +18 -0
- package/src/components/Modal/index.ts +6 -0
- package/src/components/PageLoader/PageLoader.stories.tsx +217 -0
- package/src/components/PageLoader/PageLoader.tsx +96 -0
- package/src/components/PageLoader/index.ts +2 -0
- package/src/components/ScrollTopButton/ScrollTopButton.stories.tsx +158 -0
- package/src/components/ScrollTopButton/ScrollTopButton.tsx +135 -0
- package/src/components/ScrollTopButton/index.ts +8 -0
- package/src/components/ScrollTopButton/scrollToTop.ts +37 -0
- package/src/components/Select/Select.definitions.ts +602 -0
- package/src/components/Select/Select.helpers.ts +71 -0
- package/src/components/Select/Select.stories.tsx +687 -0
- package/src/components/Select/Select.sx.ts +14 -0
- package/src/components/Select/Select.tsx +429 -0
- package/src/components/Select/Select.types.ts +15 -0
- package/src/components/Select/_parts/SelectMenuItem.tsx +40 -0
- package/src/components/Select/_parts/SelectSearchHeader.tsx +51 -0
- package/src/components/Select/_parts/SelectValue.tsx +96 -0
- package/src/components/Select/index.ts +14 -0
- package/src/components/Stat/Stat.stories.tsx +85 -0
- package/src/components/Stat/Stat.tsx +117 -0
- package/src/components/Stat/index.ts +2 -0
- package/src/components/StatusMessage/StatusMessage.stories.tsx +130 -0
- package/src/components/StatusMessage/StatusMessage.tsx +162 -0
- package/src/components/StatusMessage/index.ts +2 -0
- package/src/components/Stepper/Step.tsx +21 -0
- package/src/components/Stepper/Stepper.definition.ts +75 -0
- package/src/components/Stepper/Stepper.stories.tsx +122 -0
- package/src/components/Stepper/Stepper.tsx +75 -0
- package/src/components/Stepper/index.ts +2 -0
- package/src/components/Table/EmptyTable.png +0 -0
- package/src/components/Table/Table.definition.ts +580 -0
- package/src/components/Table/Table.stories.tsx +853 -0
- package/src/components/Table/Table.tsx +495 -0
- package/src/components/Table/data.ts +134 -0
- package/src/components/Table/exportsUtils.ts +195 -0
- package/src/components/Table/index.ts +3 -0
- package/src/components/Table/types.ts +34 -0
- package/src/components/Tabs/Tab.definition.ts +53 -0
- package/src/components/Tabs/Tab.tsx +19 -0
- package/src/components/Tabs/Tabs.stories.tsx +118 -0
- package/src/components/Tabs/Tabs.tsx +99 -0
- package/src/components/Tabs/_tabUtils.tsx +4 -0
- package/src/components/Tabs/index.ts +2 -0
- package/src/components/Timeline/Timeline.definition.ts +43 -0
- package/src/components/Timeline/Timeline.stories.tsx +108 -0
- package/src/components/Timeline/Timeline.tsx +49 -0
- package/src/components/Timeline/TimelineItem.tsx +31 -0
- package/src/components/Timeline/index.ts +2 -0
- package/src/components/Tooltip/Tooltip.stories.tsx +129 -0
- package/src/components/Tooltip/Tooltip.tsx +58 -0
- package/src/components/Tooltip/index.ts +1 -0
- package/src/components/_shared/formField.sx.ts +118 -0
- package/src/components/_shared/resolvePreset.ts +35 -0
- package/src/hooks/ClipBoard/ClipBoard.stories.tsx +168 -0
- package/src/hooks/ClipBoard/ClipBoard.tsx +131 -0
- package/src/hooks/ClipBoard/ClipboardUnifiedDemo.tsx +111 -0
- package/src/hooks/ClipBoard/index.ts +1 -0
- package/src/hooks/Wizard/Wizard.stories.tsx +301 -0
- package/src/hooks/Wizard/WizardContext.tsx +166 -0
- package/src/hooks/Wizard/index.ts +6 -0
- package/src/hooks/Wizard/useWizard.ts +13 -0
- package/src/index.ts +17 -0
- package/src/mui.ts +54 -0
- package/src/styles.css +3 -0
- package/src/theme/componentStyles.ts +47 -0
- package/src/theme/tokens.ts +43 -0
- package/tailwind.config.js +10 -0
- package/tsconfig.json +48 -0
- package/tsup.config.js +41 -0
- package/vite.config.js +132 -0
- package/vitest.config.ts +35 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { Button } from "./Button";
|
|
3
|
+
import NotificationsIcon from "@mui/icons-material/Notifications";
|
|
4
|
+
import { expect, within } from "storybook/test";
|
|
5
|
+
import {
|
|
6
|
+
ContainedButtonDefinition,
|
|
7
|
+
DefaultButtonDefinition,
|
|
8
|
+
DisabledStateButtonDefinition,
|
|
9
|
+
LoadingButtonDefinition,
|
|
10
|
+
LoadingIndicatorEndButtonDefinition,
|
|
11
|
+
LoadingIndicatorStartButtonDefinition,
|
|
12
|
+
OutlinedButtonDefinition,
|
|
13
|
+
WithEndIconButtonDefinition,
|
|
14
|
+
WithLargeSizeButtonDefinition,
|
|
15
|
+
WithSmallSizeButtonDefinition,
|
|
16
|
+
WithStartIconButtonDefinition
|
|
17
|
+
} from "./Button.definition";
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
const meta: Meta<typeof Button> = {
|
|
21
|
+
title: "Components/Button",
|
|
22
|
+
component: Button,
|
|
23
|
+
parameters: {
|
|
24
|
+
layout: "centered"
|
|
25
|
+
},
|
|
26
|
+
tags: ["autodocs"]
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default meta;
|
|
30
|
+
|
|
31
|
+
type Story = StoryObj<typeof Button>;
|
|
32
|
+
|
|
33
|
+
export const Default: Story = {
|
|
34
|
+
args: {
|
|
35
|
+
children: "Button",
|
|
36
|
+
variant: "text"
|
|
37
|
+
},
|
|
38
|
+
parameters: {
|
|
39
|
+
docs: {
|
|
40
|
+
description: {
|
|
41
|
+
story: "Boton con el texto \"Button\" y el estilo \"default\"."
|
|
42
|
+
},
|
|
43
|
+
source: {
|
|
44
|
+
code: DefaultButtonDefinition.trim()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const Outlined: Story = {
|
|
51
|
+
args: {
|
|
52
|
+
children: "Button",
|
|
53
|
+
variant: "outlined"
|
|
54
|
+
},
|
|
55
|
+
parameters: {
|
|
56
|
+
docs: {
|
|
57
|
+
description: {
|
|
58
|
+
story: "Boton con el texto \"Button\" y el estilo \"outlined\"."
|
|
59
|
+
},
|
|
60
|
+
source: {
|
|
61
|
+
code: OutlinedButtonDefinition.trim()
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const Contained: Story = {
|
|
68
|
+
args: {
|
|
69
|
+
children: "Button",
|
|
70
|
+
variant: "contained"
|
|
71
|
+
},
|
|
72
|
+
parameters: {
|
|
73
|
+
docs: {
|
|
74
|
+
description: {
|
|
75
|
+
story: "Boton con el texto \"Button\" y el estilo \"contained\"."
|
|
76
|
+
},
|
|
77
|
+
source: {
|
|
78
|
+
code: ContainedButtonDefinition.trim()
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
export const WithStartIcon: Story = {
|
|
86
|
+
args: {
|
|
87
|
+
children: "Button",
|
|
88
|
+
variant: "contained",
|
|
89
|
+
startIcon: <NotificationsIcon />,
|
|
90
|
+
"data-testid": "button"
|
|
91
|
+
},
|
|
92
|
+
parameters: {
|
|
93
|
+
docs: {
|
|
94
|
+
description: {
|
|
95
|
+
story: "Boton con el texto \"Button\", el estilo \"contained\" y un icono al inicio."
|
|
96
|
+
},
|
|
97
|
+
source: {
|
|
98
|
+
code: WithStartIconButtonDefinition.trim()
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
play: async ({ canvasElement }) => {
|
|
103
|
+
const canvas = within(canvasElement);
|
|
104
|
+
const button = canvas.getByTestId("button");
|
|
105
|
+
expect(within(button).getByText("Button")).toBeInTheDocument();
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
export const WithEndIcon: Story = {
|
|
111
|
+
args: {
|
|
112
|
+
children: "Button",
|
|
113
|
+
variant: "contained",
|
|
114
|
+
endIcon: <NotificationsIcon />,
|
|
115
|
+
"data-testid": "button"
|
|
116
|
+
},
|
|
117
|
+
parameters: {
|
|
118
|
+
docs: {
|
|
119
|
+
description: {
|
|
120
|
+
story: "Boton con el texto \"Button\", el estilo \"contained\" y un icono al final."
|
|
121
|
+
},
|
|
122
|
+
source: {
|
|
123
|
+
code: WithEndIconButtonDefinition.trim()
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
play: async ({ canvasElement }) => {
|
|
128
|
+
const canvas = within(canvasElement);
|
|
129
|
+
const button = canvas.getByTestId("button");
|
|
130
|
+
expect(within(button).getByText("Button")).toBeInTheDocument();
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const WithSmallSize: Story = {
|
|
135
|
+
args: {
|
|
136
|
+
children: "Button",
|
|
137
|
+
variant: "contained",
|
|
138
|
+
size: "small",
|
|
139
|
+
"data-testid": "button"
|
|
140
|
+
},
|
|
141
|
+
parameters: {
|
|
142
|
+
docs: {
|
|
143
|
+
description: {
|
|
144
|
+
story: "Boton tamaño \"small\"."
|
|
145
|
+
},
|
|
146
|
+
source: {
|
|
147
|
+
code: WithSmallSizeButtonDefinition.trim()
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
play: async ({ canvasElement }) => {
|
|
152
|
+
const canvas = within(canvasElement);
|
|
153
|
+
const button = canvas.getByTestId("button");
|
|
154
|
+
expect(within(button).getByText("Button")).toBeInTheDocument();
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
export const WithLargeSize: Story = {
|
|
160
|
+
args: {
|
|
161
|
+
children: "Button",
|
|
162
|
+
variant: "contained",
|
|
163
|
+
size: "large",
|
|
164
|
+
"data-testid": "button"
|
|
165
|
+
},
|
|
166
|
+
parameters: {
|
|
167
|
+
docs: {
|
|
168
|
+
description: {
|
|
169
|
+
story: "Boton tamaño \"large\"."
|
|
170
|
+
},
|
|
171
|
+
source: {
|
|
172
|
+
code: WithLargeSizeButtonDefinition.trim()
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
play: async ({ canvasElement }) => {
|
|
177
|
+
const canvas = within(canvasElement);
|
|
178
|
+
const button = canvas.getByTestId("button");
|
|
179
|
+
expect(within(button).getByText("Button")).toBeInTheDocument();
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
export const Disabled: Story = {
|
|
184
|
+
args: {
|
|
185
|
+
children: "Button",
|
|
186
|
+
variant: "contained",
|
|
187
|
+
"data-testid": "button",
|
|
188
|
+
disabled: true
|
|
189
|
+
},
|
|
190
|
+
parameters: {
|
|
191
|
+
docs: {
|
|
192
|
+
description: {
|
|
193
|
+
story: "Boton deshabilitado."
|
|
194
|
+
},
|
|
195
|
+
source: {
|
|
196
|
+
code: DisabledStateButtonDefinition.trim()
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
play: async ({ canvasElement }) => {
|
|
201
|
+
const canvas = within(canvasElement);
|
|
202
|
+
const button = canvas.getByTestId("button");
|
|
203
|
+
expect(within(button).getByText("Button")).toBeInTheDocument();
|
|
204
|
+
expect(button).toBeDisabled();
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
export const Loading: Story = {
|
|
210
|
+
args: {
|
|
211
|
+
children: "Button",
|
|
212
|
+
variant: "contained",
|
|
213
|
+
loading: true,
|
|
214
|
+
"data-testid": "button"
|
|
215
|
+
},
|
|
216
|
+
parameters: {
|
|
217
|
+
docs: {
|
|
218
|
+
description: {
|
|
219
|
+
story: "Boton con loader."
|
|
220
|
+
},
|
|
221
|
+
source: {
|
|
222
|
+
code: LoadingButtonDefinition.trim()
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
play: async ({ canvasElement }) => {
|
|
227
|
+
const canvas = within(canvasElement);
|
|
228
|
+
const button = canvas.getByTestId("button");
|
|
229
|
+
expect(within(button).getByText("Button")).toBeInTheDocument();
|
|
230
|
+
expect(button).toBeDisabled();
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
export const LoadingStart: Story = {
|
|
235
|
+
args: {
|
|
236
|
+
children: "Button",
|
|
237
|
+
variant: "contained",
|
|
238
|
+
loading: true,
|
|
239
|
+
loadingPosition: "start",
|
|
240
|
+
"data-testid": "button"
|
|
241
|
+
},
|
|
242
|
+
parameters: {
|
|
243
|
+
docs: {
|
|
244
|
+
description: {
|
|
245
|
+
story: "Boton con al inicio loader."
|
|
246
|
+
},
|
|
247
|
+
source: {
|
|
248
|
+
code: LoadingIndicatorStartButtonDefinition.trim()
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
play: async ({ canvasElement }) => {
|
|
253
|
+
const canvas = within(canvasElement);
|
|
254
|
+
const button = canvas.getByTestId("button");
|
|
255
|
+
expect(within(button).getByText("Button")).toBeInTheDocument();
|
|
256
|
+
expect(button).toBeDisabled();
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
export const LoadingEnd: Story = {
|
|
262
|
+
args: {
|
|
263
|
+
children: "Button",
|
|
264
|
+
variant: "contained",
|
|
265
|
+
loading: true,
|
|
266
|
+
loadingPosition: "end",
|
|
267
|
+
"data-testid": "button"
|
|
268
|
+
},
|
|
269
|
+
parameters: {
|
|
270
|
+
docs: {
|
|
271
|
+
description: {
|
|
272
|
+
story: "Boton con al inicio loader."
|
|
273
|
+
},
|
|
274
|
+
source: {
|
|
275
|
+
code: LoadingIndicatorEndButtonDefinition.trim()
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
play: async ({ canvasElement }) => {
|
|
280
|
+
const canvas = within(canvasElement);
|
|
281
|
+
const button = canvas.getByTestId("button");
|
|
282
|
+
expect(within(button).getByText("Button")).toBeInTheDocument();
|
|
283
|
+
expect(button).toBeDisabled();
|
|
284
|
+
}
|
|
285
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Button as MuiButton } from "@mui/material";
|
|
2
|
+
import type { ButtonProps } from "@mui/material/Button";
|
|
3
|
+
import React from "react";
|
|
4
|
+
|
|
5
|
+
type PickButtonProps = Pick<ButtonProps, "href" | "children" | "onClick" | "color" | "disabled" | "className" | "endIcon" | "startIcon" | "loading" | "loadingIndicator" | "variant" | "size" | "loadingPosition" | "type" | "sx">;
|
|
6
|
+
|
|
7
|
+
export interface MyButtonProps extends PickButtonProps {
|
|
8
|
+
children?: React.ReactNode;
|
|
9
|
+
variant?: "text" | "outlined" | "contained";
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
endIcon?: React.ReactNode;
|
|
12
|
+
startIcon?: React.ReactNode;
|
|
13
|
+
size?: "small" | "medium" | "large";
|
|
14
|
+
className?: string;
|
|
15
|
+
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
16
|
+
loading?: boolean;
|
|
17
|
+
loadingIndicator?: React.ReactNode;
|
|
18
|
+
loadingPosition?: "start" | "end" | "center";
|
|
19
|
+
color?: "primary" | "success" | "error" | "info" | "warning"; //change for tailwind class colors
|
|
20
|
+
href?: string;
|
|
21
|
+
"data-testid"?: string;
|
|
22
|
+
type?: "button" | "submit" ;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const Button: React.FC<MyButtonProps> = ({
|
|
26
|
+
children,
|
|
27
|
+
variant,
|
|
28
|
+
disabled,
|
|
29
|
+
startIcon,
|
|
30
|
+
endIcon,
|
|
31
|
+
size,
|
|
32
|
+
className,
|
|
33
|
+
onClick,
|
|
34
|
+
loading,
|
|
35
|
+
loadingPosition,
|
|
36
|
+
loadingIndicator,
|
|
37
|
+
color,
|
|
38
|
+
href,
|
|
39
|
+
"data-testid": dataTestId,
|
|
40
|
+
type,
|
|
41
|
+
sx
|
|
42
|
+
}) => {
|
|
43
|
+
return (
|
|
44
|
+
<MuiButton
|
|
45
|
+
disabled={disabled}
|
|
46
|
+
variant={variant}
|
|
47
|
+
startIcon={startIcon}
|
|
48
|
+
endIcon={endIcon}
|
|
49
|
+
size={size}
|
|
50
|
+
className={className}
|
|
51
|
+
onClick={onClick}
|
|
52
|
+
loading={loading}
|
|
53
|
+
loadingPosition={loadingPosition}
|
|
54
|
+
loadingIndicator={loadingIndicator}
|
|
55
|
+
sx={{ textTransform: "none", ...sx }}
|
|
56
|
+
disableRipple={true}
|
|
57
|
+
disableElevation={true}
|
|
58
|
+
color={color}
|
|
59
|
+
data-testid={dataTestId}
|
|
60
|
+
href={href}
|
|
61
|
+
type={type}
|
|
62
|
+
>
|
|
63
|
+
{children}
|
|
64
|
+
</MuiButton>);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export default Button;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Button } from './Button';
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import { Box, Button, Chip, Stack, Typography } from '@mui/material';
|
|
4
|
+
import EditIcon from '@mui/icons-material/Edit';
|
|
5
|
+
import DeleteIcon from '@mui/icons-material/Delete';
|
|
6
|
+
import PersonIcon from '@mui/icons-material/Person';
|
|
7
|
+
|
|
8
|
+
import { Card } from './Card';
|
|
9
|
+
import { ActionMenu } from '../ActionMenu';
|
|
10
|
+
|
|
11
|
+
const meta: Meta<typeof Card> = {
|
|
12
|
+
title: 'Components/Card',
|
|
13
|
+
component: Card,
|
|
14
|
+
tags: ['autodocs'],
|
|
15
|
+
parameters: {
|
|
16
|
+
layout: 'padded',
|
|
17
|
+
docs: {
|
|
18
|
+
description: {
|
|
19
|
+
component:
|
|
20
|
+
'Contenedor semántico con slots `title` / `subtitle` / `actions` / `footer`. Reemplaza el patrón `.card` + `.card-header` + `.card-body` de Metronic con composición declarativa. Backward-compat: si solo se pasa `children`, se comporta como el Card original.',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
argTypes: {
|
|
25
|
+
variant: {
|
|
26
|
+
control: 'select',
|
|
27
|
+
options: ['elevated', 'outlined', 'plain'],
|
|
28
|
+
},
|
|
29
|
+
padding: {
|
|
30
|
+
control: 'select',
|
|
31
|
+
options: ['none', 'dense', 'normal', 'loose'],
|
|
32
|
+
},
|
|
33
|
+
clickable: { control: 'boolean' },
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default meta;
|
|
38
|
+
type Story = StoryObj<typeof Card>;
|
|
39
|
+
|
|
40
|
+
// ── Stories ──────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
export const LegacyBodyOnly: Story = {
|
|
43
|
+
args: {
|
|
44
|
+
children: 'Contenido simple del card — sin header ni footer.',
|
|
45
|
+
},
|
|
46
|
+
parameters: {
|
|
47
|
+
docs: {
|
|
48
|
+
description: {
|
|
49
|
+
story:
|
|
50
|
+
'Modo backward-compat: pasando solo `children` el Card se comporta como el original (body con padding, sombra sutil).',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const WithTitleAndSubtitle: Story = {
|
|
57
|
+
args: {
|
|
58
|
+
title: 'Datos del cliente',
|
|
59
|
+
subtitle: 'Última actualización: hace 2 horas',
|
|
60
|
+
children: (
|
|
61
|
+
<Typography variant="body2" color="text.secondary">
|
|
62
|
+
Aquí va el contenido del card, por ejemplo un formulario o una tabla.
|
|
63
|
+
</Typography>
|
|
64
|
+
),
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const WithActions: Story = {
|
|
69
|
+
args: {
|
|
70
|
+
title: 'Afiliación #12458',
|
|
71
|
+
subtitle: 'Aprobada el 10 abril 2026',
|
|
72
|
+
actions: (
|
|
73
|
+
<ActionMenu
|
|
74
|
+
items={[
|
|
75
|
+
{ label: 'Editar', icon: <EditIcon fontSize="small" />, onClick: () => {} },
|
|
76
|
+
{
|
|
77
|
+
label: 'Eliminar',
|
|
78
|
+
icon: <DeleteIcon fontSize="small" />,
|
|
79
|
+
onClick: () => {},
|
|
80
|
+
danger: true,
|
|
81
|
+
dividerBefore: true,
|
|
82
|
+
},
|
|
83
|
+
]}
|
|
84
|
+
/>
|
|
85
|
+
),
|
|
86
|
+
children: (
|
|
87
|
+
<Stack direction="row" spacing={2} alignItems="center">
|
|
88
|
+
<PersonIcon color="action" />
|
|
89
|
+
<Box>
|
|
90
|
+
<Typography variant="body2" sx={{ fontWeight: 600 }}>
|
|
91
|
+
Andrea Pineda
|
|
92
|
+
</Typography>
|
|
93
|
+
<Typography variant="caption" color="text.secondary">
|
|
94
|
+
andrea@soyfri.com
|
|
95
|
+
</Typography>
|
|
96
|
+
</Box>
|
|
97
|
+
<Chip label="Activo" color="success" size="small" sx={{ ml: 'auto' }} />
|
|
98
|
+
</Stack>
|
|
99
|
+
),
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export const WithFooter: Story = {
|
|
104
|
+
args: {
|
|
105
|
+
title: 'Nueva afiliación',
|
|
106
|
+
children: (
|
|
107
|
+
<Stack spacing={1}>
|
|
108
|
+
<Typography variant="body2">
|
|
109
|
+
Confirma los datos antes de enviar la solicitud.
|
|
110
|
+
</Typography>
|
|
111
|
+
<Typography variant="caption" color="text.secondary">
|
|
112
|
+
Campos marcados con * son obligatorios.
|
|
113
|
+
</Typography>
|
|
114
|
+
</Stack>
|
|
115
|
+
),
|
|
116
|
+
footer: (
|
|
117
|
+
<>
|
|
118
|
+
<Button variant="outlined" color="secondary">
|
|
119
|
+
Cancelar
|
|
120
|
+
</Button>
|
|
121
|
+
<Button variant="contained">Enviar</Button>
|
|
122
|
+
</>
|
|
123
|
+
),
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export const OutlinedVariant: Story = {
|
|
128
|
+
args: {
|
|
129
|
+
variant: 'outlined',
|
|
130
|
+
title: 'Card outlined',
|
|
131
|
+
subtitle: 'Sin sombra, solo borde',
|
|
132
|
+
children: (
|
|
133
|
+
<Typography variant="body2">
|
|
134
|
+
Útil cuando el card vive sobre un fondo con elevation mayor, o para
|
|
135
|
+
evitar stacked shadows en listas densas.
|
|
136
|
+
</Typography>
|
|
137
|
+
),
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export const PlainVariant: Story = {
|
|
142
|
+
args: {
|
|
143
|
+
variant: 'plain',
|
|
144
|
+
title: 'Card plain',
|
|
145
|
+
subtitle: 'Sin borde ni sombra',
|
|
146
|
+
children: (
|
|
147
|
+
<Typography variant="body2">
|
|
148
|
+
Perfecto para composición interna — por ejemplo, cuando un card va
|
|
149
|
+
dentro de otro y quieres evitar doble sombra.
|
|
150
|
+
</Typography>
|
|
151
|
+
),
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export const Clickable: Story = {
|
|
156
|
+
args: {
|
|
157
|
+
clickable: true,
|
|
158
|
+
title: 'Haz click en este card',
|
|
159
|
+
subtitle: 'Fíjate en el hover',
|
|
160
|
+
children: (
|
|
161
|
+
<Typography variant="body2" color="text.secondary">
|
|
162
|
+
El cursor cambia y aparece un lift sutil en hover. Se dispara `onClick`.
|
|
163
|
+
</Typography>
|
|
164
|
+
),
|
|
165
|
+
onClick: () => console.log('card clicked'),
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
export const DensePadding: Story = {
|
|
170
|
+
args: {
|
|
171
|
+
padding: 'dense',
|
|
172
|
+
title: 'Card denso',
|
|
173
|
+
subtitle: 'Padding reducido',
|
|
174
|
+
children: (
|
|
175
|
+
<Stack spacing={0.5}>
|
|
176
|
+
<Typography variant="body2">Línea 1</Typography>
|
|
177
|
+
<Typography variant="body2">Línea 2</Typography>
|
|
178
|
+
<Typography variant="body2">Línea 3</Typography>
|
|
179
|
+
</Stack>
|
|
180
|
+
),
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
export const CustomHeader: Story = {
|
|
185
|
+
args: {
|
|
186
|
+
header: (
|
|
187
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5, width: '100%' }}>
|
|
188
|
+
<Box
|
|
189
|
+
sx={{
|
|
190
|
+
width: 40,
|
|
191
|
+
height: 40,
|
|
192
|
+
borderRadius: 2,
|
|
193
|
+
bgcolor: 'primary.main',
|
|
194
|
+
color: 'primary.contrastText',
|
|
195
|
+
display: 'flex',
|
|
196
|
+
alignItems: 'center',
|
|
197
|
+
justifyContent: 'center',
|
|
198
|
+
fontWeight: 700,
|
|
199
|
+
}}
|
|
200
|
+
>
|
|
201
|
+
42
|
|
202
|
+
</Box>
|
|
203
|
+
<Box sx={{ flex: 1 }}>
|
|
204
|
+
<Typography variant="subtitle1" sx={{ fontWeight: 700 }}>
|
|
205
|
+
Header totalmente custom
|
|
206
|
+
</Typography>
|
|
207
|
+
<Typography variant="caption" color="text.secondary">
|
|
208
|
+
Tomas control del slot entero
|
|
209
|
+
</Typography>
|
|
210
|
+
</Box>
|
|
211
|
+
<Chip label="v2" size="small" variant="outlined" />
|
|
212
|
+
</Box>
|
|
213
|
+
),
|
|
214
|
+
children: (
|
|
215
|
+
<Typography variant="body2">
|
|
216
|
+
Cuando `header` se usa, los props `title`/`subtitle`/`actions` son
|
|
217
|
+
ignorados.
|
|
218
|
+
</Typography>
|
|
219
|
+
),
|
|
220
|
+
},
|
|
221
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { SxProps, Theme } from '@mui/material/styles';
|
|
2
|
+
|
|
3
|
+
export type CardVariant = 'elevated' | 'outlined' | 'plain';
|
|
4
|
+
export type CardPadding = 'none' | 'dense' | 'normal' | 'loose';
|
|
5
|
+
|
|
6
|
+
export interface BuildCardSxArgs {
|
|
7
|
+
variant: CardVariant;
|
|
8
|
+
padding: CardPadding;
|
|
9
|
+
clickable: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const paddingMap: Record<CardPadding, number> = {
|
|
13
|
+
none: 0,
|
|
14
|
+
dense: 1.5,
|
|
15
|
+
normal: 2.5,
|
|
16
|
+
loose: 4,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Estilo base del Card (root). La props `sx` del consumer se compone encima
|
|
21
|
+
* junto al preset resuelto (en Card.tsx). Este builder solo se ocupa del
|
|
22
|
+
* variant + padding, para que siga siendo predecible.
|
|
23
|
+
*/
|
|
24
|
+
export function buildCardSx({
|
|
25
|
+
variant,
|
|
26
|
+
padding,
|
|
27
|
+
clickable,
|
|
28
|
+
}: BuildCardSxArgs): SxProps<Theme> {
|
|
29
|
+
return (theme) => ({
|
|
30
|
+
borderRadius: 2,
|
|
31
|
+
backgroundColor: 'background.paper',
|
|
32
|
+
boxShadow:
|
|
33
|
+
variant === 'elevated'
|
|
34
|
+
? theme.shadows[1]
|
|
35
|
+
: 'none',
|
|
36
|
+
border:
|
|
37
|
+
variant === 'outlined'
|
|
38
|
+
? `1px solid ${theme.palette.divider}`
|
|
39
|
+
: 'none',
|
|
40
|
+
transition: theme.transitions.create(
|
|
41
|
+
['box-shadow', 'transform', 'border-color'],
|
|
42
|
+
{ duration: theme.transitions.duration.shorter },
|
|
43
|
+
),
|
|
44
|
+
cursor: clickable ? 'pointer' : 'default',
|
|
45
|
+
...(clickable && {
|
|
46
|
+
'&:hover': {
|
|
47
|
+
boxShadow:
|
|
48
|
+
variant === 'elevated'
|
|
49
|
+
? theme.shadows[3]
|
|
50
|
+
: variant === 'outlined'
|
|
51
|
+
? theme.shadows[1]
|
|
52
|
+
: 'none',
|
|
53
|
+
transform: variant !== 'plain' ? 'translateY(-1px)' : 'none',
|
|
54
|
+
},
|
|
55
|
+
'&:active': {
|
|
56
|
+
transform: 'translateY(0)',
|
|
57
|
+
},
|
|
58
|
+
}),
|
|
59
|
+
// Body padding (cuando no hay header/footer envolventes). Para el caso
|
|
60
|
+
// compuesto los slots manejan su propio padding, dejamos el root en 0.
|
|
61
|
+
p: paddingMap[padding],
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function buildCardHeaderSx(): SxProps<Theme> {
|
|
66
|
+
return (theme) => ({
|
|
67
|
+
display: 'flex',
|
|
68
|
+
alignItems: 'center',
|
|
69
|
+
gap: 1.5,
|
|
70
|
+
px: 2.5,
|
|
71
|
+
py: 1.75,
|
|
72
|
+
borderBottom: `1px solid ${theme.palette.divider}`,
|
|
73
|
+
'& .card-header-text': {
|
|
74
|
+
flex: 1,
|
|
75
|
+
minWidth: 0,
|
|
76
|
+
},
|
|
77
|
+
'& .card-header-actions': {
|
|
78
|
+
display: 'flex',
|
|
79
|
+
alignItems: 'center',
|
|
80
|
+
gap: 0.5,
|
|
81
|
+
flexShrink: 0,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function buildCardBodySx(padding: CardPadding): SxProps<Theme> {
|
|
87
|
+
return {
|
|
88
|
+
px: padding === 'none' ? 0 : padding === 'dense' ? 1.5 : padding === 'loose' ? 4 : 2.5,
|
|
89
|
+
py: padding === 'none' ? 0 : padding === 'dense' ? 1.5 : padding === 'loose' ? 4 : 2.5,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function buildCardFooterSx(): SxProps<Theme> {
|
|
94
|
+
return (theme) => ({
|
|
95
|
+
display: 'flex',
|
|
96
|
+
alignItems: 'center',
|
|
97
|
+
justifyContent: 'flex-end',
|
|
98
|
+
gap: 1,
|
|
99
|
+
px: 2.5,
|
|
100
|
+
py: 1.5,
|
|
101
|
+
borderTop: `1px solid ${theme.palette.divider}`,
|
|
102
|
+
backgroundColor: theme.palette.action.hover,
|
|
103
|
+
});
|
|
104
|
+
}
|