{color === 'danger' && (
diff --git a/src/tedi/helpers/hooks/use-file-upload.ts b/src/tedi/helpers/hooks/use-file-upload.ts
index a73e38bd2..79e029c86 100644
--- a/src/tedi/helpers/hooks/use-file-upload.ts
+++ b/src/tedi/helpers/hooks/use-file-upload.ts
@@ -93,6 +93,8 @@ const getDefaultHelpers = (
const generateId = () => `${Date.now()}-${Math.random().toString(36).slice(2)}`;
+const ANNOUNCEMENT_RESET_DELAY = 100;
+
export const useFileUpload = (props: UseFileUploadProps) => {
const { getLabel } = useLabels();
const {
@@ -117,13 +119,34 @@ export const useFileUpload = (props: UseFileUploadProps) => {
const [announcement, setAnnouncement] = React.useState('');
const isMounted = React.useRef(true);
+ const announceShowTimer = React.useRef>();
+ const announceHideTimer = React.useRef>();
useEffect(() => {
return () => {
isMounted.current = false;
+ if (announceShowTimer.current) clearTimeout(announceShowTimer.current);
+ if (announceHideTimer.current) clearTimeout(announceHideTimer.current);
};
}, []);
+ const announce = React.useCallback(
+ (message: string) => {
+ if (!message || !isMounted.current) return;
+ setAnnouncement('');
+ if (announceShowTimer.current) clearTimeout(announceShowTimer.current);
+ if (announceHideTimer.current) clearTimeout(announceHideTimer.current);
+ announceShowTimer.current = setTimeout(() => {
+ if (!isMounted.current) return;
+ setAnnouncement(message);
+ announceHideTimer.current = setTimeout(() => {
+ if (isMounted.current) setAnnouncement('');
+ }, announcementTimeout);
+ }, ANNOUNCEMENT_RESET_DELAY);
+ },
+ [announcementTimeout]
+ );
+
const fileInputRef = React.useRef(null);
const validFileType = (file: File) => {
@@ -197,36 +220,22 @@ export const useFileUpload = (props: UseFileUploadProps) => {
setInnerFiles(newFiles);
}
onChange?.(newFiles);
-
- if (rejectedFiles.length === 0) {
- const count = newFiles.length - actualFiles.length;
- const message =
- getLabel('file-upload.success-added', count.toString()) || `${count} file(s) added successfully`;
-
- if (isMounted.current) {
- setAnnouncement(message);
- }
- }
}
if (rejectedFiles.length) {
- const failedNames = rejectedFiles.map((r) => r.file.name).join(', ');
- const errorMessage = getLabel('file-upload.failed-some', failedNames) || `Upload failed for: ${failedNames}`;
-
- if (isMounted.current) {
- setAnnouncement(errorMessage);
- }
- setUploadErrorHelper({ type: 'error', text: getUploadErrorHelperText(rejectedFiles) });
+ const errorText = getUploadErrorHelperText(rejectedFiles);
+ setUploadErrorHelper({ type: 'error', text: errorText });
+ announce(errorText);
} else {
setUploadErrorHelper(getDefaultHelpers({ accept, maxSize }, getLabel));
- }
- setTimeout(() => {
- if (isMounted.current) {
- setAnnouncement('');
+ if (newFiles.length > 0) {
+ const count = newFiles.filter((file) => !actualFiles.includes(file)).length;
+ announce(getLabel('file-upload.success-added', count.toString()));
}
- }, announcementTimeout);
+ }
+ event.target.value = '';
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
@@ -246,6 +255,8 @@ export const useFileUpload = (props: UseFileUploadProps) => {
setUploadErrorHelper(getDefaultHelpers({ accept, maxSize }, getLabel));
}
+ announce(getLabel('file-upload.removed', file.name ?? ''));
+
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
@@ -261,6 +272,8 @@ export const useFileUpload = (props: UseFileUploadProps) => {
onChange?.([]);
setUploadErrorHelper(getDefaultHelpers({ accept, maxSize }, getLabel));
+ announce(getLabel('file-upload.cleared'));
+
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
diff --git a/src/tedi/providers/label-provider/labels-map.ts b/src/tedi/providers/label-provider/labels-map.ts
index 1b80fe04b..09b4cf9d2 100644
--- a/src/tedi/providers/label-provider/labels-map.ts
+++ b/src/tedi/providers/label-provider/labels-map.ts
@@ -466,6 +466,23 @@ export const labelsMap = validateDefaultLabels({
en: (files: string) => `File(s) ${files} have the wrong extension`,
ru: (files: string) => `Файл(ы) ${files} имеют неправильное расширение`,
},
+
+ 'file-upload.removed': {
+ description: 'Announced to screen readers when a file is removed',
+ components: ['FileUpload'],
+ et: (file: string) => `Fail ${file} eemaldatud`,
+ en: (file: string) => `File ${file} removed`,
+ ru: (file: string) => `Файл ${file} удалён`,
+ },
+
+ 'file-upload.cleared': {
+ description: 'Announced to screen readers when all files are removed',
+ components: ['FileUpload'],
+ et: 'Kõik failid eemaldatud',
+ en: 'All files removed',
+ ru: 'Все файлы удалены',
+ },
+
'file-dropzone.label': {
description: 'Default label for dropzone',
components: ['FileDropzone'],