[ADF-4958] [ADF-4959] Info Drawer - Buttons don't have role and accessible by keyboard alone (#5161)

* actions accessibility

* lint

* access setEditMode by keyboard

* fix test

* update action automation id reference

* update element finder reference

* update automation id reference
This commit is contained in:
Cilibiu Bogdan 2019-10-17 13:18:15 +03:00 committed by Denys Vuika
parent d7ab0417b8
commit ee5c90871a
6 changed files with 68 additions and 54 deletions

View File

@ -287,9 +287,9 @@ describe('CardView Component', () => {
it('[C279936] Should not be possible edit any parameter when editable property is false', async () => { it('[C279936] Should not be possible edit any parameter when editable property is false', async () => {
await cardViewPageComponent.disableEdit(); await cardViewPageComponent.disableEdit();
const editIconText = element(by.css('mat-icon[data-automation-id="card-textitem-edit-icon-name"]')); const editIconText = element(by.css('button[data-automation-id="card-textitem-edit-icon-name"]'));
const editIconInt = element(by.css('mat-icon[data-automation-id="card-textitem-edit-icon-int"]')); const editIconInt = element(by.css('button[data-automation-id="card-textitem-edit-icon-int"]'));
const editIconFloat = element(by.css('mat-icon[data-automation-id="card-textitem-edit-icon-float"]')); const editIconFloat = element(by.css('button[data-automation-id="card-textitem-edit-icon-float"]'));
const editIconKey = element(by.css('mat-icon[data-automation-id="card-key-value-pairs-button-key-value-pairs"]')); const editIconKey = element(by.css('mat-icon[data-automation-id="card-key-value-pairs-button-key-value-pairs"]'));
const editIconData = element(by.css('mat-datetimepicker-toggle')); const editIconData = element(by.css('mat-datetimepicker-toggle'));

View File

@ -52,12 +52,12 @@ export class CardViewComponentPage {
} }
async clickOnTextClearIcon(): Promise<void> { async clickOnTextClearIcon(): Promise<void> {
const clearIcon: ElementFinder = element(by.css(`mat-icon[data-automation-id="card-textitem-reset-name"]`)); const clearIcon: ElementFinder = element(by.css(`button[data-automation-id="card-textitem-reset-name"]`));
await BrowserActions.click(clearIcon); await BrowserActions.click(clearIcon);
} }
async clickOnTextSaveIcon(): Promise<void> { async clickOnTextSaveIcon(): Promise<void> {
const saveIcon: ElementFinder = element(by.css(`mat-icon[data-automation-id="card-textitem-update-name"]`)); const saveIcon: ElementFinder = element(by.css(`button[data-automation-id="card-textitem-update-name"]`));
await BrowserActions.click(saveIcon); await BrowserActions.click(saveIcon);
} }
@ -78,12 +78,12 @@ export class CardViewComponentPage {
} }
async clickOnIntClearIcon(): Promise<void> { async clickOnIntClearIcon(): Promise<void> {
const clearIcon: ElementFinder = element(by.css('mat-icon[data-automation-id="card-textitem-reset-int"]')); const clearIcon: ElementFinder = element(by.css('button[data-automation-id="card-textitem-reset-int"]'));
await BrowserActions.click(clearIcon); await BrowserActions.click(clearIcon);
} }
async clickOnIntSaveIcon(): Promise<void> { async clickOnIntSaveIcon(): Promise<void> {
const saveIcon: ElementFinder = element(by.css('mat-icon[data-automation-id="card-textitem-update-int"]')); const saveIcon: ElementFinder = element(by.css('button[data-automation-id="card-textitem-update-int"]'));
await BrowserActions.click(saveIcon); await BrowserActions.click(saveIcon);
} }
@ -109,12 +109,12 @@ export class CardViewComponentPage {
} }
async clickOnFloatClearIcon(): Promise<void> { async clickOnFloatClearIcon(): Promise<void> {
const clearIcon: ElementFinder = element(by.css(`mat-icon[data-automation-id="card-textitem-reset-float"]`)); const clearIcon: ElementFinder = element(by.css(`button[data-automation-id="card-textitem-reset-float"]`));
await BrowserActions.click(clearIcon); await BrowserActions.click(clearIcon);
} }
async clickOnFloatSaveIcon(): Promise<void> { async clickOnFloatSaveIcon(): Promise<void> {
const saveIcon: ElementFinder = element(by.css(`mat-icon[data-automation-id="card-textitem-update-float"]`)); const saveIcon: ElementFinder = element(by.css(`button[data-automation-id="card-textitem-update-float"]`));
await BrowserActions.click(saveIcon); await BrowserActions.click(saveIcon);
} }

View File

@ -136,22 +136,22 @@ export class MetadataViewPage {
} }
async editPropertyIconIsDisplayed(propertyName: string) { async editPropertyIconIsDisplayed(propertyName: string) {
const editPropertyIcon: ElementFinder = element(by.css('mat-icon[data-automation-id="card-textitem-edit-icon-' + propertyName + '"]')); const editPropertyIcon: ElementFinder = element(by.css('button[data-automation-id="card-textitem-edit-icon-' + propertyName + '"]'));
await BrowserVisibility.waitUntilElementIsPresent(editPropertyIcon); await BrowserVisibility.waitUntilElementIsPresent(editPropertyIcon);
} }
async updatePropertyIconIsDisplayed(propertyName: string) { async updatePropertyIconIsDisplayed(propertyName: string) {
const updatePropertyIcon: ElementFinder = element(by.css('mat-icon[data-automation-id="card-textitem-update-' + propertyName + '"]')); const updatePropertyIcon: ElementFinder = element(by.css('button[data-automation-id="card-textitem-update-' + propertyName + '"]'));
await BrowserVisibility.waitUntilElementIsVisible(updatePropertyIcon); await BrowserVisibility.waitUntilElementIsVisible(updatePropertyIcon);
} }
async clickUpdatePropertyIcon(propertyName: string): Promise<void> { async clickUpdatePropertyIcon(propertyName: string): Promise<void> {
const updatePropertyIcon: ElementFinder = element(by.css('mat-icon[data-automation-id="card-textitem-update-' + propertyName + '"]')); const updatePropertyIcon: ElementFinder = element(by.css('button[data-automation-id="card-textitem-update-' + propertyName + '"]'));
await BrowserActions.click(updatePropertyIcon); await BrowserActions.click(updatePropertyIcon);
} }
async clickClearPropertyIcon(propertyName: string): Promise<void> { async clickClearPropertyIcon(propertyName: string): Promise<void> {
const clearPropertyIcon: ElementFinder = element(by.css('mat-icon[data-automation-id="card-textitem-reset-' + propertyName + '"]')); const clearPropertyIcon: ElementFinder = element(by.css('button[data-automation-id="card-textitem-reset-' + propertyName + '"]'));
await BrowserActions.click(clearPropertyIcon); await BrowserActions.click(clearPropertyIcon);
} }
@ -183,17 +183,17 @@ export class MetadataViewPage {
} }
async clearPropertyIconIsDisplayed(propertyName: string): Promise<void> { async clearPropertyIconIsDisplayed(propertyName: string): Promise<void> {
const clearPropertyIcon: ElementFinder = element(by.css('mat-icon[data-automation-id="card-textitem-reset-' + propertyName + '"]')); const clearPropertyIcon: ElementFinder = element(by.css('button[data-automation-id="card-textitem-reset-' + propertyName + '"]'));
await BrowserVisibility.waitUntilElementIsVisible(clearPropertyIcon); await BrowserVisibility.waitUntilElementIsVisible(clearPropertyIcon);
} }
async clickEditPropertyIcons(propertyName: string): Promise<void> { async clickEditPropertyIcons(propertyName: string): Promise<void> {
const editPropertyIcon: ElementFinder = element(by.css('mat-icon[data-automation-id="card-textitem-edit-icon-' + propertyName + '"]')); const editPropertyIcon: ElementFinder = element(by.css('button[data-automation-id="card-textitem-edit-icon-' + propertyName + '"]'));
await BrowserActions.click(editPropertyIcon); await BrowserActions.click(editPropertyIcon);
} }
async getPropertyIconTooltip(propertyName: string): Promise<string> { async getPropertyIconTooltip(propertyName: string): Promise<string> {
const editPropertyIcon: ElementFinder = element(by.css('mat-icon[data-automation-id="card-textitem-edit-icon-' + propertyName + '"]')); const editPropertyIcon: ElementFinder = element(by.css('button[data-automation-id="card-textitem-edit-icon-' + propertyName + '"]'));
return await editPropertyIcon.getAttribute('title'); return await editPropertyIcon.getAttribute('title');
} }

View File

@ -75,12 +75,12 @@ export class TaskDetailsPage {
} }
async checkEditDescriptionButtonIsNotDisplayed(): Promise<void> { async checkEditDescriptionButtonIsNotDisplayed(): Promise<void> {
const editDescriptionButton = element(by.css('mat-icon[data-automation-id="card-textitem-edit-icon-description"]')); const editDescriptionButton = element(by.css('button[data-automation-id="card-textitem-edit-icon-description"]'));
await BrowserVisibility.waitUntilElementIsNotVisible(editDescriptionButton); await BrowserVisibility.waitUntilElementIsNotVisible(editDescriptionButton);
} }
async checkEditPriorityButtonIsNotDisplayed(): Promise<void> { async checkEditPriorityButtonIsNotDisplayed(): Promise<void> {
const editPriorityButton = element(by.css('mat-icon[data-automation-id="card-textitem-edit-icon-priority"]')); const editPriorityButton = element(by.css('button[data-automation-id="card-textitem-edit-icon-priority"]'));
await BrowserVisibility.waitUntilElementIsNotVisible(editPriorityButton); await BrowserVisibility.waitUntilElementIsNotVisible(editPriorityButton);
} }

