Skip to content

Commit 3a7a06c

Browse files
devadula-nandansunny-kukkar-ibmsangeethababu9223
authored
feat: react add mechanism to disable file items by exposing file state (#21446)
* feat: react add mechanism to disable file items by exposing file state * docs: fix storybooks to show disabled state properly in examples * feat: wc add disabled state support for file-uploader-item * docs: update docs on new feature flagged methods * chore: cleanup scss comments * revert: svg style * fix: update svg fill color on disable state * fix: review comments * Apply suggestion from @sangeethababu9223 Co-authored-by: Sangeetha Babu <sangeetha9223@gmail.com> * Apply suggestion from @sangeethababu9223 Co-authored-by: Sangeetha Babu <sangeetha9223@gmail.com> * test: add coverage for changed lines * chore: format mdx --------- Co-authored-by: Sunny Kukkar <sunny.kukkar@ibm.com> Co-authored-by: Sangeetha Babu <sangeetha9223@gmail.com>
1 parent 463edd5 commit 3a7a06c

13 files changed

Lines changed: 375 additions & 53 deletions

File tree

packages/react/src/components/FileUploader/FileUploader.featureflag.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ both `onChange` and `onDelete` events are augmented with detailed file
1414
information like `deletedFile`, `addedFiles`, and `currentFiles` on
1515
`event.target`.
1616

17+
the `getCurrentFiles` and `setCurrentFiles` methods are available on the
18+
`FileUploader` component, which can be used to get and update the file state and
19+
to disable specific files based on disabled prop.
20+
1721
## Enable enhanced FileUploader callbacks
1822

1923
```js

packages/react/src/components/FileUploader/FileUploader.featureflag.stories.js

Lines changed: 94 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import React from 'react';
8+
import React, { useEffect, useRef } from 'react';
99
import { FileUploader } from '../FileUploader';
1010
import { WithFeatureFlags } from '../../../.storybook/templates/WithFeatureFlags';
1111

@@ -25,48 +25,48 @@ export default {
2525
],
2626
};
2727

28+
// const DEBUG_ENABLED = process.env.NODE_ENV !== 'production';
29+
const DEBUG_ENABLED = true;
30+
31+
const debugLog = (...args) => {
32+
if (DEBUG_ENABLED) {
33+
console.log(...args);
34+
}
35+
};
36+
37+
const mapFileList = (files) =>
38+
files?.map((f) => ({ name: f.name, uuid: f.uuid })) || [];
39+
40+
const logFileList = (label, files) => {
41+
if (Array.isArray(files)) {
42+
debugLog(label, mapFileList(files));
43+
} else if (files) {
44+
debugLog(label, { name: files.name, uuid: files.uuid });
45+
}
46+
};
47+
48+
const logEventData = (event) => {
49+
debugLog(' Action:', event.target.action);
50+
51+
logFileList(' Added Files:', event.target.addedFiles);
52+
logFileList(' Deleted File:', event.target.deletedFile);
53+
logFileList(' Cleared Files:', event.target.clearedFiles);
54+
logFileList(' Current Files:', event.target.currentFiles);
55+
};
56+
57+
const logDeleteData = (event) => {
58+
debugLog(' Deleted File Object:', event.target.deletedFile);
59+
debugLog(' Deleted File Name:', event.target.deletedFile?.name);
60+
logFileList(' Remaining Files:', event.target.remainingFiles);
61+
};
62+
2863
export const EnhancedCallbacks = (args) => {
2964
const handleChange = (event, data) => {
30-
console.log(' Action:', event.target.action);
31-
32-
if (event.target.addedFiles) {
33-
console.log(
34-
' Added Files:',
35-
event.target.addedFiles.map((f) => ({ name: f.name, uuid: f.uuid }))
36-
);
37-
}
38-
39-
if (event.target.deletedFile) {
40-
console.log(' Deleted File:', {
41-
name: event.target.deletedFile.name,
42-
uuid: event.target.deletedFile.uuid,
43-
});
44-
}
45-
46-
if (event.target.clearedFiles) {
47-
console.log(
48-
' Cleared Files:',
49-
event.target.clearedFiles.map((f) => ({ name: f.name, uuid: f.uuid }))
50-
);
51-
}
52-
53-
console.log(
54-
' Current Files:',
55-
event.target.currentFiles?.map((f) => ({ name: f.name, uuid: f.uuid })) ||
56-
[]
57-
);
65+
logEventData(event);
5866
};
5967

6068
const handleDelete = (event, data) => {
61-
console.log(' Deleted File Object:', event.target.deletedFile);
62-
console.log(' Deleted File Name:', event.target.deletedFile?.name);
63-
console.log(
64-
' Remaining Files:',
65-
event.target.remainingFiles?.map((f) => ({
66-
name: f.name,
67-
uuid: f.uuid,
68-
})) || []
69-
);
69+
logDeleteData(event);
7070
};
7171

7272
return (
@@ -98,3 +98,59 @@ EnhancedCallbacks.argTypes = {
9898
},
9999
},
100100
};
101+
102+
export const ControlledFileState = (args) => {
103+
const fileUploaderRef = useRef(null);
104+
105+
useEffect(() => {
106+
if (!fileUploaderRef.current) return;
107+
const currentFiles = fileUploaderRef.current.getCurrentFiles();
108+
if (!currentFiles?.length) return;
109+
110+
const mutatedFiles = currentFiles.map((file) => ({
111+
...file,
112+
disabled: args.disabled,
113+
}));
114+
115+
fileUploaderRef.current.setCurrentFiles(mutatedFiles);
116+
}, [args.disabled]);
117+
118+
const handleChange = (event, data) => {
119+
logEventData(event);
120+
};
121+
122+
const handleDelete = (event, data) => {
123+
logDeleteData(event);
124+
};
125+
126+
return (
127+
<div>
128+
<FileUploader
129+
ref={fileUploaderRef}
130+
accept={['.jpg', '.png']}
131+
labelTitle="Enhanced FileUploader Demo"
132+
labelDescription="Add files, then toggle the disabled state and notice that the state is passed to the items."
133+
buttonLabel="Add file(s)"
134+
buttonKind="primary"
135+
filenameStatus="edit"
136+
multiple
137+
onChange={handleChange}
138+
onDelete={handleDelete}
139+
iconDescription="Remove uploaded file"
140+
{...args}
141+
/>
142+
</div>
143+
);
144+
};
145+
146+
ControlledFileState.args = {
147+
disabled: false,
148+
};
149+
150+
ControlledFileState.argTypes = {
151+
disabled: {
152+
control: {
153+
type: 'boolean',
154+
},
155+
},
156+
};

packages/react/src/components/FileUploader/FileUploader.stories.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ export const _FileUploaderItem = (args) => {
5454
};
5555

5656
_FileUploaderItem.argTypes = {
57+
disabled: {
58+
control: 'boolean',
59+
description: 'Specify whether file uploader item is disabled',
60+
},
5761
errorBody: {
5862
control: 'text',
5963
description: 'Error message body for an invalid file upload',
@@ -88,7 +92,6 @@ _FileUploaderItem.parameters = {
8892
'accept',
8993
'buttonKind',
9094
'buttonLabel',
91-
'disabled',
9295
'labelDescription',
9396
'labelTitle',
9497
'multiple',

packages/react/src/components/FileUploader/FileUploader.tsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ interface FileItem {
2727
name: string;
2828
uuid: string;
2929
file: File & { invalidFileType?: boolean };
30+
disabled?: boolean;
3031
}
3132

3233
export interface FileChangeData {
@@ -163,6 +164,11 @@ export interface FileUploaderHandle {
163164
* Get current files (only available when 'enable-enhanced-file-uploader' feature flag is enabled)
164165
*/
165166
getCurrentFiles?: () => FileItem[];
167+
168+
/**
169+
* Set current files (only available when 'enable-enhanced-file-uploader' feature flag is enabled)
170+
*/
171+
setCurrentFiles?: (files: FileItem[]) => void;
166172
}
167173

168174
const FileUploader = forwardRef<FileUploaderHandle, FileUploaderProps>(
@@ -417,6 +423,9 @@ const FileUploader = forwardRef<FileUploaderHandle, FileUploaderProps>(
417423
getCurrentFiles() {
418424
return [...fileItems];
419425
},
426+
setCurrentFiles: (files) => {
427+
setFileItems(files);
428+
},
420429
}),
421430
}),
422431
[enhancedFileUploaderEnabled, fileItems, onChange]
@@ -431,14 +440,19 @@ const FileUploader = forwardRef<FileUploaderHandle, FileUploaderProps>(
431440
[`${prefix}--label-description--disabled`]: disabled,
432441
});
433442

434-
const selectedFileClasses = classNames(`${prefix}--file__selected-file`, {
435-
[`${prefix}--file__selected-file--md`]: size === 'field' || size === 'md',
436-
[`${prefix}--file__selected-file--sm`]: size === 'small' || size === 'sm',
437-
});
443+
const getSelectedFileClasses = (file) =>
444+
classNames(`${prefix}--file__selected-file`, {
445+
[`${prefix}--file__selected-file--md`]:
446+
size === 'field' || size === 'md',
447+
[`${prefix}--file__selected-file--sm`]:
448+
size === 'small' || size === 'sm',
449+
[`${prefix}--file__selected-file--disabled`]: file.disabled,
450+
});
438451

439452
const displayFiles = enhancedFileUploaderEnabled
440453
? fileItems.map((item, index) => ({
441454
name: item.name,
455+
disabled: item.disabled,
442456
key: item.uuid,
443457
index,
444458
}))
@@ -478,7 +492,7 @@ const FileUploader = forwardRef<FileUploaderHandle, FileUploaderProps>(
478492
: displayFiles.map((file) => (
479493
<span
480494
key={file.key}
481-
className={selectedFileClasses}
495+
className={getSelectedFileClasses(file)}
482496
ref={(node) => {
483497
nodes[file.index] = node as HTMLSpanElement;
484498
}}
@@ -496,6 +510,7 @@ const FileUploader = forwardRef<FileUploaderHandle, FileUploaderProps>(
496510
<span className={`${prefix}--file__state-container`}>
497511
<Filename
498512
name={file.name}
513+
disabled={file.disabled}
499514
iconDescription={iconDescription}
500515
status={filenameStatus}
501516
onKeyDown={(evt) => {

packages/react/src/components/FileUploader/FileUploaderItem.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ import { Text } from '../Text';
1818
import { Tooltip } from '../Tooltip';
1919

2020
export interface FileUploaderItemProps extends HTMLAttributes<HTMLSpanElement> {
21+
/**
22+
* Specify whether file uploader item is disabled
23+
*/
24+
disabled?: boolean;
25+
2126
/**
2227
* Error message body for an invalid file upload
2328
*/
@@ -84,6 +89,7 @@ function FileUploaderItem({
8489
errorBody,
8590
size,
8691
className,
92+
disabled,
8793
...other
8894
}: FileUploaderItemProps) {
8995
const textRef = useRef<HTMLParagraphElement>(null);
@@ -95,6 +101,7 @@ function FileUploaderItem({
95101
[`${prefix}--file__selected-file--invalid`]: invalid,
96102
[`${prefix}--file__selected-file--md`]: size === 'md',
97103
[`${prefix}--file__selected-file--sm`]: size === 'sm',
104+
[`${prefix}--file__selected-file--disabled`]: disabled,
98105
});
99106
const isInvalid = invalid
100107
? `${prefix}--file-filename-container-wrap-invalid`
@@ -153,6 +160,7 @@ function FileUploaderItem({
153160
<span className={`${prefix}--file__state-container`}>
154161
<Filename
155162
name={name}
163+
disabled={disabled}
156164
iconDescription={iconDescription}
157165
status={status}
158166
invalid={invalid}

packages/react/src/components/FileUploader/Filename.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ export interface FilenameProps
2727
*/
2828
iconDescription?: string;
2929

30+
/**
31+
* Specify whether the file uploader item is disabled
32+
*/
33+
disabled?: boolean;
34+
3035
/**
3136
* Specify if the file is invalid
3237
*/
@@ -52,6 +57,7 @@ function Filename({
5257
iconDescription = 'Uploading file',
5358
status = 'uploading',
5459
invalid,
60+
disabled,
5561
name,
5662
tabIndex = 0,
5763
['aria-describedby']: ariaDescribedBy,
@@ -73,6 +79,7 @@ function Filename({
7379
<>
7480
{invalid && <WarningFilled className={`${prefix}--file-invalid`} />}
7581
<button
82+
disabled={disabled}
7683
aria-label={`${iconDescription} - ${name}`}
7784
className={`${prefix}--file-close`}
7885
type="button"

0 commit comments

Comments
 (0)