diff --git a/CHANGELOG.md b/CHANGELOG.md index f60d571e..867fe414 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/core/components/forms/inputFileAvatar/inputFileAvatar.api.ts b/src/core/components/forms/inputFileAvatar/inputFileAvatar.api.ts index 45a595f7..986c0f24 100644 --- a/src/core/components/forms/inputFileAvatar/inputFileAvatar.api.ts +++ b/src/core/components/forms/inputFileAvatar/inputFileAvatar.api.ts @@ -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 @@ -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) diff --git a/src/core/components/forms/inputFileAvatar/inputFileAvatar.test.tsx b/src/core/components/forms/inputFileAvatar/inputFileAvatar.test.tsx index 1d04b890..87472cb2 100644 --- a/src/core/components/forms/inputFileAvatar/inputFileAvatar.test.tsx +++ b/src/core/components/forms/inputFileAvatar/inputFileAvatar.test.tsx @@ -107,18 +107,18 @@ describe(' 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 () => { @@ -129,4 +129,26 @@ describe(' 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(); + }); }); diff --git a/src/core/components/forms/inputFileAvatar/inputFileAvatar.tsx b/src/core/components/forms/inputFileAvatar/inputFileAvatar.tsx index 9d453f25..317ceb7f 100644 --- a/src/core/components/forms/inputFileAvatar/inputFileAvatar.tsx +++ b/src/core/components/forms/inputFileAvatar/inputFileAvatar.tsx @@ -41,7 +41,6 @@ const dropzoneErrorToError: Record = { export const InputFileAvatar: React.FC = (props) => { const { - onFileError, maxFileSize, minDimension, maxDimension, @@ -64,7 +63,7 @@ export const InputFileAvatar: React.FC = (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; } @@ -78,9 +77,9 @@ export const InputFileAvatar: React.FC = (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 }); } @@ -91,12 +90,12 @@ export const InputFileAvatar: React.FC = (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({ @@ -128,7 +127,8 @@ export const InputFileAvatar: React.FC = (props) => { - {value?.url ? ( + + {value?.url || value?.error ? (