diff --git a/src/addons/calendar/components/calendar/addon-calendar-calendar.html b/src/addons/calendar/components/calendar/addon-calendar-calendar.html index 7fe5c0da7..ccae4c3f1 100644 --- a/src/addons/calendar/components/calendar/addon-calendar-calendar.html +++ b/src/addons/calendar/components/calendar/addon-calendar-calendar.html @@ -58,9 +58,7 @@ [class.addon-calendar-event-past-day]="isPastMonth || day.ispast" role="button cell" tabindex="0" - (click)="dayClicked(day.mday)" - (keyup)="dayAction.keyUp($event, day.mday)" - (keydown)="dayAction.keyDown($event)" + (ariaButtonClick)="dayClicked(day.mday)" >

@@ -80,9 +78,7 @@ [class.addon-calendar-event-past]="event.ispast" role="button" tabindex="0" - (click)="eventClicked(event, $event)" - (keyup)="eventAction.keyUp($event, event)" - (keydown)="eventAction.keyDown($event)" + (ariaButtonClick)="eventClicked(event, $event)" > { - - /** - * @inheritdoc - */ - click(event: Event, day: number): void { - this.componentInstance.dayClicked(day); - } - -} - -/** - * Helper class to manage event button. - */ -class AddonCalendarEventButton extends CoreAriaRoleButton { - - /** - * @inheritdoc - */ - click(event: Event, calendarEvent: AddonCalendarEventToDisplay): void { - this.componentInstance.eventClicked(calendarEvent, event); - } - -} diff --git a/src/core/classes/aria-role-button.ts b/src/core/classes/aria-role-button.ts deleted file mode 100644 index 28a019a14..000000000 --- a/src/core/classes/aria-role-button.ts +++ /dev/null @@ -1,70 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -export abstract class CoreAriaRoleButton { - - componentInstance: T; - - constructor(componentInstance: T) { - this.componentInstance = componentInstance; - } - - /** - * A11y key functionality that prevents keyDown events. - * - * @param event Event. - */ - keyDown(event: KeyboardEvent): void { - if ((event.key == ' ' || event.key == 'Enter') && this.isAllowed()) { - event.preventDefault(); - event.stopPropagation(); - } - } - - /** - * A11y key functionality that translates space and enter keys to click action. - * - * @param event Event. - * @param args Additional args. - */ - keyUp(event: KeyboardEvent, ...args: unknown[]): void { - if ((event.key == ' ' || event.key == 'Enter') && this.isAllowed()) { - event.preventDefault(); - event.stopPropagation(); - - this.click(event, ...args); - } - } - - /** - * A11y click functionality. - * - * @param event Event. - * @param args Additional args. - */ - // eslint-disable-next-line @typescript-eslint/no-unused-vars - click(event?: Event, ...args: unknown[]): void { - // Nothing defined here. - } - - /** - * Checks if action is allowed in class. - * - * @returns If allowed. - */ - isAllowed(): boolean { - return true; - } - -} diff --git a/src/core/components/user-avatar/core-user-avatar.html b/src/core/components/user-avatar/core-user-avatar.html index 4b8d7819d..474334be0 100644 --- a/src/core/components/user-avatar/core-user-avatar.html +++ b/src/core/components/user-avatar/core-user-avatar.html @@ -4,18 +4,15 @@ [alt]="'core.pictureof' | translate:{$a: fullname}" core-external-content onError="this.src='assets/img/user-avatar.png'" - (click)="gotoProfile($event)" + (ariaButtonClick)="gotoProfile($event)" [attr.aria-hidden]="!linkProfile" [attr.role]="linkProfile ? 'button' : null" - (keydown)="buttonAction.keyDown($event)" - (keyup)="buttonAction.keyUp($event)" [attr.tabindex]="linkProfile ? 0 : null" [class.clickable]="linkProfile" > diff --git a/src/core/components/user-avatar/user-avatar.ts b/src/core/components/user-avatar/user-avatar.ts index 6097a0371..28815d4b4 100644 --- a/src/core/components/user-avatar/user-avatar.ts +++ b/src/core/components/user-avatar/user-avatar.ts @@ -20,7 +20,6 @@ import { CoreUtils } from '@services/utils/utils'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreUserProvider, CoreUserBasicData } from '@features/user/services/user'; import { CoreNavigator } from '@services/navigator'; -import { CoreAriaRoleButton } from '@classes/aria-role-button'; /** * Component to display a "user avatar". @@ -39,15 +38,13 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy { @Input() profileUrl?: string; @Input() linkProfile = true; // Avoid linking to the profile if wanted. @Input() fullname?: string; - @Input() userId?: number; // If provided or found it will be used to link the image to the profile. - @Input() courseId?: number; + @Input() protected userId?: number; // If provided or found it will be used to link the image to the profile. + @Input() protected courseId?: number; @Input() checkOnline = false; // If want to check and show online status. @Input() extraIcon?: string; // Extra icon to show near the avatar. avatarUrl?: string; - buttonAction: CoreUserAvatarButton; - // Variable to check if we consider this user online or not. // @TODO: Use setting when available (see MDL-63972) so we can use site setting. protected timetoshowusers = 300000; // Miliseconds default. @@ -55,7 +52,6 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy { protected pictureObserver: CoreEventObserver; constructor() { - this.currentUserId = CoreSites.getCurrentSiteUserId(); this.pictureObserver = CoreEvents.on( @@ -67,15 +63,12 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy { }, CoreSites.getCurrentSiteId(), ); - - this.buttonAction = new CoreUserAvatarButton(this); } /** * Component being initialized. */ ngOnInit(): void { - this.setFields(); } @@ -137,14 +130,19 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy { * @param event Click event. */ gotoProfile(event: Event): void { - if (!this.buttonAction.isAllowed()) { + if (!this.linkProfile || !this.userId) { return; } event.preventDefault(); event.stopPropagation(); - this.buttonAction.click(); + CoreNavigator.navigateToSitePath('user', { + params: { + userId: this.userId, + courseId: this.courseId, + }, + }); } /** @@ -156,32 +154,6 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy { } -/** - * Helper class to manage rol button. - */ -class CoreUserAvatarButton extends CoreAriaRoleButton { - - /** - * @inheritdoc - */ - click(): void { - CoreNavigator.navigateToSitePath('user', { - params: { - userId: this.componentInstance.userId, - courseId: this.componentInstance.courseId, - }, - }); - } - - /** - * @inheritdoc - */ - isAllowed(): boolean { - return this.componentInstance.linkProfile && !!this.componentInstance.userId; - } - -} - /** * Type with all possible formats of user. */ diff --git a/src/core/directives/aria-button.ts b/src/core/directives/aria-button.ts new file mode 100644 index 000000000..092c53737 --- /dev/null +++ b/src/core/directives/aria-button.ts @@ -0,0 +1,57 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Directive, ElementRef, OnInit, Output, EventEmitter } from '@angular/core'; + +/** + * Directive to emulate click and key actions following aria role button. + */ +@Directive({ + selector: '[ariaButtonClick]', +}) +export class CoreAriaButtonClickDirective implements OnInit { + + protected element: HTMLElement; + + @Output() ariaButtonClick = new EventEmitter(); + + constructor( + element: ElementRef, + ) { + this.element = element.nativeElement; + } + + /** + * Initialize actions. + */ + ngOnInit(): void { + this.element.addEventListener('click', async (event) => { + this.ariaButtonClick.emit(event); + }); + + this.element.addEventListener('keydown', async (event) => { + if ((event.key == ' ' || event.key == 'Enter')) { + event.preventDefault(); + event.stopPropagation(); + } + }); + + this.element.addEventListener('keyup', async (event) => { + if ((event.key == ' ' || event.key == 'Enter')) { + this.ariaButtonClick.emit(event); + } + }); + } + +} diff --git a/src/core/directives/directives.module.ts b/src/core/directives/directives.module.ts index 3cf6d3303..b83ee4534 100644 --- a/src/core/directives/directives.module.ts +++ b/src/core/directives/directives.module.ts @@ -24,6 +24,7 @@ import { CoreLinkDirective } from './link'; import { CoreLongPressDirective } from './long-press'; import { CoreSupressEventsDirective } from './supress-events'; import { CoreUserLinkDirective } from './user-link'; +import { CoreAriaButtonClickDirective } from './aria-button'; @NgModule({ declarations: [ @@ -37,6 +38,7 @@ import { CoreUserLinkDirective } from './user-link'; CoreLongPressDirective, CoreSupressEventsDirective, CoreUserLinkDirective, + CoreAriaButtonClickDirective, ], exports: [ CoreAutoFocusDirective, @@ -49,6 +51,7 @@ import { CoreUserLinkDirective } from './user-link'; CoreLongPressDirective, CoreSupressEventsDirective, CoreUserLinkDirective, + CoreAriaButtonClickDirective, ], }) export class CoreDirectivesModule {}