ACS-3550 Fixed accessibility issues related with move and copy dialogs (#7961)

* ACS-3550 Fixed accessibility issues related with move and copy dialogs

* ACS-3550 Revert changes for button role for cells

* ACS-3550 Shorter css rule

* ACS-3550 Fixed lint issues

* ACS-3550 Changed selector to fix e2e tests
This commit is contained in:
AleksanderSklorz 2022-11-11 21:20:05 +01:00 committed by GitHub
parent 1109a73a19
commit ba05d3a1df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 55 additions and 18 deletions

View File

@ -3,6 +3,7 @@
[attr.aria-label]="'BREADCRUMB.ARIA-LABEL.BREADCRUMB' | translate"> [attr.aria-label]="'BREADCRUMB.ARIA-LABEL.BREADCRUMB' | translate">
<button <button
id="dropdown-breadcrumb-button"
[tabindex]="hasPreviousNodes() ? 0 : -1" [tabindex]="hasPreviousNodes() ? 0 : -1"
class="adf-dropdown-breadcrumb-trigger" class="adf-dropdown-breadcrumb-trigger"
(click)="open()" (click)="open()"
@ -18,7 +19,8 @@
#dropdown #dropdown
*ngIf="hasPreviousNodes()" *ngIf="hasPreviousNodes()"
tabindex="-1" tabindex="-1"
data-automation-id="dropdown-breadcrumb-path"> data-automation-id="dropdown-breadcrumb-path"
aria-labelledby="dropdown-breadcrumb-button">
<mat-option <mat-option
*ngFor="let node of previousNodes;" *ngFor="let node of previousNodes;"

View File

@ -47,7 +47,7 @@
</div> </div>
</ng-container> </ng-container>
<ng-template #loading> <ng-template #loading>
<mat-progress-bar mode="indeterminate" [attr.aria-label]="'CORE.METADATA.BASIC.DATA_LOADING' | translate"> <mat-progress-bar mode="indeterminate" [attr.aria-label]="'DATA_LOADING' | translate">
</mat-progress-bar> </mat-progress-bar>
</ng-template> </ng-template>
</ng-container> </ng-container>

View File

@ -45,7 +45,7 @@
<adf-toolbar> <adf-toolbar>
<adf-toolbar-title> <adf-toolbar-title>
<ng-container *ngIf="!showBreadcrumbs()"> <ng-container *ngIf="!showBreadcrumbs()">
<span role="heading" aria-level="3" class="adf-search-results-label">{{ 'NODE_SELECTOR.SEARCH_RESULTS' | translate }}</span> <h2 class="adf-search-results-label">{{ 'NODE_SELECTOR.SEARCH_RESULTS' | translate }}</h2>
</ng-container> </ng-container>
<adf-dropdown-breadcrumb *ngIf="showBreadcrumbs()" <adf-dropdown-breadcrumb *ngIf="showBreadcrumbs()"
class="adf-content-node-selector-content-breadcrumb" class="adf-content-node-selector-content-breadcrumb"
@ -86,10 +86,11 @@
(folderChange)="onFolderChange($event)" (folderChange)="onFolderChange($event)"
(ready)="onFolderLoaded($event)" (ready)="onFolderLoaded($event)"
(nodeSelected)="onCurrentSelection($event)" (nodeSelected)="onCurrentSelection($event)"
[class.adf-content-node-selector-content-list-empty]="emptyList"
data-automation-id="content-node-selector-document-list"> data-automation-id="content-node-selector-document-list">
<adf-custom-empty-content-template> <adf-custom-empty-content-template>
<div>{{ 'NODE_SELECTOR.NO_RESULTS' | translate }}</div> <div aria-live="polite">{{ 'NODE_SELECTOR.NO_RESULTS' | translate }}</div>
</adf-custom-empty-content-template> </adf-custom-empty-content-template>
<data-columns> <data-columns>

View File

@ -1,7 +1,7 @@
/* stylelint-disable no-descending-specificity */ /* stylelint-disable no-descending-specificity */
$content-node-selector-thumbnail-width: 35px !default; $content-node-selector-thumbnail-width: 35px !default;
.adf-search-results-label { h2.adf-search-results-label {
flex: 1; flex: 1;
font-weight: 600; font-weight: 600;
font-size: var(--theme-body-1-font-size); font-size: var(--theme-body-1-font-size);
@ -95,6 +95,13 @@ $content-node-selector-thumbnail-width: 35px !default;
overflow: auto; overflow: auto;
border: 1px solid var(--theme-border-color); border: 1px solid var(--theme-border-color);
border-top: 0; border-top: 0;
position: relative;
&-empty + adf-infinite-pagination {
position: absolute;
bottom: 0;
width: 100%;
}
.adf-highlight { .adf-highlight {
color: var(--theme-primary-color); color: var(--theme-primary-color);

View File

@ -80,6 +80,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
private showSiteList = true; private showSiteList = true;
private showSearchField = true; private showSearchField = true;
private showCounter = false; private showCounter = false;
private _emptyList = true;
/** If true will restrict the search and breadcrumbs to the currentFolderId */ /** If true will restrict the search and breadcrumbs to the currentFolderId */
@Input() @Input()
@ -295,6 +296,10 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
return this._chosenNode; return this._chosenNode;
} }
get emptyList(): boolean {
return this._emptyList;
}
getSelectedCount(): number { getSelectedCount(): number {
return this.chosenNode?.length || 0; return this.chosenNode?.length || 0;
} }
@ -567,6 +572,9 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
* Attempts to set the currently loaded node * Attempts to set the currently loaded node
*/ */
onFolderLoaded(nodePaging: NodePaging): void { onFolderLoaded(nodePaging: NodePaging): void {
setTimeout(() => {
this._emptyList = !this.documentList.data.getRows().length;
});
this.updatePaginationAfterRowFilter(nodePaging); this.updatePaginationAfterRowFilter(nodePaging);
if (!this.showingSearchResults) { if (!this.showingSearchResults) {
this.attemptNodeSelection(this.documentList.folderNode); this.attemptNodeSelection(this.documentList.folderNode);

View File

@ -1,9 +1,4 @@
<header <h1 mat-dialog-title data-automation-id="content-node-selector-title">{{title}}</h1>
mat-dialog-title
data-automation-id="content-node-selector-title">
<h2>{{title}}</h2>
</header>
<mat-tab-group class="adf-content-node-selector-dialog-content" <mat-tab-group class="adf-content-node-selector-dialog-content"
mat-align-tabs="start" mat-align-tabs="start"
(selectedIndexChange)="onTabSelectionChange($event)" (selectedIndexChange)="onTabSelectionChange($event)"

View File

@ -8,9 +8,11 @@
class="adf-site-dropdown-list-element" class="adf-site-dropdown-list-element"
id="site-dropdown" id="site-dropdown"
placeholder="{{placeholder | translate}}" placeholder="{{placeholder | translate}}"
[aria-label]="ariaLabel"
floatPlaceholder="never" floatPlaceholder="never"
[(value)]="selected" [(value)]="selected"
(selectionChange)="selectedSite($event)"> (selectionChange)="selectedSite($event)"
role="listbox">
<mat-option *ngFor="let site of siteList?.list.entries;" [value]="site"> <mat-option *ngFor="let site of siteList?.list.entries;" [value]="site">
{{ site.entry.title | translate}} {{ site.entry.title | translate}}
</mat-option> </mat-option>

View File

@ -19,6 +19,8 @@ import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } fro
import { SitesService, LogService, InfiniteSelectScrollDirective } from '@alfresco/adf-core'; import { SitesService, LogService, InfiniteSelectScrollDirective } from '@alfresco/adf-core';
import { SitePaging, SiteEntry } from '@alfresco/js-api'; import { SitePaging, SiteEntry } from '@alfresco/js-api';
import { MatSelectChange } from '@angular/material/select'; import { MatSelectChange } from '@angular/material/select';
import {LiveAnnouncer} from '@angular/cdk/a11y';
import {TranslateService} from '@ngx-translate/core';
/* eslint-disable no-shadow */ /* eslint-disable no-shadow */
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
@ -74,20 +76,28 @@ export class DropdownSitesComponent implements OnInit {
private loading = true; private loading = true;
private skipCount = 0; private skipCount = 0;
private _ariaLabel = '';
selected: SiteEntry = null; selected: SiteEntry = null;
MY_FILES_VALUE = '-my-'; MY_FILES_VALUE = '-my-';
constructor(private sitesService: SitesService, constructor(private sitesService: SitesService,
private logService: LogService) { private logService: LogService,
private liveAnnouncer: LiveAnnouncer,
private translateService: TranslateService) {
} }
ngOnInit() { ngOnInit() {
this.updateAriaLabel(this.selected);
if (!this.siteList) { if (!this.siteList) {
this.loadSiteList(); this.loadSiteList();
} }
} }
get ariaLabel(): string {
return this._ariaLabel;
}
loadAllOnScroll() { loadAllOnScroll() {
if (this.isInfiniteScrollingEnabled()) { if (this.isInfiniteScrollingEnabled()) {
this.loading = true; this.loading = true;
@ -96,6 +106,11 @@ export class DropdownSitesComponent implements OnInit {
} }
selectedSite(event: MatSelectChange) { selectedSite(event: MatSelectChange) {
this.updateAriaLabel(event.value);
this.liveAnnouncer.announce(this.translateService.instant('ADF_DROPDOWN.SELECTION_ARIA_LABEL', {
placeholder: this.translateService.instant(this.placeholder),
selectedOption: this.translateService.instant(event.value.entry.title)
}));
this.change.emit(event.value); this.change.emit(event.value);
} }
@ -140,6 +155,7 @@ export class DropdownSitesComponent implements OnInit {
} }
this.selected = this.siteList.list.entries.find((site: SiteEntry) => site.entry.id === this.value); this.selected = this.siteList.list.entries.find((site: SiteEntry) => site.entry.id === this.value);
this.updateAriaLabel(this.selected);
if (this.value && !this.selected && this.siteListHasMoreItems()) { if (this.value && !this.selected && this.siteListHasMoreItems()) {
this.loadSiteList(); this.loadSiteList();
@ -174,4 +190,8 @@ export class DropdownSitesComponent implements OnInit {
return site.entry.visibility === 'PUBLIC' || return site.entry.visibility === 'PUBLIC' ||
!!site.relations.members.list.entries.find((member) => member.entry.id.toLowerCase() === loggedUserName.toLowerCase()); !!site.relations.members.list.entries.find((member) => member.entry.id.toLowerCase() === loggedUserName.toLowerCase());
} }
private updateAriaLabel(site: SiteEntry): void {
this._ariaLabel = `${this.translateService.instant(this.placeholder)} ${site ? this.translateService.instant(site.entry.title) : ''}`;
}
} }

View File

@ -20,6 +20,7 @@
"CLAIM": "CLAIM", "CLAIM": "CLAIM",
"UNCLAIM": "RELEASE", "UNCLAIM": "RELEASE",
"START PROCESS": "START PROCESS", "START PROCESS": "START PROCESS",
"DATA_LOADING": "Data is loading",
"NOTIFICATIONS": { "NOTIFICATIONS": {
"NO_MESSAGE": "You have no notifications at this time.", "NO_MESSAGE": "You have no notifications at this time.",
"TITLE": "Notifications", "TITLE": "Notifications",
@ -219,8 +220,7 @@
"CREATED_DATE": "Created Date", "CREATED_DATE": "Created Date",
"MODIFIER": "Modifier", "MODIFIER": "Modifier",
"MODIFIED_DATE": "Modified Date", "MODIFIED_DATE": "Modified Date",
"CONTENT_TYPE": "Content Type", "CONTENT_TYPE": "Content Type"
"DATA_LOADING": "Data is loading"
}, },
"CONTENT_TYPE": { "CONTENT_TYPE": {
"DIALOG": { "DIALOG": {
@ -581,6 +581,7 @@
} }
}, },
"ADF_DROPDOWN": { "ADF_DROPDOWN": {
"LOADING": "Loading..." "LOADING": "Loading...",
"SELECTION_ARIA_LABEL": "{{placeholder}} listbox {{selectedOption}}"
} }
} }

View File

@ -11,5 +11,6 @@
<mat-progress-bar *ngIf="isLoading" <mat-progress-bar *ngIf="isLoading"
mode="indeterminate" mode="indeterminate"
class="adf-infinite-pagination-spinner" class="adf-infinite-pagination-spinner"
data-automation-id="adf-infinite-pagination-spinner"></mat-progress-bar> data-automation-id="adf-infinite-pagination-spinner"
[attr.aria-label]="'DATA_LOADING' | translate"></mat-progress-bar>
</div> </div>

View File

@ -29,7 +29,7 @@ import { TestElement } from '../../core/public-api';
export class ContentNodeSelectorDialogPage { export class ContentNodeSelectorDialogPage {
dialog = $(`adf-content-node-selector`); dialog = $(`adf-content-node-selector`);
header = this.dialog.$(`header[data-automation-id='content-node-selector-title']`); header = this.dialog.$(`h1[data-automation-id='content-node-selector-title']`);
searchInputElement = this.dialog.$(`input[data-automation-id='content-node-selector-search-input']`); searchInputElement = this.dialog.$(`input[data-automation-id='content-node-selector-search-input']`);
searchLabel = this.dialog.$('.adf-content-node-selector-content-input .mat-form-field-label'); searchLabel = this.dialog.$('.adf-content-node-selector-content-input .mat-form-field-label');
selectedRow = this.dialog.$(`adf-datatable-row[class*="adf-is-selected"]`); selectedRow = this.dialog.$(`adf-datatable-row[class*="adf-is-selected"]`);