Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/input file a11y #2304

Merged
merged 5 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/stupid-falcons-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@lion/ui': patch
---

[input-file] improve a11y labels
76 changes: 58 additions & 18 deletions packages/ui/components/input-file/src/LionInputFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export class LionInputFile extends ScopedElementsMixin(LocalizeMixin(LionField))
>
${this.buttonLabel}
</button>`,
after: () => html`<div data-description></div>`,
'selected-file-list': () => ({
template: html`
<lion-selected-file-list
Expand Down Expand Up @@ -105,8 +106,7 @@ export class LionInputFile extends ScopedElementsMixin(LocalizeMixin(LionField))
}

/**
* The helpt text for the input node.
* When no light dom defined via [slot=help-text], this value will be used
* The label of the button
* @type {string}
*/
get buttonLabel() {
Expand Down Expand Up @@ -332,8 +332,6 @@ export class LionInputFile extends ScopedElementsMixin(LocalizeMixin(LionField))
super.firstUpdated(changedProperties);

this.__setupFileValidators();
// We need to update our light dom
this._enhanceSelectedList();

// TODO: if there's no inputNode by now, we should either throw an error or add a slot change listener
if (this._inputNode) {
Expand Down Expand Up @@ -443,6 +441,7 @@ export class LionInputFile extends ScopedElementsMixin(LocalizeMixin(LionField))
this._selectedFilesMetaData = [...this._selectedFilesMetaData];
}
});
this._updateUploadButtonDescription();
}
}

Expand Down Expand Up @@ -521,20 +520,6 @@ export class LionInputFile extends ScopedElementsMixin(LocalizeMixin(LionField))
ev.target.value = ''; // eslint-disable-line no-param-reassign
}

/**
* @protected
*/
_enhanceSelectedList() {
/**
* @type {LionSelectedFileList | null}
*/
const selectedFileList = this.querySelector('[slot="selected-file-list"]');
if (selectedFileList) {
selectedFileList.setAttribute('id', `selected-file-list-${this._inputId}`);
this.addToAriaDescribedBy(selectedFileList, { idPrefix: 'selected-file-list' });
}
}

/**
* @protected
*/
Expand Down Expand Up @@ -739,6 +724,46 @@ export class LionInputFile extends ScopedElementsMixin(LocalizeMixin(LionField))
fileObj.validationFeedback?.push(errorObj);
}

/**
* Description for screen readers connected to the button about how many files have been updated
* @protected
*/
_updateUploadButtonDescription() {
const erroneousFilesNames = /** @type {string[]} */ ([]);
let errorMessage;

this._selectedFilesMetaData.forEach(file => {
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
Expand All @@ -755,6 +780,7 @@ export class LionInputFile extends ScopedElementsMixin(LocalizeMixin(LionField))
}
this._inputNode.value = '';
this._handleErrors();
this._updateUploadButtonDescription();
}

/**
Expand Down Expand Up @@ -847,6 +873,7 @@ export class LionInputFile extends ScopedElementsMixin(LocalizeMixin(LionField))
_inputGroupInputTemplate() {
return html`
<slot name="input"> </slot>
<slot name="after"> </slot>
${this.enableDropZone && this._isDragAndDropSupported
? this._dropZoneTemplate()
: html`
Expand Down Expand Up @@ -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;
}
`,
];
}
Expand Down
68 changes: 65 additions & 3 deletions packages/ui/components/input-file/test/lion-input-file.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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` <lion-input-file label="Select"></lion-input-file> `);

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` <lion-input-file label="Select"></lion-input-file> `);

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`
<lion-input-file label="Select" multiple></lion-input-file>
`);
// @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.',
);
});
});
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/components/input-file/translations/bg.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ export default {
'Моля, качете файл от тип {allowedTypesArray} или {allowedTypesLastItem} с макс. размер {maxSize}.',
dragAndDropText: 'Плъзнете и пуснете Вашите файлове тук или',
fileNameDescriptionLabel: 'Име на файл: {fileName}',
generalValidatorMessage: '"{validatorMessage}", за {listOfErroneousFiles}.',
noFilesSelected: 'Не са избрани файлове.',
numberOfFiles: '{numberOfFiles} файла.',
removeButtonLabel: 'Отстраняване на файла {fileName}',
selectTextDuplicateFileName: 'Файл със същото име на файл вече е налице.',
selectTextMultipleFile: 'Избор на файлове',
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/components/input-file/translations/cs.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/components/input-file/translations/de.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/components/input-file/translations/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/components/input-file/translations/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/components/input-file/translations/fr.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/components/input-file/translations/hu.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/components/input-file/translations/it.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/components/input-file/translations/nl.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/components/input-file/translations/pl.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/components/input-file/translations/ro.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/components/input-file/translations/ru.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ export default {
'Загрузите файл {allowedTypesArray} или {allowedTypesLastItem} размером не более {maxSize}.',
dragAndDropText: 'Перетащите файлы сюда или',
fileNameDescriptionLabel: 'Название файла: {fileName}',
generalValidatorMessage: '"{validatorMessage}", для {listOfErroneousFiles}.',
noFilesSelected: 'Файлы не выбраны.',
numberOfFiles: 'Файлов: {numberOfFiles}.',
removeButtonLabel: 'Удалить файл {fileName}',
selectTextDuplicateFileName: 'Файл с таким названием уже существует.',
selectTextMultipleFile: 'Выберите файлы',
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/components/input-file/translations/sk.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/components/input-file/translations/uk.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ export default {
'Завантажте файл {allowedTypesArray} або {allowedTypesLastItem} розміром до {maxSize}.',
dragAndDropText: 'Перетягніть файли сюди або',
fileNameDescriptionLabel: 'Ім’я файлу: {fileName}',
generalValidatorMessage: '"{validatorMessage}", для {listOfErroneousFiles}.',
noFilesSelected: 'Не вибрано жодного файлу.',
numberOfFiles: '{numberOfFiles} файли(-ів).',
removeButtonLabel: 'Видалення файлу {fileName}',
selectTextDuplicateFileName: 'Файл із такою назвою вже існував.',
selectTextMultipleFile: 'Виберіть файли',
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/components/input-file/translations/zh.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ export default {
'请上传最大 {maxSize} 的 {allowedTypesArray} 或 {allowedTypesLastItem} 文件。',
dragAndDropText: '将您的文件拖放到此处,或',
fileNameDescriptionLabel: '文件名: {fileName}',
generalValidatorMessage: '"{validatorMessage}", 例如 {listOfErroneousFiles}。',
noFilesSelected: '未选择任何文件。',
numberOfFiles: '{numberOfFiles} 个文件。',
removeButtonLabel: '删除 {fileName} 文件',
selectTextDuplicateFileName: '已存在具有相同文件名的文件。',
selectTextMultipleFile: '选择多个文件',
Expand Down