From f2a4ae963484334427494d00afac04e40c2e0b96 Mon Sep 17 00:00:00 2001 From: Marlene Simondant <msimondant@grandlyon.com> Date: Thu, 25 Apr 2024 10:34:17 +0200 Subject: [PATCH 1/8] WIP --- .../result-list/result-list.component.ts | 8 ++-- .../structure-access-modality.component.html | 1 + .../structure-consent.component.html | 2 + .../structure-labels.component.html | 1 + .../checkbox-form.component.html | 4 +- .../checkbox-form/checkbox-form.component.ts | 1 + .../checkbox/checkbox.component.html | 2 +- .../components/checkbox/checkbox.component.ts | 2 +- .../collapsable-filter.component.html | 3 +- .../collapse-header.component.ts | 1 + .../training-type-picker.component.html | 8 +++- .../more-filters/more-filters.component.html | 3 +- .../structure-list-search.component.html | 5 ++ .../structure-list-search.component.ts | 47 ++++++++++++++++++- 14 files changed, 76 insertions(+), 12 deletions(-) diff --git a/src/app/annuaire/result-list/result-list.component.ts b/src/app/annuaire/result-list/result-list.component.ts index 5a112acbb..152027e06 100644 --- a/src/app/annuaire/result-list/result-list.component.ts +++ b/src/app/annuaire/result-list/result-list.component.ts @@ -35,9 +35,9 @@ export class ResultListComponent implements OnChanges, AfterViewInit { public nextChildIndex: number; ngAfterViewInit(): void { - requestAnimationFrame(() => { + setTimeout(() => { document.getElementById('app-body')?.scrollTo({ top: this.windowScrollService.scrollYToPreserve.value }); - }); + }, 10); } ngOnChanges(changes: SimpleChanges): void { @@ -48,9 +48,9 @@ export class ResultListComponent implements OnChanges, AfterViewInit { // Accessibility: after click on "view more" button, send focus to the first newly displayed member card if (this.keyboardEvent && this.nextChildIndex) { - requestAnimationFrame(() => { + setTimeout(() => { this.setFocusOnFirstNewMemberCard(); - }); + }, 0); } } public goToUser(userId: string): void { diff --git a/src/app/form/form-view/structure-form/structure-access-modality/structure-access-modality.component.html b/src/app/form/form-view/structure-form/structure-access-modality/structure-access-modality.component.html index 1a94130e2..13dde9e44 100644 --- a/src/app/form/form-view/structure-form/structure-access-modality/structure-access-modality.component.html +++ b/src/app/form/form-view/structure-form/structure-access-modality/structure-access-modality.component.html @@ -13,6 +13,7 @@ <app-checkbox-form *ngFor="let module of accessModality.modules" [isChecked]="isInArray('accessModality', module.id)" + [id]="module.id" [text]="module.name" [iconSvg]="module.id" (checkEvent)="onCheckChange($event, 'categories.accessModality', module.id)" diff --git a/src/app/form/form-view/structure-form/structure-consent/structure-consent.component.html b/src/app/form/form-view/structure-form/structure-consent/structure-consent.component.html index c79ef6670..681a013e9 100644 --- a/src/app/form/form-view/structure-form/structure-consent/structure-consent.component.html +++ b/src/app/form/form-view/structure-form/structure-consent/structure-consent.component.html @@ -8,6 +8,7 @@ </div> <app-checkbox-form text="J’accepte que mes informations soient enregistrées" + id="acceptDataBeSaved" (checkEvent)="acceptDataBeSaved($event)" /> </div> @@ -24,6 +25,7 @@ <app-checkbox-form *ngIf="!isEditMode" text="J’accepte de partager les données de ma structure" + [id]="'acceptOpenData'" [isChecked]="false" (checkEvent)="acceptOpenData($event)" /> diff --git a/src/app/form/form-view/structure-form/structure-labels/structure-labels.component.html b/src/app/form/form-view/structure-form/structure-labels/structure-labels.component.html index d3a639bf2..1a4ac7f1c 100644 --- a/src/app/form/form-view/structure-form/structure-labels/structure-labels.component.html +++ b/src/app/form/form-view/structure-form/structure-labels/structure-labels.component.html @@ -8,6 +8,7 @@ <app-checkbox-form *ngFor="let module of labelsQualifications.modules.sort()" [isChecked]="isInArray(module.id, 'labelsQualifications')" + [id]="module.id" [text]="module.name" [iconSvg]="module.id" [iconType]="'labels'" diff --git a/src/app/shared/components/checkbox-form/checkbox-form.component.html b/src/app/shared/components/checkbox-form/checkbox-form.component.html index 9a5c736eb..11e104196 100644 --- a/src/app/shared/components/checkbox-form/checkbox-form.component.html +++ b/src/app/shared/components/checkbox-form/checkbox-form.component.html @@ -1,9 +1,9 @@ <div class="checkbox" tabindex="-1" [ngClass]="{ selected: isChecked }" (click)="clicked()" (keydown.enter)="clicked()"> - <app-checkbox [checked]="isChecked" /> + <app-checkbox [checked]="isChecked" [id]="id" /> <svg *ngIf="iconSvg" aria-hidden="true" class="icon" [ngClass]="iconType"> <use [attr.xlink:href]="'assets/form/sprite.svg#' + iconSvg" /> </svg> - <p id="checkboxLabel">{{ text }}</p> + <p id="{{ id }}">{{ text }}</p> </div> diff --git a/src/app/shared/components/checkbox-form/checkbox-form.component.ts b/src/app/shared/components/checkbox-form/checkbox-form.component.ts index 81f6046b0..553dfefec 100644 --- a/src/app/shared/components/checkbox-form/checkbox-form.component.ts +++ b/src/app/shared/components/checkbox-form/checkbox-form.component.ts @@ -6,6 +6,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; styleUrls: ['./checkbox-form.component.scss'], }) export class CheckboxFormComponent { + @Input({ required: true }) public id: string; @Input() public isChecked = false; @Input() public text: string; @Input() public iconSvg: string; diff --git a/src/app/shared/components/checkbox/checkbox.component.html b/src/app/shared/components/checkbox/checkbox.component.html index c2185f1c7..f0a9237a8 100644 --- a/src/app/shared/components/checkbox/checkbox.component.html +++ b/src/app/shared/components/checkbox/checkbox.component.html @@ -5,6 +5,6 @@ [checked]="checked" [indeterminate]="indeterminate" [disabled]="disabled" - [attr.aria-labelledby]="'checkboxLabel'" + [attr.aria-labelledby]="id" (click)="action.emit($event)" /> diff --git a/src/app/shared/components/checkbox/checkbox.component.ts b/src/app/shared/components/checkbox/checkbox.component.ts index 04cc9244c..7e25394b7 100644 --- a/src/app/shared/components/checkbox/checkbox.component.ts +++ b/src/app/shared/components/checkbox/checkbox.component.ts @@ -7,7 +7,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; }) export class CheckboxComponent { /** HTML id associated with for */ - @Input() id: string; + @Input({ required: true }) id: string; /** Checked ? */ @Input({ required: true }) checked: boolean; diff --git a/src/app/shared/components/collapsable-filter/collapsable-filter.component.html b/src/app/shared/components/collapsable-filter/collapsable-filter.component.html index 75c95e428..21e02b156 100644 --- a/src/app/shared/components/collapsable-filter/collapsable-filter.component.html +++ b/src/app/shared/components/collapsable-filter/collapsable-filter.component.html @@ -1,6 +1,7 @@ <button type="button" - [attr.aria-label]="'Déplier les filtres : ' + label" + aria-haspopup="true" + [attr.aria-label]="label + 'Déplier les filtres : '" [ngClass]="{ expanded: expanded, active: active diff --git a/src/app/shared/components/collapse/collapse-header/collapse-header.component.ts b/src/app/shared/components/collapse/collapse-header/collapse-header.component.ts index ec62e8b0e..9683c91c0 100644 --- a/src/app/shared/components/collapse/collapse-header/collapse-header.component.ts +++ b/src/app/shared/components/collapse/collapse-header/collapse-header.component.ts @@ -5,6 +5,7 @@ import { Component, EventEmitter, Output } from '@angular/core'; template: `<div class="collapse-header" role="button" + aria-haspopup="true" tabindex="0" [ngClass]="size" (click)="toggle.emit()" diff --git a/src/app/shared/components/training-type-picker/training-type-picker.component.html b/src/app/shared/components/training-type-picker/training-type-picker.component.html index 03741b86b..840073000 100644 --- a/src/app/shared/components/training-type-picker/training-type-picker.component.html +++ b/src/app/shared/components/training-type-picker/training-type-picker.component.html @@ -4,10 +4,16 @@ <div class="collapseHeader"> <app-checkbox [checked]="getCategoryCheckboxStatus(category) === 'checked'" + [id]="category.id" [indeterminate]="getCategoryCheckboxStatus(category) === 'halfChecked'" (action)="pickAllCategory(category); $event.stopPropagation()" /> - <span class="titleCollapse">{{ category.name }}</span> + <span + class="titleCollapse" + id="{{ category.id }}" + [attr.aria-label]="category.name + '. Cocher pour tout sélectionner'" + >{{ category.name }}</span + > </div> </app-collapse-header> <app-collapse-content> diff --git a/src/app/structure-list/components/more-filters/more-filters.component.html b/src/app/structure-list/components/more-filters/more-filters.component.html index 9edc35ba1..40035414c 100644 --- a/src/app/structure-list/components/more-filters/more-filters.component.html +++ b/src/app/structure-list/components/more-filters/more-filters.component.html @@ -33,11 +33,12 @@ <div class="collapseHeader"> <app-checkbox [size]="'medium'" + [id]="c.id" [checked]="getCategoryCheckboxStatus(c) === 'checked'" [indeterminate]="getCategoryCheckboxStatus(c) === 'halfChecked'" (action)="handleCategoryCheckBox($event, c); $event.stopPropagation()" /> - <span>{{ c.name }}</span> + <span id="{{ c.id }}" [attr.aria-label]="c.name + '. Cocher pour tout sélectionner'">{{ c.name }}</span> </div> </app-collapse-header> <app-collapse-content> diff --git a/src/app/structure-list/components/structure-list-search/structure-list-search.component.html b/src/app/structure-list/components/structure-list-search/structure-list-search.component.html index c8af2c9b6..ec9d1c49d 100644 --- a/src/app/structure-list/components/structure-list-search/structure-list-search.component.html +++ b/src/app/structure-list/components/structure-list-search/structure-list-search.component.html @@ -7,24 +7,28 @@ [expanded]="modalTypeOpened === TypeModal.accompaniment" [active]="numberAccompanimentChecked > 0" (toggle)="openModal(TypeModal.accompaniment)" + (keyup)="onKeyboardNavigation($event)" /> <app-collapsable-filter [label]="'Compétences numériques'" [expanded]="modalTypeOpened === TypeModal.training" [active]="numberTrainingChecked > 0" (toggle)="openModal(TypeModal.training)" + (keyup)="onKeyboardNavigation($event)" /> <app-collapsable-filter [label]="'Public'" [expanded]="modalTypeOpened === TypeModal.public" [active]="numberPublicChecked > 0" (toggle)="openModal(TypeModal.public)" + (keyup)="onKeyboardNavigation($event)" /> <app-collapsable-filter [label]="'Matériel & wifi'" [expanded]="modalTypeOpened === TypeModal.equipments" [active]="numberEquipmentChecked > 0" (toggle)="openModal(TypeModal.equipments)" + (keyup)="onKeyboardNavigation($event)" /> <app-checkbox-filter [module]="{ @@ -51,6 +55,7 @@ [modules]="checkedModulesFilter" (searchEvent)="fetchResults($event)" (closeEvent)="closeModal()" + (keyup)="onKeyboardNavigation($event)" /> </div> </div> diff --git a/src/app/structure-list/components/structure-list-search/structure-list-search.component.ts b/src/app/structure-list/components/structure-list-search/structure-list-search.component.ts index 9c0aeeabd..a19588e8c 100644 --- a/src/app/structure-list/components/structure-list-search/structure-list-search.component.ts +++ b/src/app/structure-list/components/structure-list-search/structure-list-search.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, ElementRef, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { Utils } from '../../../utils/utils'; import { Theme } from '../../enum/themes.enum'; @@ -38,12 +38,14 @@ export class StructureListSearchComponent implements OnInit { public prmAdded = false; public hasUrlParams = false; + public keyboardEvent = false; constructor( public searchService: SearchService, private activatedRoute: ActivatedRoute, private route: ActivatedRoute, private router: Router, + private elementRef: ElementRef, ) {} ngOnInit(): void { this.searchInput = this.activatedRoute.snapshot.queryParamMap.get('query'); @@ -175,11 +177,54 @@ export class StructureListSearchComponent implements OnInit { // if modal already opened, reset type if (this.modalTypeOpened === modalType) { this.closeModal(); + // Accessibility: when navigating with keyboard and closing a filter modal, send focus back to filters + if (this.keyboardEvent) { + this.setFocusOnFilters(); + } } else if (this.modalTypeOpened !== modalType) { this.modalTypeOpened = modalType; + + // Accessibility: when navigating with keyboard and opening a filter modal, send focus to the first focusable element of the opened modal + if (this.keyboardEvent) { + this.setFocusOnOpenedModal(); + } + } + } + + // When filters and their modal are in the same component, we can remove onKeyboardNavigation, setFocusOnOpenedModal, and setFocusOnFilters. + // because the focus will then flow normally between the filter and the modal + public onKeyboardNavigation(event: KeyboardEvent): void { + switch (event.key) { + case 'ArrowUp': + case 'ArrowDown': + case 'Tab': + this.keyboardEvent = true; + break; } } + private setFocusOnOpenedModal(): void { + setTimeout(() => { + const modalFirstFocusableElement = this.elementRef.nativeElement.querySelector( + `.modalContent input, .modalContent button`, + ); + if (modalFirstFocusableElement) { + const focusedElement = modalFirstFocusableElement as HTMLElement; + focusedElement.focus(); + } + }, 0); + } + + private setFocusOnFilters(): void { + setTimeout(() => { + const firstfilter = this.elementRef.nativeElement.querySelector(`.filters button`); + if (firstfilter) { + const focusedElement = firstfilter as HTMLElement; + focusedElement.focus(); + } + }, 0); + } + public closeModal(): void { this.modalTypeOpened = undefined; } -- GitLab From 599a44b7a0324cf4f4644ee92686a03ff8477543 Mon Sep 17 00:00:00 2001 From: Marlene Simondant <msimondant@grandlyon.com> Date: Thu, 25 Apr 2024 14:55:22 +0200 Subject: [PATCH 2/8] wip improve checkbox labels --- .../components/checkbox-form/checkbox-form.component.html | 2 +- src/app/shared/components/checkbox/checkbox.component.html | 2 +- .../training-type-picker/training-type-picker.component.html | 2 +- .../components/more-filters/more-filters.component.html | 4 +++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/app/shared/components/checkbox-form/checkbox-form.component.html b/src/app/shared/components/checkbox-form/checkbox-form.component.html index 11e104196..6b56b0e7f 100644 --- a/src/app/shared/components/checkbox-form/checkbox-form.component.html +++ b/src/app/shared/components/checkbox-form/checkbox-form.component.html @@ -5,5 +5,5 @@ <use [attr.xlink:href]="'assets/form/sprite.svg#' + iconSvg" /> </svg> - <p id="{{ id }}">{{ text }}</p> + <p id="label-{{ id }}">{{ text }}</p> </div> diff --git a/src/app/shared/components/checkbox/checkbox.component.html b/src/app/shared/components/checkbox/checkbox.component.html index f0a9237a8..5460622bc 100644 --- a/src/app/shared/components/checkbox/checkbox.component.html +++ b/src/app/shared/components/checkbox/checkbox.component.html @@ -5,6 +5,6 @@ [checked]="checked" [indeterminate]="indeterminate" [disabled]="disabled" - [attr.aria-labelledby]="id" + [attr.aria-labelledby]="'label-' + id" (click)="action.emit($event)" /> diff --git a/src/app/shared/components/training-type-picker/training-type-picker.component.html b/src/app/shared/components/training-type-picker/training-type-picker.component.html index 840073000..0f96db8bc 100644 --- a/src/app/shared/components/training-type-picker/training-type-picker.component.html +++ b/src/app/shared/components/training-type-picker/training-type-picker.component.html @@ -10,7 +10,7 @@ /> <span class="titleCollapse" - id="{{ category.id }}" + id="label-{{ category.id }}" [attr.aria-label]="category.name + '. Cocher pour tout sélectionner'" >{{ category.name }}</span > diff --git a/src/app/structure-list/components/more-filters/more-filters.component.html b/src/app/structure-list/components/more-filters/more-filters.component.html index 40035414c..2820d736f 100644 --- a/src/app/structure-list/components/more-filters/more-filters.component.html +++ b/src/app/structure-list/components/more-filters/more-filters.component.html @@ -38,7 +38,9 @@ [indeterminate]="getCategoryCheckboxStatus(c) === 'halfChecked'" (action)="handleCategoryCheckBox($event, c); $event.stopPropagation()" /> - <span id="{{ c.id }}" [attr.aria-label]="c.name + '. Cocher pour tout sélectionner'">{{ c.name }}</span> + <span id="label-{{ c.id }}" [attr.aria-label]="c.name + '. Cocher pour tout sélectionner'">{{ + c.name + }}</span> </div> </app-collapse-header> <app-collapse-content> -- GitLab From 8e75ae85ae8a1c582896d4c4ac107bdb0d597e4f Mon Sep 17 00:00:00 2001 From: Marlene Simondant <msimondant@grandlyon.com> Date: Thu, 25 Apr 2024 15:03:34 +0200 Subject: [PATCH 3/8] wip improve checkbox labels --- .../components/checkbox-form/checkbox-form.component.html | 2 +- .../components/checkbox-form/checkbox-form.component.scss | 2 +- src/app/shared/components/checkbox/checkbox.component.html | 1 - .../training-type-picker.component.html | 6 +++--- .../components/more-filters/more-filters.component.html | 4 +--- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/app/shared/components/checkbox-form/checkbox-form.component.html b/src/app/shared/components/checkbox-form/checkbox-form.component.html index 6b56b0e7f..7ebc8d9dc 100644 --- a/src/app/shared/components/checkbox-form/checkbox-form.component.html +++ b/src/app/shared/components/checkbox-form/checkbox-form.component.html @@ -5,5 +5,5 @@ <use [attr.xlink:href]="'assets/form/sprite.svg#' + iconSvg" /> </svg> - <p id="label-{{ id }}">{{ text }}</p> + <label for="{{ id }}">{{ text }}</label> </div> diff --git a/src/app/shared/components/checkbox-form/checkbox-form.component.scss b/src/app/shared/components/checkbox-form/checkbox-form.component.scss index 0078cd152..eb1760740 100644 --- a/src/app/shared/components/checkbox-form/checkbox-form.component.scss +++ b/src/app/shared/components/checkbox-form/checkbox-form.component.scss @@ -22,7 +22,7 @@ div.checkbox { min-width: 44px; } - p { + label { @include font-bold-16; color: $grey-1; text-align: left; diff --git a/src/app/shared/components/checkbox/checkbox.component.html b/src/app/shared/components/checkbox/checkbox.component.html index 5460622bc..17d7e6d70 100644 --- a/src/app/shared/components/checkbox/checkbox.component.html +++ b/src/app/shared/components/checkbox/checkbox.component.html @@ -5,6 +5,5 @@ [checked]="checked" [indeterminate]="indeterminate" [disabled]="disabled" - [attr.aria-labelledby]="'label-' + id" (click)="action.emit($event)" /> diff --git a/src/app/shared/components/training-type-picker/training-type-picker.component.html b/src/app/shared/components/training-type-picker/training-type-picker.component.html index 0f96db8bc..a62a4c18e 100644 --- a/src/app/shared/components/training-type-picker/training-type-picker.component.html +++ b/src/app/shared/components/training-type-picker/training-type-picker.component.html @@ -8,11 +8,11 @@ [indeterminate]="getCategoryCheckboxStatus(category) === 'halfChecked'" (action)="pickAllCategory(category); $event.stopPropagation()" /> - <span + <label class="titleCollapse" - id="label-{{ category.id }}" + for="{{ category.id }}" [attr.aria-label]="category.name + '. Cocher pour tout sélectionner'" - >{{ category.name }}</span + >{{ category.name }}</label > </div> </app-collapse-header> diff --git a/src/app/structure-list/components/more-filters/more-filters.component.html b/src/app/structure-list/components/more-filters/more-filters.component.html index 2820d736f..bda9529a0 100644 --- a/src/app/structure-list/components/more-filters/more-filters.component.html +++ b/src/app/structure-list/components/more-filters/more-filters.component.html @@ -38,9 +38,7 @@ [indeterminate]="getCategoryCheckboxStatus(c) === 'halfChecked'" (action)="handleCategoryCheckBox($event, c); $event.stopPropagation()" /> - <span id="label-{{ c.id }}" [attr.aria-label]="c.name + '. Cocher pour tout sélectionner'">{{ - c.name - }}</span> + <label for="{{ c.id }}" [attr.aria-label]="c.name + '. Cocher pour tout sélectionner'">{{ c.name }}</label> </div> </app-collapse-header> <app-collapse-content> -- GitLab From 0e1155ccf05b6431dc0cf9ee3205f59e6d64bc57 Mon Sep 17 00:00:00 2001 From: Marlene Simondant <msimondant@grandlyon.com> Date: Thu, 25 Apr 2024 16:11:50 +0200 Subject: [PATCH 4/8] wip --- .../filter-modal/filter-modal.component.html | 7 ++++++- .../more-filters/more-filters.component.html | 7 ++++++- .../structure-list-search.component.ts | 14 +++++++------- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/app/annuaire/filter-modal/filter-modal.component.html b/src/app/annuaire/filter-modal/filter-modal.component.html index 5031c5b06..6798808c8 100644 --- a/src/app/annuaire/filter-modal/filter-modal.component.html +++ b/src/app/annuaire/filter-modal/filter-modal.component.html @@ -12,7 +12,12 @@ </div> <div class="modalFooter"> - <app-button [variant]="'secondary'" [label]="'Effacer'" (action)="clearFilters()" /> + <app-button + [variant]="'secondary'" + [label]="'Effacer'" + [attr.aria-label]="'Effacer et fermer'" + (action)="clearFilters()" + /> <app-button [variant]="'primary'" [label]="'Appliquer'" (action)="onSubmitFilters()" /> </div> </div> diff --git a/src/app/structure-list/components/more-filters/more-filters.component.html b/src/app/structure-list/components/more-filters/more-filters.component.html index bda9529a0..71f0a0858 100644 --- a/src/app/structure-list/components/more-filters/more-filters.component.html +++ b/src/app/structure-list/components/more-filters/more-filters.component.html @@ -56,7 +56,12 @@ </app-collapse> </div> <div class="modalFooter"> - <app-button [variant]="'secondary'" [label]="'Effacer'" (action)="clearFilters()" /> + <app-button + [variant]="'secondary'" + [label]="'Effacer'" + [attr.aria-label]="'Effacer et fermer'" + (action)="clearFilters()" + /> <app-button [variant]="'primary'" [label]="'Appliquer'" (action)="emitModules(checkedModules)" /> </div> </div> diff --git a/src/app/structure-list/components/structure-list-search/structure-list-search.component.ts b/src/app/structure-list/components/structure-list-search/structure-list-search.component.ts index a19588e8c..77b01766b 100644 --- a/src/app/structure-list/components/structure-list-search/structure-list-search.component.ts +++ b/src/app/structure-list/components/structure-list-search/structure-list-search.component.ts @@ -177,10 +177,6 @@ export class StructureListSearchComponent implements OnInit { // if modal already opened, reset type if (this.modalTypeOpened === modalType) { this.closeModal(); - // Accessibility: when navigating with keyboard and closing a filter modal, send focus back to filters - if (this.keyboardEvent) { - this.setFocusOnFilters(); - } } else if (this.modalTypeOpened !== modalType) { this.modalTypeOpened = modalType; @@ -217,15 +213,19 @@ export class StructureListSearchComponent implements OnInit { private setFocusOnFilters(): void { setTimeout(() => { - const firstfilter = this.elementRef.nativeElement.querySelector(`.filters button`); - if (firstfilter) { - const focusedElement = firstfilter as HTMLElement; + const firstFilter = this.elementRef.nativeElement.querySelector('.filters button:first-of-type'); + if (firstFilter) { + const focusedElement = firstFilter as HTMLElement; focusedElement.focus(); } }, 0); } public closeModal(): void { + // Accessibility: when navigating with keyboard and closing a filter modal, send focus back to filters + if (this.keyboardEvent) { + this.setFocusOnFilters(); + } this.modalTypeOpened = undefined; } -- GitLab From 6e4f753ae8618292141bfd94215428d0b47fc694 Mon Sep 17 00:00:00 2001 From: Marlene Simondant <msimondant@grandlyon.com> Date: Thu, 25 Apr 2024 16:31:13 +0200 Subject: [PATCH 5/8] wip --- .../structure-list-search.component.html | 11 ++++++++++- .../structure-list-search.component.ts | 14 +++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/app/structure-list/components/structure-list-search/structure-list-search.component.html b/src/app/structure-list/components/structure-list-search/structure-list-search.component.html index ec9d1c49d..1352ff43e 100644 --- a/src/app/structure-list/components/structure-list-search/structure-list-search.component.html +++ b/src/app/structure-list/components/structure-list-search/structure-list-search.component.html @@ -5,6 +5,7 @@ <app-collapsable-filter [label]="'Démarches en ligne'" [expanded]="modalTypeOpened === TypeModal.accompaniment" + [id]="'modal' + TypeModal.accompaniment" [active]="numberAccompanimentChecked > 0" (toggle)="openModal(TypeModal.accompaniment)" (keyup)="onKeyboardNavigation($event)" @@ -12,6 +13,7 @@ <app-collapsable-filter [label]="'Compétences numériques'" [expanded]="modalTypeOpened === TypeModal.training" + [id]="'modal' + TypeModal.training" [active]="numberTrainingChecked > 0" (toggle)="openModal(TypeModal.training)" (keyup)="onKeyboardNavigation($event)" @@ -19,6 +21,7 @@ <app-collapsable-filter [label]="'Public'" [expanded]="modalTypeOpened === TypeModal.public" + [id]="'modal' + TypeModal.public" [active]="numberPublicChecked > 0" (toggle)="openModal(TypeModal.public)" (keyup)="onKeyboardNavigation($event)" @@ -26,6 +29,7 @@ <app-collapsable-filter [label]="'Matériel & wifi'" [expanded]="modalTypeOpened === TypeModal.equipments" + [id]="'modal' + TypeModal.equipments" [active]="numberEquipmentChecked > 0" (toggle)="openModal(TypeModal.equipments)" (keyup)="onKeyboardNavigation($event)" @@ -47,7 +51,12 @@ [checked]="searchService.getIndex(checkedModulesFilter, 'accesLibre', 'accessModality') > -1" (toggle)="externalCheckboxCheck($event)" /> - <app-button [variant]="'tertiary'" [label]="'Plus de filtres'" (action)="openModal(TypeModal.moreFilters)" /> + <app-button + [variant]="'tertiary'" + [label]="'Plus de filtres'" + [id]="'modal' + TypeModal.moreFilters" + (action)="openModal(TypeModal.moreFilters)" + /> <div *ngIf="modalTypeOpened"> <app-more-filters [modalType]="modalTypeOpened" diff --git a/src/app/structure-list/components/structure-list-search/structure-list-search.component.ts b/src/app/structure-list/components/structure-list-search/structure-list-search.component.ts index 77b01766b..a8e3d48c2 100644 --- a/src/app/structure-list/components/structure-list-search/structure-list-search.component.ts +++ b/src/app/structure-list/components/structure-list-search/structure-list-search.component.ts @@ -212,13 +212,13 @@ export class StructureListSearchComponent implements OnInit { } private setFocusOnFilters(): void { - setTimeout(() => { - const firstFilter = this.elementRef.nativeElement.querySelector('.filters button:first-of-type'); - if (firstFilter) { - const focusedElement = firstFilter as HTMLElement; - focusedElement.focus(); - } - }, 0); + const firstFilter = this.elementRef.nativeElement.querySelector( + '#modal' + this.modalTypeOpened + ' button:first-of-type', + ); + if (firstFilter) { + const focusedElement = firstFilter as HTMLElement; + focusedElement.focus(); + } } public closeModal(): void { -- GitLab From 9cd11cc7a21a44ff14ba0d8170881c0faf4b0476 Mon Sep 17 00:00:00 2001 From: Marlene Simondant <msimondant@grandlyon.com> Date: Thu, 25 Apr 2024 16:41:07 +0200 Subject: [PATCH 6/8] renaming on clarity --- .../structure-list-search.component.html | 10 +++++----- .../structure-list-search.component.ts | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/app/structure-list/components/structure-list-search/structure-list-search.component.html b/src/app/structure-list/components/structure-list-search/structure-list-search.component.html index 1352ff43e..07de08215 100644 --- a/src/app/structure-list/components/structure-list-search/structure-list-search.component.html +++ b/src/app/structure-list/components/structure-list-search/structure-list-search.component.html @@ -8,7 +8,7 @@ [id]="'modal' + TypeModal.accompaniment" [active]="numberAccompanimentChecked > 0" (toggle)="openModal(TypeModal.accompaniment)" - (keyup)="onKeyboardNavigation($event)" + (keyup)="onKeyboardNavOnFilters($event)" /> <app-collapsable-filter [label]="'Compétences numériques'" @@ -16,7 +16,7 @@ [id]="'modal' + TypeModal.training" [active]="numberTrainingChecked > 0" (toggle)="openModal(TypeModal.training)" - (keyup)="onKeyboardNavigation($event)" + (keyup)="onKeyboardNavOnFilters($event)" /> <app-collapsable-filter [label]="'Public'" @@ -24,7 +24,7 @@ [id]="'modal' + TypeModal.public" [active]="numberPublicChecked > 0" (toggle)="openModal(TypeModal.public)" - (keyup)="onKeyboardNavigation($event)" + (keyup)="onKeyboardNavOnFilters($event)" /> <app-collapsable-filter [label]="'Matériel & wifi'" @@ -32,7 +32,7 @@ [id]="'modal' + TypeModal.equipments" [active]="numberEquipmentChecked > 0" (toggle)="openModal(TypeModal.equipments)" - (keyup)="onKeyboardNavigation($event)" + (keyup)="onKeyboardNavOnFilters($event)" /> <app-checkbox-filter [module]="{ @@ -64,7 +64,7 @@ [modules]="checkedModulesFilter" (searchEvent)="fetchResults($event)" (closeEvent)="closeModal()" - (keyup)="onKeyboardNavigation($event)" + (keyup)="onKeyboardNavOnFilters($event)" /> </div> </div> diff --git a/src/app/structure-list/components/structure-list-search/structure-list-search.component.ts b/src/app/structure-list/components/structure-list-search/structure-list-search.component.ts index a8e3d48c2..d3b3e0024 100644 --- a/src/app/structure-list/components/structure-list-search/structure-list-search.component.ts +++ b/src/app/structure-list/components/structure-list-search/structure-list-search.component.ts @@ -189,7 +189,7 @@ export class StructureListSearchComponent implements OnInit { // When filters and their modal are in the same component, we can remove onKeyboardNavigation, setFocusOnOpenedModal, and setFocusOnFilters. // because the focus will then flow normally between the filter and the modal - public onKeyboardNavigation(event: KeyboardEvent): void { + public onKeyboardNavOnFilters(event: KeyboardEvent): void { switch (event.key) { case 'ArrowUp': case 'ArrowDown': @@ -211,12 +211,12 @@ export class StructureListSearchComponent implements OnInit { }, 0); } - private setFocusOnFilters(): void { - const firstFilter = this.elementRef.nativeElement.querySelector( + private setFocusOnFilter(): void { + const filterButton = this.elementRef.nativeElement.querySelector( '#modal' + this.modalTypeOpened + ' button:first-of-type', ); - if (firstFilter) { - const focusedElement = firstFilter as HTMLElement; + if (filterButton) { + const focusedElement = filterButton as HTMLElement; focusedElement.focus(); } } @@ -224,7 +224,7 @@ export class StructureListSearchComponent implements OnInit { public closeModal(): void { // Accessibility: when navigating with keyboard and closing a filter modal, send focus back to filters if (this.keyboardEvent) { - this.setFocusOnFilters(); + this.setFocusOnFilter(); } this.modalTypeOpened = undefined; } -- GitLab From afdd08f345e13e4943acefb9dfd6b9d14e7a6475 Mon Sep 17 00:00:00 2001 From: Marlene Simondant <msimondant@grandlyon.com> Date: Mon, 29 Apr 2024 10:53:16 +0200 Subject: [PATCH 7/8] update comment --- .../structure-list-search/structure-list-search.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/structure-list/components/structure-list-search/structure-list-search.component.ts b/src/app/structure-list/components/structure-list-search/structure-list-search.component.ts index d3b3e0024..15dbc16db 100644 --- a/src/app/structure-list/components/structure-list-search/structure-list-search.component.ts +++ b/src/app/structure-list/components/structure-list-search/structure-list-search.component.ts @@ -187,7 +187,7 @@ export class StructureListSearchComponent implements OnInit { } } - // When filters and their modal are in the same component, we can remove onKeyboardNavigation, setFocusOnOpenedModal, and setFocusOnFilters. + // When filters and their modal are in the same component, we can remove onKeyboardNavOnFilters, setFocusOnOpenedModal, and setFocusOnFilters. // because the focus will then flow normally between the filter and the modal public onKeyboardNavOnFilters(event: KeyboardEvent): void { switch (event.key) { -- GitLab From f67d8b791825eb06deae30652347493c1eb48108 Mon Sep 17 00:00:00 2001 From: Marlene Simondant <msimondant@grandlyon.com> Date: Mon, 29 Apr 2024 11:02:08 +0200 Subject: [PATCH 8/8] fix aria label buttons --- src/app/annuaire/filter-modal/filter-modal.component.html | 2 +- .../components/more-filters/more-filters.component.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/annuaire/filter-modal/filter-modal.component.html b/src/app/annuaire/filter-modal/filter-modal.component.html index 6798808c8..56163e779 100644 --- a/src/app/annuaire/filter-modal/filter-modal.component.html +++ b/src/app/annuaire/filter-modal/filter-modal.component.html @@ -15,7 +15,7 @@ <app-button [variant]="'secondary'" [label]="'Effacer'" - [attr.aria-label]="'Effacer et fermer'" + [ariaLabel]="'Effacer et fermer'" (action)="clearFilters()" /> <app-button [variant]="'primary'" [label]="'Appliquer'" (action)="onSubmitFilters()" /> diff --git a/src/app/structure-list/components/more-filters/more-filters.component.html b/src/app/structure-list/components/more-filters/more-filters.component.html index 71f0a0858..8c8dd97c7 100644 --- a/src/app/structure-list/components/more-filters/more-filters.component.html +++ b/src/app/structure-list/components/more-filters/more-filters.component.html @@ -59,7 +59,7 @@ <app-button [variant]="'secondary'" [label]="'Effacer'" - [attr.aria-label]="'Effacer et fermer'" + [ariaLabel]="'Effacer et fermer'" (action)="clearFilters()" /> <app-button [variant]="'primary'" [label]="'Appliquer'" (action)="emitModules(checkedModules)" /> -- GitLab