Skip to content

Commit

Permalink
chore: Types for FileUploader, FileUploaderButton, FileUploaderDropCo…
Browse files Browse the repository at this point in the history
…ntainer, FileUploaderItem, FileUploaderSkeleton, Filename, Upload and ButtonSkeleton (#13992)

* chore: renamed fileuploader files to tsx

* chore: added types for Loading component

* chore: added types for FileUploader components and ButtonSkeleton

* chore: tab index prop takes boths string & number, test snapshot updated

* chore: removed field size usage in button skeleton

---------

Co-authored-by: TJ Egan <tw15egan@gmail.com>
  • Loading branch information
imp-dance and tw15egan committed Jun 26, 2023
1 parent 2ee33d7 commit 033ecc7
Show file tree
Hide file tree
Showing 8 changed files with 439 additions and 35 deletions.
12 changes: 11 additions & 1 deletion packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3440,7 +3440,17 @@ Map {
"type": "oneOf",
},
"tabIndex": Object {
"type": "string",
"args": Array [
Array [
Object {
"type": "number",
},
Object {
"type": "string",
},
],
],
"type": "oneOfType",
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ import cx from 'classnames';
import SkeletonText from '../SkeletonText';
import ButtonSkeleton from '../Button/Button.Skeleton';
import { usePrefix } from '../../internal/usePrefix';
import { ReactAttr } from '../../types/common';

export interface FileUploaderSkeletonProps extends ReactAttr<HTMLDivElement> {
/**
* Specify an optional className to add.
*/
className?: string;
}

function FileUploaderSkeleton({ className, ...rest }) {
const prefix = usePrefix();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,102 @@ import FileUploaderButton from './FileUploaderButton';
import { ButtonKinds } from '../../prop-types/types';
import { keys, matches } from '../../internal/keyboard';
import { PrefixContext } from '../../internal/usePrefix';
import { ReactAttr } from '../../types/common';

export default class FileUploader extends React.Component {
export interface FileUploaderProps extends ReactAttr<HTMLSpanElement> {
/**
* Specify the types of files that this input should be able to receive
*/
accept?: string[];

/**
* Specify the type of the `<FileUploaderButton>`
*/
buttonKind?:
| 'primary'
| 'secondary'
| 'danger'
| 'ghost'
| 'danger--primary'
| 'danger--ghost'
| 'danger--tertiary'
| 'tertiary';

/**
* Provide the label text to be read by screen readers when interacting with
* the `<FileUploaderButton>`
*/
buttonLabel?: string;

/**
* Provide a custom className to be applied to the container node
*/
className?: string;

/**
* Specify whether file input is disabled
*/
disabled?: boolean;

/**
* Specify the status of the File Upload
*/
filenameStatus: 'edit' | 'complete' | 'uploading';

/**
* Provide a description for the complete/close icon that can be read by screen readers
*/
iconDescription: string;

/**
* Specify the description text of this `<FileUploader>`
*/
labelDescription?: string;

/**
* Specify the title text of this `<FileUploader>`
*/
labelTitle?: string;

/**
* Specify if the component should accept multiple files to upload
*/
multiple?: boolean;

/**
* Provide a name for the underlying `<input>` node
*/
name?: string;

/**
* Provide an optional `onChange` hook that is called each time the input is
* changed
*/
onChange?: (event: any) => void;

/**
* Provide an optional `onClick` hook that is called each time the
* FileUploader is clicked
*/
onClick?: (event: any) => void;

/**
* Provide an optional `onDelete` hook that is called when an uploaded item
* is removed
*/
onDelete?: (event: any) => void;

/**
* Specify the size of the FileUploaderButton, from a list of available
* sizes.
*/
size?: 'sm' | 'small' | 'md' | 'field' | 'lg';
}

export default class FileUploader extends React.Component<
FileUploaderProps,
{ filenames: string[] }
> {
static propTypes = {
/**
* Specify the types of files that this input should be able to receive
Expand Down Expand Up @@ -111,12 +205,12 @@ export default class FileUploader extends React.Component {
};

state = {
filenames: [],
filenames: [] as string[],
};

nodes = [];
nodes: HTMLElement[] = [];

uploaderButton = React.createRef();
uploaderButton = React.createRef<HTMLLabelElement>();

static getDerivedStateFromProps({ filenameStatus }, state) {
const { prevFilenameStatus } = state;
Expand All @@ -133,7 +227,7 @@ export default class FileUploader extends React.Component {
const filenames = Array.prototype.map.call(
evt.target.files,
(file) => file.name
);
) as string[];
this.setState({
filenames: this.props.multiple
? this.state.filenames.concat(filenames)
Expand All @@ -153,9 +247,9 @@ export default class FileUploader extends React.Component {
this.setState({ filenames: filteredArray });
if (this.props.onDelete) {
this.props.onDelete(evt);
this.uploaderButton.current.focus();
this.uploaderButton.current?.focus?.();
}
this.props.onClick(evt);
this.props.onClick?.(evt);
}
};

Expand All @@ -178,15 +272,15 @@ export default class FileUploader extends React.Component {
accept,
name,
size = 'md',
onDelete, // eslint-disable-line no-unused-vars
onDelete, // eslint-disable-line
...other
} = this.props;

const prefix = this.context;

const classes = classNames({
[`${prefix}--form-item`]: true,
[className]: className,
[className as string]: className,
});

const getHelperLabelClasses = (baseClass) =>
Expand Down Expand Up @@ -228,7 +322,7 @@ export default class FileUploader extends React.Component {
<span
key={index}
className={selectedFileClasses}
ref={(node) => (this.nodes[index] = node)} // eslint-disable-line
ref={(node) => (this.nodes[index] = node as HTMLSpanElement)} // eslint-disable-line
{...other}>
<p className={`${prefix}--file-filename`} id={name}>
{name}
Expand All @@ -239,7 +333,12 @@ export default class FileUploader extends React.Component {
iconDescription={iconDescription}
status={filenameStatus}
onKeyDown={(evt) => {
if (matches(evt, [keys.Enter, keys.Space])) {
if (
matches(evt as unknown as Event, [
keys.Enter,
keys.Space,
])
) {
this.handleClick(evt, { index, filenameStatus });
}
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,98 @@ import { ButtonKinds } from '../../prop-types/types';
import uid from '../../tools/uniqueId';
import { usePrefix } from '../../internal/usePrefix';
import deprecate from '../../prop-types/deprecate';
import { ReactAttr } from '../../types/common';

function noop() {}

export interface FileUploaderButtonProps
extends Omit<ReactAttr<HTMLButtonElement>, 'onChange' | 'tabIndex'> {
/**
* Specify the types of files that this input should be able to receive
*/
accept?: string[];

/**
* Specify the type of underlying button
*/
buttonKind?:
| 'primary'
| 'secondary'
| 'danger'
| 'ghost'
| 'danger--primary'
| 'danger--ghost'
| 'danger--tertiary'
| 'tertiary';

/**
* Provide a custom className to be applied to the container node
*/
className?: string;

/**
* Specify whether you want to disable any updates to the FileUploaderButton
* label
*/
disableLabelChanges?: boolean;

/**
* Specify whether file input is disabled
*/
disabled?: boolean;

/**
* Provide a unique id for the underlying `<input>` node
*/
id?: string;

/**
* Provide the label text to be read by screen readers when interacting with
* this control
*/
labelText?: React.ReactNode;

/**
* Specify if the component should accept multiple files to upload
*/
multiple?: boolean;

/**
* Provide a name for the underlying `<input>` node
*/
name?: string;

/**
* Provide an optional `onChange` hook that is called each time the `<input>`
* value changes
*/
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;

/**
* Provide an optional `onClick` hook that is called each time the button is
* clicked
*/
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;

/**
* Provide an accessibility role for the `<FileUploaderButton>`
*/
role?: string;

/**
* Specify the size of the FileUploaderButton, from a list of available
* sizes.
*/
size?: 'sm' | 'small' | 'field' | 'md' | 'lg';

/**
* @deprecated The `tabIndex` prop for `FileUploaderButton` has been deprecated since it now renders a button element by default.
*/
tabIndex?: number | string;

innerRef?: React.RefObject<HTMLLabelElement>;
}

function FileUploaderButton({
accept,
buttonKind = 'primary',
Expand All @@ -31,12 +120,12 @@ function FileUploaderButton({
// eslint-disable-next-line react/prop-types
innerRef,
...other
}) {
}: FileUploaderButtonProps) {
const prefix = usePrefix();
const [labelText, setLabelText] = useState(ownerLabelText);
const [prevOwnerLabelText, setPrevOwnerLabelText] = useState(ownerLabelText);
const { current: inputId } = useRef(id || uid());
const inputNode = useRef(null);
const inputNode = useRef<HTMLInputElement>(null);
const classes = cx(`${prefix}--btn`, className, {
[`${prefix}--btn--${buttonKind}`]: buttonKind,
[`${prefix}--btn--disabled`]: disabled,
Expand All @@ -54,20 +143,22 @@ function FileUploaderButton({

function onClick(event) {
event.target.value = null;
inputNode.current.value = '';
inputNode.current.click();
if (inputNode.current) {
inputNode.current.value = '';
inputNode.current.click();
}
}

function onKeyDown(event) {
if (matches(event, [keys.Enter, keys.Space])) {
if (matches(event, [keys.Enter, keys.Space]) && inputNode.current) {
inputNode.current.value = '';
inputNode.current.click();
}
}

function handleOnChange(event) {
function handleOnChange(event: React.ChangeEvent<HTMLInputElement>) {
const files = event.target.files;
const length = event.target.files.length;
const length = event.target.files?.length || 0;
if (files && !disableLabelChanges) {
if (length > 1) {
setLabelText(`${length} files`);
Expand All @@ -86,7 +177,12 @@ function FileUploaderButton({
className={classes}
onClick={onClick}
onKeyDown={onKeyDown}
{...other}>
{...other}
tabIndex={
other.tabIndex !== undefined
? parseInt(other.tabIndex as string)
: undefined
}>
{labelText}
</button>
<label
Expand All @@ -101,9 +197,9 @@ function FileUploaderButton({
id={inputId}
disabled={disabled}
type="file"
tabIndex="-1"
tabIndex={-1}
multiple={multiple}
accept={accept}
accept={accept?.toString()}
name={name}
onChange={handleOnChange}
/>
Expand Down
Loading

0 comments on commit 033ecc7

Please sign in to comment.