diff --git a/.changeset/stupid-falcons-drop.md b/.changeset/stupid-falcons-drop.md new file mode 100644 index 0000000000..9a08b223c9 --- /dev/null +++ b/.changeset/stupid-falcons-drop.md @@ -0,0 +1,5 @@ +--- +'@lion/ui': patch +--- + +[input-file] improve a11y labels diff --git a/packages/ui/components/input-file/src/LionInputFile.js b/packages/ui/components/input-file/src/LionInputFile.js index 894ab36bf4..094565c989 100644 --- a/packages/ui/components/input-file/src/LionInputFile.js +++ b/packages/ui/components/input-file/src/LionInputFile.js @@ -77,6 +77,7 @@ export class LionInputFile extends ScopedElementsMixin(LocalizeMixin(LionField)) > ${this.buttonLabel} `, + after: () => html`
`, 'selected-file-list': () => ({ template: html` { + if (file.status === 'FAIL') { + errorMessage = file.validationFeedback ? file.validationFeedback[0].message.toString() : ''; + erroneousFilesNames.push(/** @type {string} */ (file.systemFile.name)); + } + }); + + const selectedFiles = this.querySelector('[slot="after"]'); + if (selectedFiles) { + if (!this._selectedFilesMetaData || this._selectedFilesMetaData.length === 0) { + selectedFiles.textContent = /** @type {string} */ ( + this.msgLit('lion-input-file:noFilesSelected') + ); + } else if (this._selectedFilesMetaData.length === 1) { + selectedFiles.textContent = /** @type {string} */ ( + errorMessage || this._selectedFilesMetaData[0].systemFile.name + ); + } else { + selectedFiles.textContent = `${this.msgLit('lion-input-file:numberOfFiles', { + numberOfFiles: this._selectedFilesMetaData.length, + })} ${ + errorMessage + ? this.msgLit('lion-input-file:generalValidatorMessage', { + validatorMessage: errorMessage, + listOfErroneousFiles: erroneousFilesNames.join(', '), + }) + : '' + }`; + } + } + } + /** * @private * @param {InputFile} removedFile @@ -755,6 +780,7 @@ export class LionInputFile extends ScopedElementsMixin(LocalizeMixin(LionField)) } this._inputNode.value = ''; this._handleErrors(); + this._updateUploadButtonDescription(); } /** @@ -847,6 +873,7 @@ export class LionInputFile extends ScopedElementsMixin(LocalizeMixin(LionField)) _inputGroupInputTemplate() { return html` + ${this.enableDropZone && this._isDragAndDropSupported ? this._dropZoneTemplate() : html` @@ -891,6 +918,19 @@ export class LionInputFile extends ScopedElementsMixin(LocalizeMixin(LionField)) border: dashed 2px black; padding: 24px 0; } + + .input-group__container ::slotted([slot='after']) { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip-path: inset(100%); + clip: rect(1px, 1px, 1px, 1px); + white-space: nowrap; + border: 0; + margin: 0; + padding: 0; + } `, ]; } diff --git a/packages/ui/components/input-file/test/lion-input-file.test.js b/packages/ui/components/input-file/test/lion-input-file.test.js index 8ac170ed60..85750eb5fe 100644 --- a/packages/ui/components/input-file/test/lion-input-file.test.js +++ b/packages/ui/components/input-file/test/lion-input-file.test.js @@ -1133,7 +1133,7 @@ describe('lion-input-file', () => { ); }); - it('select-button has aria-describedby set to the help-text, selected list and the feedback message', async () => { + it('select-button has aria-describedby set to the help-text, after and the feedback message', async () => { const uploadResponse = [ { name: 'file1.txt', @@ -1159,11 +1159,73 @@ describe('lion-input-file', () => { // @ts-expect-error [allow-protected-in-test] `feedback-${el._inputId}`, ); - await el.updateComplete; // @ts-expect-error [allow-protected-in-test] expect(el._buttonNode?.getAttribute('aria-describedby')).to.contain( // @ts-expect-error [allow-protected-in-test] - `selected-file-list-${el._inputId}`, + `after-${el._inputId}`, + ); + }); + + it('after contains upload name of file when SUCCESS', async () => { + const uploadResponse = [ + { + name: 'file1.txt', + status: 'SUCCESS', + errorMessage: '', + downloadUrl: '/downloadFile', + }, + ]; + const el = await fixture(html` `); + + expect(el.querySelector('[slot="after"]')?.textContent).to.equal('No files selected.'); + // @ts-expect-error + el.uploadResponse = uploadResponse; + await el.updateComplete; + expect(el.querySelector('[slot="after"]')?.textContent).to.equal('file1.txt'); + }); + + it('after contains upload validator message of file when FAIL', async () => { + const uploadResponse = [ + { + name: 'file1.txt', + status: 'FAIL', + errorMessage: 'something went wrong', + downloadUrl: '/downloadFile', + }, + ]; + const el = await fixture(html` `); + + expect(el.querySelector('[slot="after"]')?.textContent).to.equal('No files selected.'); + // @ts-expect-error + el.uploadResponse = uploadResponse; + await el.updateComplete; + expect(el.querySelector('[slot="after"]')?.textContent).to.equal('something went wrong'); + }); + + it('after contains upload status of files when multiple files have been uploaded', async () => { + const uploadResponse = [ + { + name: 'file1.txt', + status: 'SUCCESS', + errorMessage: '', + downloadUrl: '/downloadFile', + }, + { + name: 'file2.txt', + status: 'FAIL', + errorMessage: 'something went wrong', + downloadUrl: '/downloadFile', + }, + ]; + const el = await fixture(html` + + `); + // @ts-expect-error + el.uploadResponse = uploadResponse; + + await el.updateComplete; + expect(el.querySelector('[slot="after"]')?.textContent?.trim()).to.equal( + '2 files. "something went wrong", for file2.txt.', ); }); }); diff --git a/packages/ui/components/input-file/translations/bg.js b/packages/ui/components/input-file/translations/bg.js index aeb71cb10f..5ae2a9dfb6 100644 --- a/packages/ui/components/input-file/translations/bg.js +++ b/packages/ui/components/input-file/translations/bg.js @@ -5,6 +5,9 @@ export default { 'Моля, качете файл от тип {allowedTypesArray} или {allowedTypesLastItem} с макс. размер {maxSize}.', dragAndDropText: 'Плъзнете и пуснете Вашите файлове тук или', fileNameDescriptionLabel: 'Име на файл: {fileName}', + generalValidatorMessage: '"{validatorMessage}", за {listOfErroneousFiles}.', + noFilesSelected: 'Не са избрани файлове.', + numberOfFiles: '{numberOfFiles} файла.', removeButtonLabel: 'Отстраняване на файла {fileName}', selectTextDuplicateFileName: 'Файл със същото име на файл вече е налице.', selectTextMultipleFile: 'Избор на файлове', diff --git a/packages/ui/components/input-file/translations/cs.js b/packages/ui/components/input-file/translations/cs.js index a3ddfbb95d..8557272c7a 100644 --- a/packages/ui/components/input-file/translations/cs.js +++ b/packages/ui/components/input-file/translations/cs.js @@ -6,6 +6,9 @@ export default { 'Nahrajte soubor typu {allowedTypesArray} nebo {allowedTypesLastItem} s max. velikostí {maxSize}.', dragAndDropText: 'Přetáhněte soubory sem nebo', fileNameDescriptionLabel: 'Název souboru: {fileName}', + generalValidatorMessage: '"{validatorMessage}", pro {listOfErroneousFiles}.', + noFilesSelected: 'Nebyly vybrány žádné soubory.', + numberOfFiles: '{numberOfFiles} soubory/souborů.', removeButtonLabel: 'Odebrat soubor {fileName}', selectTextDuplicateFileName: 'Soubor se stejným názvem byl již přítomen.', selectTextMultipleFile: 'Vybrat soubory', diff --git a/packages/ui/components/input-file/translations/de.js b/packages/ui/components/input-file/translations/de.js index 1ca7a7746b..375cb676ec 100644 --- a/packages/ui/components/input-file/translations/de.js +++ b/packages/ui/components/input-file/translations/de.js @@ -5,6 +5,9 @@ export default { 'Laden Sie eine {allowedTypesArray} oder {allowedTypesLastItem}-Datei mit max. {maxSize} hoch.', dragAndDropText: 'Ziehen Sie Ihre Dateien per Drag & Drop hierher oder', fileNameDescriptionLabel: 'Dateiname: {fileName}', + generalValidatorMessage: '"{validatorMessage}", für {listOfErroneousFiles}.', + noFilesSelected: 'Keine Dateien ausgewählt.', + numberOfFiles: '{numberOfFiles} Dateien.', removeButtonLabel: 'Datei {fileName} entfernen', selectTextDuplicateFileName: 'Eine Datei mit demselben Dateinamen war bereits vorhanden.', selectTextMultipleFile: 'Dateien auswählen', diff --git a/packages/ui/components/input-file/translations/en.js b/packages/ui/components/input-file/translations/en.js index 7dfaf51ddc..9362bc084b 100644 --- a/packages/ui/components/input-file/translations/en.js +++ b/packages/ui/components/input-file/translations/en.js @@ -5,6 +5,9 @@ export default { 'Please select a {allowedTypesArray} or {allowedTypesLastItem} file with max {maxSize}.', dragAndDropText: 'Drag & Drop your files here or', // TODO: or what? Why is Drag & Drop capitalized? fileNameDescriptionLabel: 'File name: {fileName}', + generalValidatorMessage: '"{validatorMessage}", for {listOfErroneousFiles}.', + noFilesSelected: 'No files selected.', + numberOfFiles: '{numberOfFiles} files.', removeButtonLabel: 'Remove {fileName} file', selectTextDuplicateFileName: 'A file with same filename was already present.', selectTextMultipleFile: 'Select files', diff --git a/packages/ui/components/input-file/translations/es.js b/packages/ui/components/input-file/translations/es.js index 5ea291dd86..7718c4fda1 100644 --- a/packages/ui/components/input-file/translations/es.js +++ b/packages/ui/components/input-file/translations/es.js @@ -5,6 +5,9 @@ export default { 'Cargue un archivo {allowedTypesArray} o {allowedTypesLastItem} de {maxSize} como máximo.', dragAndDropText: 'Arrastre y suelte los archivos aquí o', fileNameDescriptionLabel: 'Nombre de archivo: {fileName}', + generalValidatorMessage: '"{validatorMessage}", para {listOfErroneousFiles}.', + noFilesSelected: 'No se han seleccionado archivos.', + numberOfFiles: '{numberOfFiles} archivos.', removeButtonLabel: 'Elimine el archivo: {fileName}', selectTextDuplicateFileName: 'Ya había un archivo con el mismo nombre de archivo.', selectTextMultipleFile: 'Seleccionar archivos', diff --git a/packages/ui/components/input-file/translations/fr.js b/packages/ui/components/input-file/translations/fr.js index d3af1c0352..1d702f5927 100644 --- a/packages/ui/components/input-file/translations/fr.js +++ b/packages/ui/components/input-file/translations/fr.js @@ -6,6 +6,9 @@ export default { 'Veuillez télécharger un fichier {allowedTypesArray} ou {allowedTypesLastItem} avec une taille maximale de {maxSize}.', dragAndDropText: 'Glissez et déposez vos fichiers ici ou', fileNameDescriptionLabel: 'Nom de fichier: {fileName}', + generalValidatorMessage: '"{validatorMessage}", pour {listOfErroneousFiles}.', + noFilesSelected: 'Aucun fichier sélectionné.', + numberOfFiles: '{numberOfFiles} fichiers.', removeButtonLabel: 'Supprimer le fichier {fileName}', selectTextDuplicateFileName: 'Un fichier portant le même nom de fichier était déjà présent.', selectTextMultipleFile: 'Sélectionnez des fichiers', diff --git a/packages/ui/components/input-file/translations/hu.js b/packages/ui/components/input-file/translations/hu.js index e7c26518e3..7540a82698 100644 --- a/packages/ui/components/input-file/translations/hu.js +++ b/packages/ui/components/input-file/translations/hu.js @@ -5,6 +5,9 @@ export default { 'Töltsön fel egy legfeljebb {maxSize} méretű {allowedTypesArray} vagy {allowedTypesLastItem} fájlt.', dragAndDropText: 'Húzza át a fájlokat ide vagy', fileNameDescriptionLabel: 'Fájlnév: {fileName}', + generalValidatorMessage: '"{validatorMessage}", ehhez: {listOfErroneousFiles}.', + noFilesSelected: 'Nincs fájl kiválasztva.', + numberOfFiles: '{numberOfFiles} fájl.', removeButtonLabel: 'A(z) {fileName} fájl eltávolítása', selectTextDuplicateFileName: 'Már volt ilyen nevű fájl.', selectTextMultipleFile: 'Fájl(ok) kiválasztása', diff --git a/packages/ui/components/input-file/translations/it.js b/packages/ui/components/input-file/translations/it.js index 0321377896..48bfc86c40 100644 --- a/packages/ui/components/input-file/translations/it.js +++ b/packages/ui/components/input-file/translations/it.js @@ -5,6 +5,9 @@ export default { 'Caricare un file {allowedTypesArray} o {allowedTypesLastItem} di {maxSize} max.', dragAndDropText: 'Trascinare i file qui o', fileNameDescriptionLabel: 'Nome file: {fileName}', + generalValidatorMessage: '"{validatorMessage}", per {listOfErroneousFiles}.', + noFilesSelected: 'Nessun file selezionato.', + numberOfFiles: '{numberOfFiles} file.', removeButtonLabel: 'Rimuovere il file {fileName}', selectTextDuplicateFileName: 'Un file con lo stesso nome file era già presente.', selectTextMultipleFile: 'Seleziona file', diff --git a/packages/ui/components/input-file/translations/nl.js b/packages/ui/components/input-file/translations/nl.js index b035173fce..71e8414f9e 100644 --- a/packages/ui/components/input-file/translations/nl.js +++ b/packages/ui/components/input-file/translations/nl.js @@ -5,6 +5,9 @@ export default { 'Upload een {allowedTypesArray} of {allowedTypesLastItem}-bestand van maximaal {maxSize}.', dragAndDropText: 'Sleep uw bestanden hierheen of', fileNameDescriptionLabel: 'Bestandsnaam: {fileName}', + generalValidatorMessage: '"{validatorMessage}", voor {listOfErroneousFiles}.', + noFilesSelected: 'Geen bestanden geselecteerd.', + numberOfFiles: '{numberOfFiles} bestanden.', removeButtonLabel: 'Verwijder het bestand {fileName}', selectTextDuplicateFileName: 'Er bestaat al een bestand met dezelfde bestandsnaam.', selectTextMultipleFile: 'Selecteer bestanden', diff --git a/packages/ui/components/input-file/translations/pl.js b/packages/ui/components/input-file/translations/pl.js index 0e7c87cce5..ec899f89e5 100644 --- a/packages/ui/components/input-file/translations/pl.js +++ b/packages/ui/components/input-file/translations/pl.js @@ -5,6 +5,9 @@ export default { 'Prześlij plik {allowedTypesArray} lub {allowedTypesLastItem} o maks. rozmiarze {maxSize}.', dragAndDropText: 'Przeciągnij i upuść pliki tutaj lub', fileNameDescriptionLabel: 'Nazwa pliku: {fileName}', + generalValidatorMessage: '"{validatorMessage}", dla {listOfErroneousFiles}.', + noFilesSelected: 'Nie wybrano żadnych plików.', + numberOfFiles: 'Liczba plików: {numberOfFiles}.', removeButtonLabel: 'Usuń plik {fileName}', selectTextDuplicateFileName: 'Plik o tej samej nazwie już istnieje.', selectTextMultipleFile: 'Wybierz pliki', diff --git a/packages/ui/components/input-file/translations/ro.js b/packages/ui/components/input-file/translations/ro.js index f04bc15862..ddd573b759 100644 --- a/packages/ui/components/input-file/translations/ro.js +++ b/packages/ui/components/input-file/translations/ro.js @@ -5,6 +5,9 @@ export default { 'Încărcaţi un fişier {allowedTypesArray} sau {allowedTypesLastItem} de max. {maxSize}.', dragAndDropText: 'Glisaţi şi fixaţi fişierele aici sau', fileNameDescriptionLabel: 'Nume fişier: {fileName}', + generalValidatorMessage: '"{validatorMessage}", pentru {listOfErroneousFiles}.', + noFilesSelected: 'Niciun fișier selectat.', + numberOfFiles: '{numberOfFiles} fișiere.', removeButtonLabel: 'Eliminaţi fişierul {filename}', selectTextDuplicateFileName: 'Există deja un fişier cu acelaşi nume de fişier.', selectTextMultipleFile: 'Selectare fișiere', diff --git a/packages/ui/components/input-file/translations/ru.js b/packages/ui/components/input-file/translations/ru.js index 62e4a8521e..32cf5df976 100644 --- a/packages/ui/components/input-file/translations/ru.js +++ b/packages/ui/components/input-file/translations/ru.js @@ -5,6 +5,9 @@ export default { 'Загрузите файл {allowedTypesArray} или {allowedTypesLastItem} размером не более {maxSize}.', dragAndDropText: 'Перетащите файлы сюда или', fileNameDescriptionLabel: 'Название файла: {fileName}', + generalValidatorMessage: '"{validatorMessage}", для {listOfErroneousFiles}.', + noFilesSelected: 'Файлы не выбраны.', + numberOfFiles: 'Файлов: {numberOfFiles}.', removeButtonLabel: 'Удалить файл {fileName}', selectTextDuplicateFileName: 'Файл с таким названием уже существует.', selectTextMultipleFile: 'Выберите файлы', diff --git a/packages/ui/components/input-file/translations/sk.js b/packages/ui/components/input-file/translations/sk.js index ad9903c50c..e03f96ac88 100644 --- a/packages/ui/components/input-file/translations/sk.js +++ b/packages/ui/components/input-file/translations/sk.js @@ -5,6 +5,9 @@ export default { 'Nahrajte súbor {allowedTypesArray} alebo {allowedTypesLastItem} s maximálnou veľkosťou {maxSize}.', dragAndDropText: 'Súbory presuňte sem alebo', fileNameDescriptionLabel: 'Názov súboru: {fileName}', + generalValidatorMessage: '"{validatorMessage}", pre {listOfErroneousFiles}.', + noFilesSelected: 'Neboli vybrané žiadne súbory.', + numberOfFiles: 'Počet súborov: {numberOfFiles}.', removeButtonLabel: 'Odstrániť súbor {fileName}', selectTextDuplicateFileName: 'Súbor s rovnakým názvom súboru už existoval.', selectTextMultipleFile: 'Vybrať súbory', diff --git a/packages/ui/components/input-file/translations/uk.js b/packages/ui/components/input-file/translations/uk.js index 45379d7877..fedfdbec6f 100644 --- a/packages/ui/components/input-file/translations/uk.js +++ b/packages/ui/components/input-file/translations/uk.js @@ -5,6 +5,9 @@ export default { 'Завантажте файл {allowedTypesArray} або {allowedTypesLastItem} розміром до {maxSize}.', dragAndDropText: 'Перетягніть файли сюди або', fileNameDescriptionLabel: 'Ім’я файлу: {fileName}', + generalValidatorMessage: '"{validatorMessage}", для {listOfErroneousFiles}.', + noFilesSelected: 'Не вибрано жодного файлу.', + numberOfFiles: '{numberOfFiles} файли(-ів).', removeButtonLabel: 'Видалення файлу {fileName}', selectTextDuplicateFileName: 'Файл із такою назвою вже існував.', selectTextMultipleFile: 'Виберіть файли', diff --git a/packages/ui/components/input-file/translations/zh.js b/packages/ui/components/input-file/translations/zh.js index ee2ad6cd8d..ac7f1e4163 100644 --- a/packages/ui/components/input-file/translations/zh.js +++ b/packages/ui/components/input-file/translations/zh.js @@ -5,6 +5,9 @@ export default { '请上传最大 {maxSize} 的 {allowedTypesArray} 或 {allowedTypesLastItem} 文件。', dragAndDropText: '将您的文件拖放到此处,或', fileNameDescriptionLabel: '文件名: {fileName}', + generalValidatorMessage: '"{validatorMessage}", 例如 {listOfErroneousFiles}。', + noFilesSelected: '未选择任何文件。', + numberOfFiles: '{numberOfFiles} 个文件。', removeButtonLabel: '删除 {fileName} 文件', selectTextDuplicateFileName: '已存在具有相同文件名的文件。', selectTextMultipleFile: '选择多个文件',