@recursica/adapter-mui-v7 1.3.2 → 1.3.3

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 (73) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +1 -1
  3. package/dist/adapter-mui-v7.cjs +16 -16
  4. package/dist/adapter-mui-v7.cjs.map +1 -1
  5. package/dist/adapter-mui-v7.js +902 -880
  6. package/dist/adapter-mui-v7.js.map +1 -1
  7. package/dist/index.d.ts +8 -5
  8. package/docs/PHILOSOPHY.md +4 -0
  9. package/package.json +4 -8
  10. package/src/components/Heading/HEADING_IMPLEMENTATION_NOTES.md +10 -0
  11. package/src/components/Panel/PANEL_IMPLEMENTATION_NOTES.md +8 -0
  12. package/src/components/Panel/Panel.tsx +51 -9
  13. package/src/components/Text/TEXT_IMPLEMENTATION_NOTES.md +12 -0
  14. package/src/components/Text/Text.headings.dom.test.tsx +54 -0
  15. package/src/components/Text/Text.tsx +16 -0
  16. package/.storybook/commonArgTypes.ts +0 -148
  17. package/recursica_brand.json +0 -2610
  18. package/recursica_tokens.json +0 -769
  19. package/recursica_ui-kit.json +0 -9049
  20. package/recursica_variables_scoped.css +0 -6178
  21. package/src/Introduction.stories.tsx +0 -178
  22. package/src/components/Accordion/Accordion.stories.tsx +0 -198
  23. package/src/components/AssistiveElement/AssistiveElement.stories.tsx +0 -68
  24. package/src/components/AutoComplete/AutoComplete.stories.tsx +0 -336
  25. package/src/components/Avatar/Avatar.stories.tsx +0 -92
  26. package/src/components/Badge/Badge.stories.tsx +0 -78
  27. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +0 -114
  28. package/src/components/Button/Button.stories.tsx +0 -196
  29. package/src/components/Card/Card.stories.tsx +0 -132
  30. package/src/components/Checkbox/Checkbox.stories.tsx +0 -97
  31. package/src/components/Checkbox/CheckboxGroup.stories.tsx +0 -139
  32. package/src/components/Chip/Chip.stories.tsx +0 -154
  33. package/src/components/Container/Container.stories.tsx +0 -118
  34. package/src/components/DatePicker/DatePicker.stories.tsx +0 -163
  35. package/src/components/Dropdown/Dropdown.stories.tsx +0 -252
  36. package/src/components/FileInput/FileInput.stories.tsx +0 -313
  37. package/src/components/FileUpload/FileUpload.stories.tsx +0 -362
  38. package/src/components/Flex/Flex.stories.tsx +0 -128
  39. package/src/components/FormControlLayout/FormControlLayout.stories.tsx +0 -118
  40. package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +0 -129
  41. package/src/components/Group/Group.stories.tsx +0 -120
  42. package/src/components/Heading/Heading.stories.tsx +0 -112
  43. package/src/components/HoverCard/HoverCard.stories.tsx +0 -172
  44. package/src/components/Label/Label.stories.tsx +0 -131
  45. package/src/components/LayoutGrid/LayoutGrid.stories.tsx +0 -61
  46. package/src/components/Link/Link.stories.tsx +0 -58
  47. package/src/components/Loader/Loader.stories.tsx +0 -123
  48. package/src/components/Menu/Menu.stories.tsx +0 -402
  49. package/src/components/Modal/Modal.stories.tsx +0 -105
  50. package/src/components/NumberInput/NumberInput.stories.tsx +0 -96
  51. package/src/components/Pagination/Pagination.stories.tsx +0 -48
  52. package/src/components/Panel/Panel.stories.tsx +0 -227
  53. package/src/components/Popover/Popover.stories.tsx +0 -133
  54. package/src/components/Radio/Radio.stories.tsx +0 -93
  55. package/src/components/Radio/RadioGroup.stories.tsx +0 -117
  56. package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +0 -163
  57. package/src/components/SegmentedControl/SegmentedControl.stories.tsx +0 -106
  58. package/src/components/Slider/Slider.stories.tsx +0 -282
  59. package/src/components/Stack/Stack.stories.tsx +0 -108
  60. package/src/components/Stepper/Stepper.stories.tsx +0 -157
  61. package/src/components/Switch/Switch.stories.tsx +0 -114
  62. package/src/components/Switch/SwitchGroup.stories.tsx +0 -132
  63. package/src/components/Table/Table.stories.tsx +0 -154
  64. package/src/components/Tabs/Tabs.stories.tsx +0 -174
  65. package/src/components/Text/Text.stories.tsx +0 -123
  66. package/src/components/TextArea/TextArea.stories.tsx +0 -94
  67. package/src/components/TextField/TextField.stories.tsx +0 -162
  68. package/src/components/TimePicker/TimePicker.stories.tsx +0 -155
  69. package/src/components/Timeline/Timeline.stories.tsx +0 -128
  70. package/src/components/Toast/Toast.stories.tsx +0 -67
  71. package/src/components/Tooltip/Tooltip.stories.tsx +0 -165
  72. package/src/components/TransferList/TransferList.stories.tsx +0 -121
  73. package/src/components/Tree/Tree.stories.tsx +0 -104