View File

@ -5,7 +5,7 @@
<span *ngIf="showProperty()" class="adf-textitem-ellipsis">{{ property.displayValue }}</span> <span *ngIf="showProperty()" class="adf-textitem-ellipsis">{{ property.displayValue }}</span>
</span> </span>
<ng-template #elseBlock> <ng-template #elseBlock>
<div class="adf-textitem-clickable" (click)="clicked()" fxLayout="row" fxLayoutAlign="space-between center"> <div role="button" class="adf-textitem-clickable" (click)="clicked()" fxLayout="row" fxLayoutAlign="space-between center">
<span class="adf-textitem-clickable-value" [attr.data-automation-id]="'card-textitem-value-' + property.key"> <span class="adf-textitem-clickable-value" [attr.data-automation-id]="'card-textitem-value-' + property.key">
<span *ngIf="showProperty(); else elseEmptyValueBlock">{{ property.displayValue }}</span> <span *ngIf="showProperty(); else elseEmptyValueBlock">{{ property.displayValue }}</span>
</span> </span>
@ -13,14 +13,26 @@
</ng-template> </ng-template>
</span> </span>
<span *ngIf="isEditable()"> <span *ngIf="isEditable()">
<div *ngIf="!inEdit" (click)="setEditMode(true)" class="adf-textitem-readonly" [attr.data-automation-id]="'card-textitem-edit-toggle-' + property.key" fxLayout="row" fxLayoutAlign="space-between center"> <div *ngIf="!inEdit" role="button"
tabindex="0"
[attr.aria-label]="'CORE.METADATA.ACTIONS.EDIT' | translate"
(click)="setEditMode(true)"
(keydown.enter)="setEditMode(true)"
class="adf-textitem-readonly"
[attr.data-automation-id]="'card-textitem-edit-toggle-' + property.key"
fxLayout="row" fxLayoutAlign="space-between center">
<span [attr.data-automation-id]="'card-textitem-value-' + property.key"> <span [attr.data-automation-id]="'card-textitem-value-' + property.key">
<span *ngIf="showProperty(); else elseEmptyValueBlock">{{ property.displayValue }}</span> <span *ngIf="showProperty(); else elseEmptyValueBlock">{{ property.displayValue }}</span>
</span> </span>
<mat-icon fxFlex="0 0 auto"
[attr.data-automation-id]="'card-textitem-edit-icon-' + property.key" <button mat-icon-button fxFlex="0 0 auto"
class="adf-textitem-action"
[attr.aria-label]="'CORE.METADATA.ACTIONS.EDIT' | translate"
[attr.title]="'CORE.METADATA.ACTIONS.EDIT' | translate" [attr.title]="'CORE.METADATA.ACTIONS.EDIT' | translate"
class="adf-textitem-icon">create</mat-icon> [attr.data-automation-id]="'card-textitem-edit-icon-' + property.key">
<mat-icon class="adf-textitem-icon"> create</mat-icon>
</button>
</div> </div>
<div *ngIf="inEdit" class="adf-textitem-editable"> <div *ngIf="inEdit" class="adf-textitem-editable">
<div class="adf-textitem-editable-controls"> <div class="adf-textitem-editable-controls">
@ -42,21 +54,24 @@
(input)="onTextAreaInputChange()" (input)="onTextAreaInputChange()"
[attr.data-automation-id]="'card-textitem-edittextarea-' + property.key"></textarea> [attr.data-automation-id]="'card-textitem-edittextarea-' + property.key"></textarea>
</mat-form-field> </mat-form-field>
<mat-icon <button mat-icon-button class="adf-textitem-action" (click)="update()"
[ngClass]="{'disable': hasErrors()}" [attr.aria-label]="'CORE.METADATA.ACTIONS.SAVE' | translate"
(click)="update()" [attr.title]="'CORE.METADATA.ACTIONS.SAVE' | translate"
[attr.data-automation-id]="'card-textitem-update-' + property.key" [disabled]="hasErrors()"
class="adf-textitem-icon adf-update-icon" [attr.data-automation-id]="'card-textitem-update-' + property.key">
[class.adf-button-disabled]="hasErrors()"
[attr.title]="'CORE.METADATA.ACTIONS.SAVE' | translate">done</mat-icon>
<mat-icon
class="adf-textitem-icon adf-reset-icon"
(click)="reset()"
[attr.title]="'CORE.METADATA.ACTIONS.CANCEL' | translate"
[attr.data-automation-id]="'card-textitem-reset-' + property.key">clear</mat-icon>
<mat-icon class="adf-textitem-icon">done</mat-icon>
</button>
<button mat-icon-button (click)="reset()" class="adf-textitem-action"
[attr.aria-label]="'CORE.METADATA.ACTIONS.CANCEL' | translate"
[attr.title]="'CORE.METADATA.ACTIONS.CANCEL' | translate"
[attr.data-automation-id]="'card-textitem-reset-' + property.key">
<mat-icon>clear</mat-icon>
</button>
</div> </div>
<mat-error [attr.data-automation-id]="'card-textitem-error-' + property.key" class="adf-textitem-editable-error" *ngIf="hasErrors()"> <mat-error [attr.data-automation-id]="'card-textitem-error-' + property.key" class="adf-textitem-editable-error" *ngIf="hasErrors()">
<ul> <ul>
<li *ngFor="let errorMessage of errorMessages">{{ errorMessage | translate }}</li> <li *ngFor="let errorMessage of errorMessages">{{ errorMessage | translate }}</li>
</ul> </ul>

