MOBILE-4065 directive: Improve ariaButtonClick directive

main
Pau Ferrer Ocaña 2023-02-07 10:59:06 +01:00
parent 15cdc017e5
commit 7fac6895d9
7 changed files with 72 additions and 28 deletions

View File

@ -1,10 +1,14 @@
<img *ngIf="avatarUrl" [src]="avatarUrl" [alt]="'core.pictureof' | translate:{$a: fullname}" core-external-content
onError="this.src='assets/img/user-avatar.png'" (ariaButtonClick)="gotoProfile($event)" [attr.aria-hidden]="!linkProfile"
[attr.role]="linkProfile ? 'button' : null" [attr.tabindex]="linkProfile ? 0 : null" [class.clickable]="linkProfile">
<img *ngIf="avatarUrl && linkProfile" [src]="avatarUrl" [alt]="'core.pictureof' | translate:{$a: fullname}" core-external-content
onError="this.src='assets/img/user-avatar.png'" (ariaButtonClick)="gotoProfile($event)">
<img *ngIf="!avatarUrl" src="assets/img/user-avatar.png" [alt]="'core.pictureof' | translate:{$a: fullname}"
(ariaButtonClick)="gotoProfile($event)" [attr.aria-hidden]="!linkProfile" [attr.role]="linkProfile ? 'button' : null"
[attr.tabindex]="linkProfile ? 0 : null">
<img *ngIf="avatarUrl && !linkProfile" [src]="avatarUrl" [alt]="'core.pictureof' | translate:{$a: fullname}" core-external-content
onError="this.src='assets/img/user-avatar.png'" aria-hidden="true">
<img *ngIf="!avatarUrl && linkProfile" src="assets/img/user-avatar.png" [alt]="'core.pictureof' | translate:{$a: fullname}"
(ariaButtonClick)="gotoProfile($event)">
<img *ngIf="!avatarUrl && !linkProfile" src="assets/img/user-avatar.png" [alt]="'core.pictureof' | translate:{$a: fullname}"
aria-hidden="true">
<span *ngIf="checkOnline && isOnline()" class="contact-status online" role="status" [attr.aria-label]="'core.online' | translate">
</span>

View File

@ -5,9 +5,6 @@
width: var(--core-avatar-size);
height: var(--core-avatar-size);
.clickable {
cursor: pointer;
}
img {
border-radius: 50%;
width: var(--core-avatar-size);

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Directive, ElementRef, OnInit, Output, EventEmitter } from '@angular/core';
import { Directive, ElementRef, OnInit, Output, EventEmitter, OnChanges, SimpleChanges, Input } from '@angular/core';
import { CoreDom } from '@singletons/dom';
/**
@ -21,10 +21,11 @@ import { CoreDom } from '@singletons/dom';
@Directive({
selector: '[ariaButtonClick]',
})
export class CoreAriaButtonClickDirective implements OnInit {
export class CoreAriaButtonClickDirective implements OnInit, OnChanges {
protected element: HTMLElement;
@Input() disabled = false;
@Output() ariaButtonClick = new EventEmitter();
constructor(
@ -34,10 +35,27 @@ export class CoreAriaButtonClickDirective implements OnInit {
}
/**
* Initialize actions.
* @inheritdoc
*/
ngOnInit(): void {
CoreDom.onActivate(this.element, (event) => this.ariaButtonClick.emit(event));
CoreDom.initializeClickableElementA11y(this.element, (event) => this.ariaButtonClick.emit(event));
}
/**
* @inheritdoc
*/
ngOnChanges(changes: SimpleChanges): void {
if (!changes.disabled) {
return;
}
if (this.element.getAttribute('tabindex') === '0' && this.disabled) {
this.element.setAttribute('tabindex', '-1');
}
if (this.element.getAttribute('tabindex') === '-1' && !this.disabled) {
this.element.setAttribute('tabindex', '0');
}
}
}

View File

@ -610,12 +610,7 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncCompo
return;
}
if (element.tagName !== 'BUTTON' && element.tagName !== 'A') {
element.setAttribute('tabindex', '0');
element.setAttribute('role', 'button');
}
CoreDom.onActivate(element, async (event) => {
CoreDom.initializeClickableElementA11y(element, async (event) => {
event.preventDefault();
event.stopPropagation();

View File

@ -57,12 +57,7 @@ export class CoreLinkDirective implements OnInit {
* Function executed when the component is initialized.
*/
ngOnInit(): void {
if (this.element.tagName != 'BUTTON' && this.element.tagName != 'A') {
this.element.setAttribute('tabindex', '0');
this.element.setAttribute('role', 'button');
}
CoreDom.onActivate(this.element, (event) => this.performAction(event));
CoreDom.initializeClickableElementA11y(this.element, (event) => this.performAction(event));
}
/**

View File

@ -1,4 +1,4 @@
<core-user-avatar *ngIf="(alwaysShow || isMainScreen) && siteInfo" [user]="siteInfo" class="core-bar-button-image clickable"
[linkProfile]="false" (ariaButtonClick)="openUserMenu($event)" [userTour]="userTour" role="button" tabindex="0"
[linkProfile]="false" (ariaButtonClick)="openUserMenu($event)" [userTour]="userTour"
[attr.aria-label]="'core.user.useraccount' | translate">
</core-user-avatar>

View File

@ -514,8 +514,26 @@ export class CoreDom {
*
* @param element Element to listen to events.
* @param callback Callback to call when clicked or the key is pressed.
* @deprecated since 4.1.1: Use initializeClickableElementA11y instead.
*/
static onActivate(element: HTMLElement, callback: (event: MouseEvent | KeyboardEvent) => void): void {
static onActivate(
element: HTMLElement & {disabled?: boolean},
callback: (event: MouseEvent | KeyboardEvent) => void,
): void {
this.initializeClickableElementA11y(element, callback);
}
/**
* Initializes a clickable element a11y calling the click action when pressed enter or space
* and adding tabindex and role if needed.
*
* @param element Element to listen to events.
* @param callback Callback to call when clicked or the key is pressed.
*/
static initializeClickableElementA11y(
element: HTMLElement & {disabled?: boolean},
callback: (event: MouseEvent | KeyboardEvent) => void,
): void {
element.addEventListener('click', (event) => callback(event));
element.addEventListener('keydown', (event) => {
@ -526,10 +544,27 @@ export class CoreDom {
});
element.addEventListener('keyup', (event) => {
if ((event.key == ' ' || event.key == 'Enter')) {
if (event.key === ' ' || event.key === 'Enter') {
event.preventDefault();
event.stopPropagation();
callback(event);
}
});
if (element.tagName !== 'BUTTON' && element.tagName !== 'A') {
// Set tabindex if not previously set.
if (element.getAttribute('tabindex') === null) {
element.setAttribute('tabindex', element.disabled ? '-1' : '0');
}
// Set role if not previously set.
if (!element.getAttribute('role')) {
element.setAttribute('role', 'button');
}
element.classList.add('clickable');
}
}
}