diff --git a/src/core/components/user-avatar/core-user-avatar.html b/src/core/components/user-avatar/core-user-avatar.html index 1e90d6bf3..67eae965c 100644 --- a/src/core/components/user-avatar/core-user-avatar.html +++ b/src/core/components/user-avatar/core-user-avatar.html @@ -1,10 +1,14 @@ - + - + + + + + diff --git a/src/core/components/user-avatar/user-avatar.scss b/src/core/components/user-avatar/user-avatar.scss index 38eea5500..f2028ece8 100644 --- a/src/core/components/user-avatar/user-avatar.scss +++ b/src/core/components/user-avatar/user-avatar.scss @@ -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); diff --git a/src/core/directives/aria-button.ts b/src/core/directives/aria-button.ts index 3d0d29593..ec05e4cab 100644 --- a/src/core/directives/aria-button.ts +++ b/src/core/directives/aria-button.ts @@ -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'); + } } } diff --git a/src/core/directives/format-text.ts b/src/core/directives/format-text.ts index 7fdd4e165..05e911330 100644 --- a/src/core/directives/format-text.ts +++ b/src/core/directives/format-text.ts @@ -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(); diff --git a/src/core/directives/link.ts b/src/core/directives/link.ts index 86d2c18ef..7511193d1 100644 --- a/src/core/directives/link.ts +++ b/src/core/directives/link.ts @@ -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)); } /** diff --git a/src/core/features/mainmenu/components/user-menu-button/user-menu-button.html b/src/core/features/mainmenu/components/user-menu-button/user-menu-button.html index 1282c55ab..5e8829b0b 100644 --- a/src/core/features/mainmenu/components/user-menu-button/user-menu-button.html +++ b/src/core/features/mainmenu/components/user-menu-button/user-menu-button.html @@ -1,4 +1,4 @@ diff --git a/src/core/singletons/dom.ts b/src/core/singletons/dom.ts index 43e20abbe..6e6b3d8d8 100644 --- a/src/core/singletons/dom.ts +++ b/src/core/singletons/dom.ts @@ -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'); + } } }