From 31a5ccc1be83b0447b807b300817bc121fd8040d Mon Sep 17 00:00:00 2001 From: fpaim-symp Date: Wed, 7 May 2025 11:58:52 -0300 Subject: [PATCH 1/2] Ax mention fixes (#1) --- .../src/lib/mention-list.component.ts | 13 +++++++------ .../angular-mentions/src/lib/mention.directive.ts | 9 +++++---- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/projects/angular-mentions/src/lib/mention-list.component.ts b/projects/angular-mentions/src/lib/mention-list.component.ts index 663d103f..19d03326 100644 --- a/projects/angular-mentions/src/lib/mention-list.component.ts +++ b/projects/angular-mentions/src/lib/mention-list.component.ts @@ -18,15 +18,15 @@ import { getCaretCoordinates } from './caret-coords'; {{item[labelKey]}} - `, standalone: false @@ -34,6 +34,7 @@ import { getCaretCoordinates } from './caret-coords'; export class MentionListComponent implements AfterContentChecked { @Input() labelKey: string = 'label'; @Input() itemTemplate: TemplateRef; + @Input() listAriaLabel: string; @Output() itemClick = new EventEmitter(); @ViewChild('list', { static: true }) list: ElementRef; @ViewChild('defaultItemTemplate', { static: true }) defaultItemTemplate: TemplateRef; diff --git a/projects/angular-mentions/src/lib/mention.directive.ts b/projects/angular-mentions/src/lib/mention.directive.ts index 89ce35a2..04f83442 100644 --- a/projects/angular-mentions/src/lib/mention.directive.ts +++ b/projects/angular-mentions/src/lib/mention.directive.ts @@ -1,4 +1,4 @@ -import { ComponentFactoryResolver, Directive, ElementRef, TemplateRef, ViewContainerRef } from "@angular/core"; +import { Directive, ElementRef, TemplateRef, ViewContainerRef } from "@angular/core"; import { EventEmitter, Input, OnChanges, Output, SimpleChanges } from "@angular/core"; import { getCaretPosition, getValue, insertValue, setCaretPosition } from './mention-utils'; @@ -66,6 +66,8 @@ export class MentionDirective implements OnChanges { // template to use for rendering list items @Input() mentionListTemplate: TemplateRef; + @Input() listAriaLabel = 'Suggestions'; + // event emitted whenever the search term changes @Output() searchTerm = new EventEmitter(); @@ -88,7 +90,6 @@ export class MentionDirective implements OnChanges { constructor( private _element: ElementRef, - private _componentResolver: ComponentFactoryResolver, private _viewContainerRef: ViewContainerRef ) { } @@ -357,10 +358,10 @@ export class MentionDirective implements OnChanges { this.opened.emit(); if (this.searchList == null) { - let componentFactory = this._componentResolver.resolveComponentFactory(MentionListComponent); - let componentRef = this._viewContainerRef.createComponent(componentFactory); + let componentRef = this._viewContainerRef.createComponent(MentionListComponent); this.searchList = componentRef.instance; this.searchList.itemTemplate = this.mentionListTemplate; + this.searchList.listAriaLabel = this.listAriaLabel; componentRef.instance['itemClick'].subscribe(() => { nativeElement.focus(); let fakeKeydown = { key: 'Enter', keyCode: KEY_ENTER, wasClick: true }; From 7a0e889c4332c08761282f62aa61a494b290bcc8 Mon Sep 17 00:00:00 2001 From: fpaim-symp Date: Wed, 16 Jul 2025 16:15:06 -0300 Subject: [PATCH 2/2] [fix AX] set ariaActiveDescendantElement on textarea element on open and acive element change (#2) --- .../angular-mentions/src/lib/mention-list.component.ts | 3 +++ projects/angular-mentions/src/lib/mention-utils.ts | 4 ++++ projects/angular-mentions/src/lib/mention.directive.ts | 8 +++++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/projects/angular-mentions/src/lib/mention-list.component.ts b/projects/angular-mentions/src/lib/mention-list.component.ts index 19d03326..c47204c2 100644 --- a/projects/angular-mentions/src/lib/mention-list.component.ts +++ b/projects/angular-mentions/src/lib/mention-list.component.ts @@ -36,6 +36,7 @@ export class MentionListComponent implements AfterContentChecked { @Input() itemTemplate: TemplateRef; @Input() listAriaLabel: string; @Output() itemClick = new EventEmitter(); + @Output() itemActivated = new EventEmitter(true); @ViewChild('list', { static: true }) list: ElementRef; @ViewChild('defaultItemTemplate', { static: true }) defaultItemTemplate: TemplateRef; items = []; @@ -86,6 +87,7 @@ export class MentionListComponent implements AfterContentChecked { } activateNextItem() { + this.itemActivated.emit(); // adjust scrollable-menu offset if the next item is out of view let listEl: HTMLElement = this.list.nativeElement; let activeEl = listEl.getElementsByClassName('active').item(0); @@ -103,6 +105,7 @@ export class MentionListComponent implements AfterContentChecked { } activatePreviousItem() { + this.itemActivated.emit(); // adjust the scrollable-menu offset if the previous item is out of view let listEl: HTMLElement = this.list.nativeElement; let activeEl = listEl.getElementsByClassName('active').item(0); diff --git a/projects/angular-mentions/src/lib/mention-utils.ts b/projects/angular-mentions/src/lib/mention-utils.ts index fb7fec28..7cf8552e 100644 --- a/projects/angular-mentions/src/lib/mention-utils.ts +++ b/projects/angular-mentions/src/lib/mention-utils.ts @@ -176,3 +176,7 @@ function localToRelativeCoordinates( } } } + +export function setAriaActiveDescendant(nativeElement: any, list: HTMLElement) { + nativeElement.ariaActiveDescendantElement = list.querySelector('[aria-selected="true"]'); +} \ No newline at end of file diff --git a/projects/angular-mentions/src/lib/mention.directive.ts b/projects/angular-mentions/src/lib/mention.directive.ts index 04f83442..3ce979cd 100644 --- a/projects/angular-mentions/src/lib/mention.directive.ts +++ b/projects/angular-mentions/src/lib/mention.directive.ts @@ -1,6 +1,6 @@ import { Directive, ElementRef, TemplateRef, ViewContainerRef } from "@angular/core"; import { EventEmitter, Input, OnChanges, Output, SimpleChanges } from "@angular/core"; -import { getCaretPosition, getValue, insertValue, setCaretPosition } from './mention-utils'; +import { getCaretPosition, getValue, insertValue, setAriaActiveDescendant, setCaretPosition } from './mention-utils'; import { MentionConfig } from "./mention-config"; import { MentionListComponent } from './mention-list.component'; @@ -351,6 +351,9 @@ export class MentionDirective implements OnChanges { if (this.searchList) { this.searchList.items = matches; this.searchList.hidden = matches.length == 0; + setTimeout(() => { + setAriaActiveDescendant(this._element.nativeElement, this.searchList.list.nativeElement); + }, 50); } } @@ -367,6 +370,9 @@ export class MentionDirective implements OnChanges { let fakeKeydown = { key: 'Enter', keyCode: KEY_ENTER, wasClick: true }; this.keyHandler(fakeKeydown, nativeElement); }); + componentRef.instance['itemActivated'].subscribe(() => { + setAriaActiveDescendant(nativeElement, this.searchList.list.nativeElement); + }); } this.searchList.labelKey = this.activeConfig.labelKey; this.searchList.dropUp = this.activeConfig.dropUp;