View File

@ -1,15 +1,20 @@
@mixin adf-card-view-textitem-theme($theme) { @mixin adf-card-view-textitem-theme($theme) {
$foreground: map-get($theme, foreground); $foreground: map-get($theme, foreground);
$outline: 1px solid mat-color($alfresco-ecm-blue, A200) !default;
.adf { .adf {
&-textitem-icon { &-textitem-icon {
font-size: 16px; font-size: 16px;
width: 16px; width: 16px;
height: 16px; height: 16px;
position: relative; }
top: 4px;
padding-left: 8px; &-textitem-action {
opacity: 0.3; color: mat-color($foreground, text, 0.25);
}
&-textitem-action:hover, &-textitem-action:focus {
color: mat-color($foreground, text);
} }
&-update-icon { &-update-icon {
@ -19,14 +24,15 @@
&-textitem-readonly { &-textitem-readonly {
cursor: pointer !important; cursor: pointer !important;
&:hover mat-icon { &:hover .adf-textitem-action,
opacity: 1; &:focus .adf-textitem-action {
color: mat-color($foreground, text);
} }
} }
&-textitem-clickable { &-textitem-clickable {
&:hover mat-icon { &:hover .adf-textitem-action {
opacity: 1; color: mat-color($foreground, text);
} }
} }
@ -39,9 +45,10 @@
&-controls { &-controls {
display: flex; display: flex;
align-items: center;
mat-icon:not(.adf-button-disabled):hover { mat-icon:not(.adf-button-disabled):hover {
opacity: 1; color: mat-color($foreground, text);
cursor: pointer !important;; cursor: pointer !important;;
} }
@ -51,7 +58,7 @@
input:focus, input:focus,
textarea:focus { textarea:focus {
border: 1px solid mat-color($foreground, text, 0.15); border: $outline;
} }
} }
@ -126,12 +133,4 @@
display: block; display: block;
} }
} }
.mat-button.adf-update-icon {
min-width: 40px;
line-height: 0;
margin-top: -4px;
padding: 0;
}
} }