@@ -1,313 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react";
2
- import React, { useState } from "react";
3
- import { FileInput } from "./FileInput";
4
- import { type RecursicaFileUploadItem } from "@recursica/adapter-common";
5
- import { formControlArgTypes } from "../../../.storybook/commonArgTypes";
6
-
7
- function mockFile(name: string, size = 1024) {
8
- return new File([new Uint8Array(size)], name);
9
- }
10
-
11
- const meta: Meta<typeof FileInput> = {
12
- title: "UI-Kit/FileInput",
13
- component: FileInput,
14
- tags: ["autodocs"],
15
- parameters: {
16
- docs: {
17
- description: {
18
- component: `
19
- The \`FileInput\` component is a single-line, \`TextField\`-shaped control for choosing files, integrated directly into the \`FormControlWrapper\` architecture. It shares \`FileUpload\`'s selection/validation interface (\`accept\`/\`maxSize\`/\`maxFiles\`, \`readOnly\`) behind a different presentation — every selected file renders as a removable chip in a horizontally scrollable row, and a trailing \`Button\` clears the current selection.
20
-
21
- ### Examples
22
- \`\`\`tsx
23
- <FileInput
24
- label="Resume"
25
- files={files}
26
- onFilesAdded={(added) => setFiles(added.map((file) => ({ file })))}
27
- onFileRemove={() => setFiles([])}
28
- />
29
- \`\`\`
30
- `,
31
- },
32
- },
33
- },
34
- argTypes: {
35
- ...formControlArgTypes,
36
- accept: {
37
- control: "text",
38
- description:
39
- "Native `accept` attribute for the file picker (e.g. `.pdf,.png`).",
40
- },
41
- multiple: {
42
- control: "boolean",
43
- description:
44
- "Whether more than one file can be selected/dropped at once. Defaults to `false`.",
45
- },
46
- maxSize: {
47
- control: "number",
48
- description: "Maximum size per file, in bytes.",
49
- },
50
- maxFiles: {
51
- control: "number",
52
- description:
53
- "Maximum total number of files allowed. Only meaningful when `multiple` is `true`.",
54
- },
55
- readOnly: {
56
- control: "boolean",
57
- description:
58
- "Renders `files` as a static, non-interactive display with no clear/remove icons.",
59
- },
60
- placeholder: {
61
- control: "text",
62
- },
63
- icon: {
64
- table: { disable: true },
65
- },
66
- clearIcon: {
67
- table: { disable: true },
68
- },
69
- files: {
70
- table: { disable: true },
71
- },
72
- onFilesAdded: {
73
- table: { disable: true },
74
- },
75
- onFileRemove: {
76
- table: { disable: true },
77
- },
78
- onFilesRejected: {
79
- table: { disable: true },
80
- },
81
- },
82
- };
83
-
84
- export default meta;
85
-
86
- type Story = StoryObj<typeof FileInput>;
87
- type FileInputStoryArgs = React.ComponentProps<typeof FileInput> & {
88
- withLayer?: boolean;
89
- layer?: number;
90
- };
91
-
92
- export const Default: Story = {
93
- args: {
94
- label: "Resume",
95
- },
96
- /* eslint-disable @typescript-eslint/no-unused-vars */
97
- render: function FileInputStory({
98
- withLayer,
99
- layer,
100
- ...args
101
- }: FileInputStoryArgs) {
102
- /* eslint-enable @typescript-eslint/no-unused-vars */
103
- const [files, setFiles] = useState<RecursicaFileUploadItem[]>([]);
104
- return (
105
- <FileInput
106
- {...args}
107
- files={files}
108
- onFilesAdded={(added) => setFiles(added.map((file) => ({ file })))}
109
- onFileRemove={() => setFiles([])}
110
- />
111
- );
112
- },
113
- };
114
-
115
- export const WithFile: Story = {
116
- args: {
117
- label: "Resume",
118
- },
119
- /* eslint-disable @typescript-eslint/no-unused-vars */
120
- render: function FileInputStory({
121
- withLayer,
122
- layer,
123
- ...args
124
- }: FileInputStoryArgs) {
125
- /* eslint-enable @typescript-eslint/no-unused-vars */
126
- const [files, setFiles] = useState<RecursicaFileUploadItem[]>([
127
- { file: mockFile("resume.pdf") },
128
- ]);
129
- return (
130
- <FileInput
131
- {...args}
132
- files={files}
133
- onFilesAdded={(added) => setFiles(added.map((file) => ({ file })))}
134
- onFileRemove={() => setFiles([])}
135
- />
136
- );
137
- },
138
- };
139
-
140
- export const MultipleFiles: Story = {
141
- args: {
142
- label: "Attachments",
143
- assistiveText: "Up to 5 files",
144
- multiple: true,
145
- },
146
- /* eslint-disable @typescript-eslint/no-unused-vars */
147
- render: function FileInputStory({
148
- withLayer,
149
- layer,
150
- ...args
151
- }: FileInputStoryArgs) {
152
- /* eslint-enable @typescript-eslint/no-unused-vars */
153
- const [files, setFiles] = useState<RecursicaFileUploadItem[]>([
154
- { file: mockFile("document.pdf") },
155
- { file: mockFile("image.png") },
156
- { file: mockFile("spreadsheet.xlsx") },
157
- ]);
158
- return (
159
- <FileInput
160
- {...args}
161
- files={files}
162
- onFilesAdded={(added) =>
163
- setFiles((prev) => [...prev, ...added.map((file) => ({ file }))])
164
- }
165
- onFileRemove={(id) =>
166
- setFiles((prev) =>
167
- prev.filter((item) => (item.id ?? item.file.name) !== id),
168
- )
169
- }
170
- />
171
- );
172
- },
173
- };
174
-
175
- export const SideBySide: Story = {
176
- args: {
177
- label: "Resume",
178
- assistiveText: "PDF or Word document",
179
- formLayout: "side-by-side",
180
- },
181
- /* eslint-disable @typescript-eslint/no-unused-vars */
182
- render: function FileInputStory({
183
- withLayer,
184
- layer,
185
- ...args
186
- }: FileInputStoryArgs) {
187
- /* eslint-enable @typescript-eslint/no-unused-vars */
188
- const [files, setFiles] = useState<RecursicaFileUploadItem[]>([]);
189
- return (
190
- <FileInput
191
- {...args}
192
- files={files}
193
- onFilesAdded={(added) => setFiles(added.map((file) => ({ file })))}
194
- onFileRemove={() => setFiles([])}
195
- />
196
- );
197
- },
198
- };
199
-
200
- export const Disabled: Story = {
201
- args: {
202
- label: "Resume",
203
- disabled: true,
204
- },
205
- /* eslint-disable @typescript-eslint/no-unused-vars */
206
- render: function FileInputStory({
207
- withLayer,
208
- layer,
209
- ...args
210
- }: FileInputStoryArgs) {
211
- /* eslint-enable @typescript-eslint/no-unused-vars */
212
- const [files] = useState<RecursicaFileUploadItem[]>([
213
- { file: mockFile("resume.pdf") },
214
- ]);
215
- return <FileInput {...args} files={files} />;
216
- },
217
- };
218
-
219
- export const ErrorState: Story = {
220
- args: {
221
- label: "Resume",
222
- error: "A file is required.",
223
- },
224
- };
225
-
226
- export const ReadOnly: Story = {
227
- args: {
228
- label: "Attachments",
229
- assistiveText: "Submitted files cannot be changed",
230
- multiple: true,
231
- readOnly: true,
232
- },
233
- /* eslint-disable @typescript-eslint/no-unused-vars */
234
- render: function FileInputStory({
235
- withLayer,
236
- layer,
237
- ...args
238
- }: FileInputStoryArgs) {
239
- /* eslint-enable @typescript-eslint/no-unused-vars */
240
- const [files] = useState<RecursicaFileUploadItem[]>([
241
- { file: mockFile("document.pdf") },
242
- { file: mockFile("image.png") },
243
- ]);
244
- return <FileInput {...args} files={files} />;
245
- },
246
- };
247
-
248
- // Demonstrates that `accept` isn't just cosmetic on the native file-picker dialog — files that
249
- // don't match it are rejected the same way whether picked or dropped directly (drag-and-drop
250
- // bypasses the native `accept` filtering entirely, so this only works because FileInput
251
- // re-validates it itself, same as FileUpload). Drag a non-.pdf file onto the control to see it
252
- // switch into its error state with the default `invalidFileTypeMessage`.
253
- export const AcceptRestriction: Story = {
254
- args: {
255
- label: "Resume",
256
- assistiveText: "Only .pdf files are accepted",
257
- accept: ".pdf",
258
- },
259
- /* eslint-disable @typescript-eslint/no-unused-vars */
260
- render: function FileInputStory({
261
- withLayer,
262
- layer,
263
- ...args
264
- }: FileInputStoryArgs) {
265
- /* eslint-enable @typescript-eslint/no-unused-vars */
266
- const [files, setFiles] = useState<RecursicaFileUploadItem[]>([]);
267
- return (
268
- <FileInput
269
- {...args}
270
- files={files}
271
- onFilesAdded={(added) => setFiles(added.map((file) => ({ file })))}
272
- onFileRemove={() => setFiles([])}
273
- />
274
- );
275
- },
276
- };
277
-
278
- // Demonstrates `maxFiles` in multiple-file mode: already at the 2-file cap below, so
279
- // dropping/picking another file is rejected with the default `maxFilesMessage`.
280
- export const MaxFilesRestriction: Story = {
281
- args: {
282
- label: "Attachments",
283
- assistiveText: "Up to 2 files allowed",
284
- multiple: true,
285
- maxFiles: 2,
286
- },
287
- /* eslint-disable @typescript-eslint/no-unused-vars */
288
- render: function FileInputStory({
289
- withLayer,
290
- layer,
291
- ...args
292
- }: FileInputStoryArgs) {
293
- /* eslint-enable @typescript-eslint/no-unused-vars */
294
- const [files, setFiles] = useState<RecursicaFileUploadItem[]>([
295
- { file: mockFile("document.pdf") },
296
- { file: mockFile("image.png") },
297
- ]);
298
- return (
299
- <FileInput
300
- {...args}
301
- files={files}
302
- onFilesAdded={(added) =>
303
- setFiles((prev) => [...prev, ...added.map((file) => ({ file }))])
304
- }
305
- onFileRemove={(id) =>
306
- setFiles((prev) =>
307
- prev.filter((item) => (item.id ?? item.file.name) !== id),
308
- )
309
- }
310
- />
311
- );
312
- },
313
- };
@@ -1,362 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react";
2
- import React, { useState } from "react";
3
- import { FileUpload } from "./FileUpload";
4
- import { type RecursicaFileUploadItem } from "@recursica/adapter-common";
5
- import { formControlArgTypes } from "../../../.storybook/commonArgTypes";
6
-
7
- function mockFile(name: string, size = 1024) {
8
- return new File([new Uint8Array(size)], name);
9
- }
10
-
11
- const meta: Meta<typeof FileUpload> = {
12
- title: "UI-Kit/FileUpload",
13
- component: FileUpload,
14
- tags: ["autodocs"],
15
- parameters: {
16
- docs: {
17
- description: {
18
- component: `
19
- The \`FileUpload\` component is a drag-and-drop dropzone with a native browse-button fallback, integrated directly into the \`FormControlWrapper\` architecture. Selected files render as a removable-chip list below the dropzone.
20
-
21
- ### Examples
22
- \`\`\`tsx
23
- <FileUpload
24
- label="Upload Files"
25
- assistiveText="Max file size 5MB"
26
- files={files}
27
- onFilesAdded={(added) =>
28
- setFiles((prev) => [...prev, ...added.map((file) => ({ file }))])
29
- }
30
- onFileRemove={(id) =>
31
- setFiles((prev) => prev.filter((item) => (item.id ?? item.file.name) !== id))
32
- }
33
- />
34
- \`\`\`
35
- `,
36
- },
37
- },
38
- },
39
- argTypes: {
40
- ...formControlArgTypes,
41
- accept: {
42
- control: "text",
43
- description:
44
- "Native `accept` attribute for the file picker (e.g. `.pdf,.png`).",
45
- },
46
- multiple: {
47
- control: "boolean",
48
- description: "Whether multiple files can be selected/dropped at once.",
49
- },
50
- maxSize: {
51
- control: "number",
52
- description: "Maximum size per file, in bytes.",
53
- },
54
- maxFiles: {
55
- control: "number",
56
- description: "Maximum total number of files allowed.",
57
- },
58
- readOnly: {
59
- control: "boolean",
60
- description:
61
- "Renders `files` as a static chip list with no remove icon, and hides the dropzone.",
62
- },
63
- dropzoneLabel: {
64
- control: "text",
65
- },
66
- browseButtonLabel: {
67
- control: "text",
68
- },
69
- icon: {
70
- table: { disable: true },
71
- },
72
- files: {
73
- table: { disable: true },
74
- },
75
- onFilesAdded: {
76
- table: { disable: true },
77
- },
78
- onFileRemove: {
79
- table: { disable: true },
80
- },
81
- onFilesRejected: {
82
- table: { disable: true },
83
- },
84
- },
85
- };
86
-
87
- export default meta;
88
-
89
- type Story = StoryObj<typeof FileUpload>;
90
- type FileUploadStoryArgs = React.ComponentProps<typeof FileUpload> & {
91
- withLayer?: boolean;
92
- layer?: number;
93
- };
94
-
95
- export const Default: Story = {
96
- args: {
97
- label: "Upload Files",
98
- assistiveText: "Max file size 5MB",
99
- },
100
- /* eslint-disable @typescript-eslint/no-unused-vars */
101
- render: function FileUploadStory({
102
- withLayer,
103
- layer,
104
- ...args
105
- }: FileUploadStoryArgs) {
106
- /* eslint-enable @typescript-eslint/no-unused-vars */
107
- const [files, setFiles] = useState<RecursicaFileUploadItem[]>([]);
108
- return (
109
- <FileUpload
110
- {...args}
111
- files={files}
112
- onFilesAdded={(added) =>
113
- setFiles((prev) => [...prev, ...added.map((file) => ({ file }))])
114
- }
115
- onFileRemove={(id) =>
116
- setFiles((prev) =>
117
- prev.filter((item) => (item.id ?? item.file.name) !== id),
118
- )
119
- }
120
- />
121
- );
122
- },
123
- };
124
-
125
- export const WithFiles: Story = {
126
- args: {
127
- label: "Upload Files",
128
- assistiveText: "Max file size 5MB",
129
- },
130
- /* eslint-disable @typescript-eslint/no-unused-vars */
131
- render: function FileUploadStory({
132
- withLayer,
133
- layer,
134
- ...args
135
- }: FileUploadStoryArgs) {
136
- /* eslint-enable @typescript-eslint/no-unused-vars */
137
- const [files, setFiles] = useState<RecursicaFileUploadItem[]>([
138
- { file: mockFile("document.pdf") },
139
- { file: mockFile("image.png") },
140
- { file: mockFile("spreadsheet.xlsx") },
141
- ]);
142
- return (
143
- <FileUpload
144
- {...args}
145
- files={files}
146
- onFilesAdded={(added) =>
147
- setFiles((prev) => [...prev, ...added.map((file) => ({ file }))])
148
- }
149
- onFileRemove={(id) =>
150
- setFiles((prev) =>
151
- prev.filter((item) => (item.id ?? item.file.name) !== id),
152
- )
153
- }
154
- />
155
- );
156
- },
157
- };
158
-
159
- export const EmptyState: Story = {
160
- args: {
161
- label: "Upload Files",
162
- },
163
- };
164
-
165
- export const Disabled: Story = {
166
- args: {
167
- label: "Upload Files",
168
- disabled: true,
169
- },
170
- /* eslint-disable @typescript-eslint/no-unused-vars */
171
- render: function FileUploadStory({
172
- withLayer,
173
- layer,
174
- ...args
175
- }: FileUploadStoryArgs) {
176
- /* eslint-enable @typescript-eslint/no-unused-vars */
177
- const [files] = useState<RecursicaFileUploadItem[]>([
178
- { file: mockFile("document.pdf") },
179
- ]);
180
- return <FileUpload {...args} files={files} />;
181
- },
182
- };
183
-
184
- export const ErrorState: Story = {
185
- args: {
186
- label: "Upload Files",
187
- error: "File upload failed. Please try again.",
188
- },
189
- };
190
-
191
- function StarIcon() {
192
- return (
193
- <svg
194
- xmlns="http://www.w3.org/2000/svg"
195
- viewBox="0 0 24 24"
196
- fill="currentColor"
197
- aria-hidden
198
- >
199
- <path d="M12 2l2.9 6.6 7.1.6-5.4 4.7 1.6 7-6.2-3.8-6.2 3.8 1.6-7L2 9.2l7.1-.6z" />
200
- </svg>
201
- );
202
- }
203
-
204
- export const CustomIcon: Story = {
205
- args: {
206
- label: "Upload Files",
207
- icon: <StarIcon />,
208
- },
209
- /* eslint-disable @typescript-eslint/no-unused-vars */
210
- render: function FileUploadStory({
211
- withLayer,
212
- layer,
213
- ...args
214
- }: FileUploadStoryArgs) {
215
- /* eslint-enable @typescript-eslint/no-unused-vars */
216
- const [files, setFiles] = useState<RecursicaFileUploadItem[]>([]);
217
- return (
218
- <FileUpload
219
- {...args}
220
- files={files}
221
- onFilesAdded={(added) =>
222
- setFiles((prev) => [...prev, ...added.map((file) => ({ file }))])
223
- }
224
- onFileRemove={(id) =>
225
- setFiles((prev) =>
226
- prev.filter((item) => (item.id ?? item.file.name) !== id),
227
- )
228
- }
229
- />
230
- );
231
- },
232
- };
233
-
234
- export const LongFilenames: Story = {
235
- args: {
236
- label: "Upload Files",
237
- },
238
- /* eslint-disable @typescript-eslint/no-unused-vars */
239
- render: function FileUploadStory({
240
- withLayer,
241
- layer,
242
- ...args
243
- }: FileUploadStoryArgs) {
244
- /* eslint-enable @typescript-eslint/no-unused-vars */
245
- const [files, setFiles] = useState<RecursicaFileUploadItem[]>([
246
- {
247
- file: mockFile("quarterly-financial-report-final-version-approved.pdf"),
248
- },
249
- { file: mockFile("2026-08-team-offsite-photos-and-notes.zip") },
250
- { file: mockFile("resume.docx") },
251
- ]);
252
- return (
253
- <FileUpload
254
- {...args}
255
- files={files}
256
- onFilesAdded={(added) =>
257
- setFiles((prev) => [...prev, ...added.map((file) => ({ file }))])
258
- }
259
- onFileRemove={(id) =>
260
- setFiles((prev) =>
261
- prev.filter((item) => (item.id ?? item.file.name) !== id),
262
- )
263
- }
264
- />
265
- );
266
- },
267
- };
268
-
269
- export const ReadOnly: Story = {
270
- args: {
271
- label: "Upload Files",
272
- assistiveText: "Submitted files cannot be changed",
273
- readOnly: true,
274
- },
275
- /* eslint-disable @typescript-eslint/no-unused-vars */
276
- render: function FileUploadStory({
277
- withLayer,
278
- layer,
279
- ...args
280
- }: FileUploadStoryArgs) {
281
- /* eslint-enable @typescript-eslint/no-unused-vars */
282
- const [files] = useState<RecursicaFileUploadItem[]>([
283
- { file: mockFile("document.pdf") },
284
- { file: mockFile("image.png") },
285
- ]);
286
- return <FileUpload {...args} files={files} />;
287
- },
288
- };
289
-
290
- // Demonstrates that `accept` isn't just cosmetic on the browse-button dialog — files that don't
291
- // match it are rejected the same way whether picked via Browse or dropped directly (drag-and-drop
292
- // bypasses the native `accept` filtering entirely, so this only works because FileUpload
293
- // re-validates it itself). Drag a non-.pdf/.png file onto the dropzone (or edit `accept` in the
294
- // controls) to see the control switch into its error state with the default
295
- // `invalidFileTypeMessage` ("File type not accepted") instead of accepting the file.
296
- export const AcceptRestriction: Story = {
297
- args: {
298
- label: "Upload Files",
299
- assistiveText: "Only .pdf and .png files are accepted",
300
- accept: ".pdf,.png",
301
- },
302
- /* eslint-disable @typescript-eslint/no-unused-vars */
303
- render: function FileUploadStory({
304
- withLayer,
305
- layer,
306
- ...args
307
- }: FileUploadStoryArgs) {
308
- /* eslint-enable @typescript-eslint/no-unused-vars */
309
- const [files, setFiles] = useState<RecursicaFileUploadItem[]>([]);
310
- return (
311
- <FileUpload
312
- {...args}
313
- files={files}
314
- onFilesAdded={(added) =>
315
- setFiles((prev) => [...prev, ...added.map((file) => ({ file }))])
316
- }
317
- onFileRemove={(id) =>
318
- setFiles((prev) =>
319
- prev.filter((item) => (item.id ?? item.file.name) !== id),
320
- )
321
- }
322
- />
323
- );
324
- },
325
- };
326
-
327
- // Demonstrates `maxFiles`: already at the 2-file cap below, so dropping/picking another file is
328
- // rejected the same way an `accept` mismatch is — the control switches into its error state with
329
- // the default `maxFilesMessage` ("Maximum of 2 files allowed") instead of accepting the file.
330
- export const MaxFilesRestriction: Story = {
331
- args: {
332
- label: "Upload Files",
333
- assistiveText: "Up to 2 files allowed",
334
- maxFiles: 2,
335
- },
336
- /* eslint-disable @typescript-eslint/no-unused-vars */
337
- render: function FileUploadStory({
338
- withLayer,
339
- layer,
340
- ...args
341
- }: FileUploadStoryArgs) {
342
- /* eslint-enable @typescript-eslint/no-unused-vars */
343
- const [files, setFiles] = useState<RecursicaFileUploadItem[]>([
344
- { file: mockFile("document.pdf") },
345
- { file: mockFile("image.png") },
346
- ]);
347
- return (
348
- <FileUpload
349
- {...args}
350
- files={files}
351
- onFilesAdded={(added) =>
352
- setFiles((prev) => [...prev, ...added.map((file) => ({ file }))])
353
- }
354
- onFileRemove={(id) =>
355
- setFiles((prev) =>
356
- prev.filter((item) => (item.id ?? item.file.name) !== id),
357
- )
358
- }
359
- />
360
- );
361
- },
362
- };