Skip to content

Commit

Permalink
feat(APP-3693): Remove onFileError property from InputFileAvatar, use…
Browse files Browse the repository at this point in the history
… onChange to handle errors (#387)
  • Loading branch information
shan8851 authored Jan 15, 2025
1 parent 189cef5 commit 003d7c5
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 17 deletions.
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

### Changed

- Update `InputFileAvatar` core component to remove `onFileSelect` property and use `value` and `onChange` properties
instead
- Update `InputFileAvatar` core component to remove `onFileSelect` and `onFileError` properties and use `value` and
`onChange` properties instead
- Update `IProposalActionUpdateMetadataDaoMetadata` interface `logo` property to `avatar` to better align with actions
- Bump `softprops/action-gh-release` from 2.2.0 to 2.2.1
- Update minor and patch NPM dependencies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ export interface IInputFileAvatarValue {
* File object of the image.
*/
file?: File;
/**
* Error message if image upload fails.
*/
error?: InputFileAvatarError;
}

export interface IInputFileAvatarProps
Expand All @@ -33,10 +37,6 @@ export interface IInputFileAvatarProps
* The current value of the input.
*/
value?: IInputFileAvatarValue;
/**
* Function that is called when a file is rejected. Passes the error message to the parent component.
*/
onFileError?: (error: InputFileAvatarError) => void;
/**
* Allowed file extensions, it must be an object with the keys set to the MIME type
* and the values an array of file extensions (see https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker#accept)
Expand Down
30 changes: 26 additions & 4 deletions src/core/components/forms/inputFileAvatar/inputFileAvatar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,18 +107,18 @@ describe('<InputFileAvatar /> component', () => {
expect(revokeObjectURLMock).toHaveBeenCalledWith(fileSrc);
});

it('calls onFileError when file has incorrect dimensions', async () => {
it('calls onChange and sets error property when file has incorrect dimensions', async () => {
const user = userEvent.setup();
(window.Image.prototype as HTMLImageElement).width = 800;

const label = 'test-label';
const file = new File(['test'], 'test.png', { type: 'image/png' });
const onFileError = jest.fn();
const onChange = jest.fn();
const minDimension = 1000;

render(createTestComponent({ label, onFileError, minDimension }));
render(createTestComponent({ label, onChange, minDimension }));
await user.upload(screen.getByLabelText(label), file);
await waitFor(() => expect(onFileError).toHaveBeenCalledWith(InputFileAvatarError.WRONG_DIMENSION));
await waitFor(() => expect(onChange).toHaveBeenCalledWith({ error: InputFileAvatarError.WRONG_DIMENSION }));
});

it('displays the initialValue image preview when provided', async () => {
Expand All @@ -129,4 +129,26 @@ describe('<InputFileAvatar /> component', () => {
expect(previewImg).toBeInTheDocument();
expect(previewImg.src).toEqual(value.url);
});

it('renders the cancel button when error is present and clears error on button click', async () => {
const user = userEvent.setup();
const onChange = jest.fn();

const { rerender } = render(
createTestComponent({
onChange,
value: { error: InputFileAvatarError.WRONG_DIMENSION },
}),
);

const cancelButton = await screen.findByRole('button');
expect(cancelButton).toBeInTheDocument();

await user.click(cancelButton);

expect(onChange).toHaveBeenCalledWith(undefined);

rerender(createTestComponent({ onChange }));
expect(screen.queryByRole('button')).not.toBeInTheDocument();
});
});
14 changes: 7 additions & 7 deletions src/core/components/forms/inputFileAvatar/inputFileAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ const dropzoneErrorToError: Record<string, InputFileAvatarError | undefined> = {

export const InputFileAvatar: React.FC<IInputFileAvatarProps> = (props) => {
const {
onFileError,
maxFileSize,
minDimension,
maxDimension,
Expand All @@ -64,7 +63,7 @@ export const InputFileAvatar: React.FC<IInputFileAvatarProps> = (props) => {
if (rejectedFiles.length > 0) {
const dropzoneError = rejectedFiles[0].errors[0].code;
const internalError = dropzoneErrorToError[dropzoneError] ?? InputFileAvatarError.UNKNOWN_ERROR;
onFileError?.(internalError);
onChange({ error: internalError });

return;
}
Expand All @@ -78,9 +77,9 @@ export const InputFileAvatar: React.FC<IInputFileAvatarProps> = (props) => {
const isAboveMaxDimension = maxDimension && (image.width > maxDimension || image.height > maxDimension);

if (onlySquare && image.height !== image.width) {
onFileError?.(InputFileAvatarError.SQUARE_ONLY);
onChange({ error: InputFileAvatarError.SQUARE_ONLY });
} else if (isBelowMinDimension ?? isAboveMaxDimension) {
onFileError?.(InputFileAvatarError.WRONG_DIMENSION);
onChange({ error: InputFileAvatarError.WRONG_DIMENSION });
} else {
onChange({ url: image.src, file });
}
Expand All @@ -91,12 +90,12 @@ export const InputFileAvatar: React.FC<IInputFileAvatarProps> = (props) => {
image.addEventListener('load', onImageLoad);
image.addEventListener('error', () => {
setIsLoading(false);
onFileError?.(InputFileAvatarError.UNKNOWN_ERROR);
onChange({ error: InputFileAvatarError.UNKNOWN_ERROR });
});

image.src = URL.createObjectURL(file);
},
[maxDimension, minDimension, onChange, onFileError, onlySquare],
[maxDimension, minDimension, onChange, onlySquare],
);

const { getRootProps, getInputProps } = useDropzone({
Expand Down Expand Up @@ -128,7 +127,8 @@ export const InputFileAvatar: React.FC<IInputFileAvatarProps> = (props) => {
<InputContainer id={randomId} useCustomWrapper={true} {...containerProps}>
<div {...getRootProps()} className={inputAvatarClassNames}>
<input {...getInputProps()} id={randomId} />
{value?.url ? (

{value?.url || value?.error ? (
<div className="relative">
<Avatar src={value.url} size="lg" className="cursor-pointer" data-testid="avatar" />
<button
Expand Down

0 comments on commit 003d7c5

Please sign in to comment.