mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2026-09-09 18:02:54 +00:00
[ACS-10219] a11y Keyboard focus is lost when dismissing popup with Esc (#4906)
* [ACS-10219] a11y Keyboard focus is lost when dismissing popup with Esc * [ACS-10219] cr fixes * [link-adf:dev-mmaliarchuk/ACS-10219-a11y-keyboard-focus-is-lost-when-dismissing-popup][affected:*][ci:force] * empty commit * [ACS-10219] move hostListeners under constructor
This commit is contained in:
+76
-2
@@ -22,10 +22,84 @@
|
|||||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Component, DebugElement } from '@angular/core';
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { OutsideEventDirective } from './context-menu-outside-event.directive';
|
import { OutsideEventDirective } from './context-menu-outside-event.directive';
|
||||||
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
|
import { UnitTestingUtils } from '@alfresco/adf-core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
imports: [OutsideEventDirective, MatMenuModule],
|
||||||
|
template: `
|
||||||
|
<div class="custom-selector">
|
||||||
|
<button #menuTrigger="matMenuTrigger" [matMenuTriggerFor]="menu" data-automation-id="trigger-button">Open Menu</button>
|
||||||
|
<mat-menu #menu="matMenu" class="aca-context-menu" acaContextMenuOutsideEvent>
|
||||||
|
<button mat-menu-item data-automation-id="menu-item">Item 1</button>
|
||||||
|
</mat-menu>
|
||||||
|
<button data-automation-id="outside-button">Outside Button</button>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class TestComponent {}
|
||||||
|
|
||||||
describe('OutsideEventDirective', () => {
|
describe('OutsideEventDirective', () => {
|
||||||
it('should be defined', () => {
|
let fixture: ComponentFixture<TestComponent>;
|
||||||
expect(OutsideEventDirective).toBeDefined();
|
let directive: DebugElement;
|
||||||
|
let unitTestingUtils: UnitTestingUtils;
|
||||||
|
let directiveInstance: OutsideEventDirective;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [TestComponent, OutsideEventDirective, MatMenuModule]
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(TestComponent);
|
||||||
|
unitTestingUtils = new UnitTestingUtils(fixture.debugElement);
|
||||||
|
directive = unitTestingUtils.getByDirective(OutsideEventDirective);
|
||||||
|
directiveInstance = directive.injector.get(OutsideEventDirective);
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit clickOutside when clicking outside the context menu', () => {
|
||||||
|
spyOn(directiveInstance.clickOutside, 'next');
|
||||||
|
|
||||||
|
unitTestingUtils.clickByDataAutomationId('outside-button');
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(directiveInstance.clickOutside.next).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not emit clickOutside when clicking inside the context menu', () => {
|
||||||
|
unitTestingUtils.clickByDataAutomationId('trigger-button');
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
spyOn(directiveInstance.clickOutside, 'next');
|
||||||
|
|
||||||
|
unitTestingUtils.clickByDataAutomationId('menu-item');
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(directiveInstance.clickOutside.next).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should focus focusTargetSelector element on escape key', () => {
|
||||||
|
expect(directiveInstance.focusTargetSelector).toBe('.adf-context-menu-source');
|
||||||
|
|
||||||
|
directiveInstance.focusTargetSelector = '.custom-selector';
|
||||||
|
unitTestingUtils.clickByDataAutomationId('trigger-button');
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const contextMenuSource: HTMLElement = unitTestingUtils.getByCSS(directiveInstance.focusTargetSelector).nativeElement;
|
||||||
|
spyOn(contextMenuSource, 'focus');
|
||||||
|
|
||||||
|
document.dispatchEvent(
|
||||||
|
new KeyboardEvent('keydown', {
|
||||||
|
key: 'Escape',
|
||||||
|
code: 'Escape',
|
||||||
|
bubbles: true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(contextMenuSource.focus).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+9
-1
@@ -22,7 +22,7 @@
|
|||||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { DestroyRef, Directive, EventEmitter, inject, OnInit, Output } from '@angular/core';
|
import { DestroyRef, Directive, EventEmitter, HostListener, inject, Input, OnInit, Output } from '@angular/core';
|
||||||
import { fromEvent } from 'rxjs';
|
import { fromEvent } from 'rxjs';
|
||||||
import { filter } from 'rxjs/operators';
|
import { filter } from 'rxjs/operators';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
@@ -35,6 +35,9 @@ export class OutsideEventDirective implements OnInit {
|
|||||||
@Output()
|
@Output()
|
||||||
clickOutside: EventEmitter<void> = new EventEmitter();
|
clickOutside: EventEmitter<void> = new EventEmitter();
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
focusTargetSelector = '.adf-context-menu-source';
|
||||||
|
|
||||||
private readonly destroyRef = inject(DestroyRef);
|
private readonly destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@@ -46,6 +49,11 @@ export class OutsideEventDirective implements OnInit {
|
|||||||
.subscribe(() => this.clickOutside.next());
|
.subscribe(() => this.clickOutside.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HostListener('document:keydown.escape', ['$event'])
|
||||||
|
onEscapeKeydown() {
|
||||||
|
document.querySelector<HTMLElement>(this.focusTargetSelector)?.focus();
|
||||||
|
}
|
||||||
|
|
||||||
private findAncestor(el: Element): boolean {
|
private findAncestor(el: Element): boolean {
|
||||||
const className = 'aca-context-menu';
|
const className = 'aca-context-menu';
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -80,7 +80,8 @@ describe('SaveSearchDirective', () => {
|
|||||||
element.triggerEventHandler('click', event);
|
element.triggerEventHandler('click', event);
|
||||||
|
|
||||||
const expectedConfig = {
|
const expectedConfig = {
|
||||||
data: { searchUrl: 'encodedQuery' }
|
data: { searchUrl: 'encodedQuery' },
|
||||||
|
restoreFocus: true
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(dialog.open).toHaveBeenCalledWith(SaveSearchDialogComponent, expectedConfig);
|
expect(dialog.open).toHaveBeenCalledWith(SaveSearchDialogComponent, expectedConfig);
|
||||||
|
|||||||
+7
-3
@@ -22,7 +22,7 @@
|
|||||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Directive, HostListener, Input } from '@angular/core';
|
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { SaveSearchDialogComponent } from '../dialog/save-search-dialog.component';
|
import { SaveSearchDialogComponent } from '../dialog/save-search-dialog.component';
|
||||||
|
|
||||||
@@ -39,16 +39,20 @@ export class SaveSearchDirective {
|
|||||||
@Input()
|
@Input()
|
||||||
acaSaveSearchQuery: string;
|
acaSaveSearchQuery: string;
|
||||||
|
|
||||||
constructor(private readonly dialogRef: MatDialog) {}
|
constructor(
|
||||||
|
private readonly dialogRef: MatDialog,
|
||||||
|
private readonly elementRef: ElementRef<HTMLElement>
|
||||||
|
) {}
|
||||||
|
|
||||||
@HostListener('click', ['$event'])
|
@HostListener('click', ['$event'])
|
||||||
onClick(event: MouseEvent) {
|
onClick(event: MouseEvent) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
this.elementRef.nativeElement.focus();
|
||||||
this.openDialog();
|
this.openDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
private openDialog(): void {
|
private openDialog(): void {
|
||||||
this.dialogRef.open(SaveSearchDialogComponent, this.getDialogConfig());
|
this.dialogRef.open(SaveSearchDialogComponent, { ...this.getDialogConfig(), restoreFocus: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
private getDialogConfig(): { data: SaveSearchDirectiveDialogData } {
|
private getDialogConfig(): { data: SaveSearchDirectiveDialogData } {
|
||||||
|
|||||||
+12
-8
@@ -22,16 +22,18 @@
|
|||||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { fakeAsync, TestBed } from '@angular/core/testing';
|
||||||
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
|
import { MatDialog, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { SavedSearchesListUiService } from './saved-searches-list-ui.service';
|
import { SavedSearchesListUiService } from './saved-searches-list-ui.service';
|
||||||
import { SavedSearch } from '@alfresco/adf-content-services';
|
import { SavedSearch } from '@alfresco/adf-content-services';
|
||||||
import { SavedSearchEditDialogComponent } from '../dialog/edit/saved-search-edit-dialog.component';
|
import { SavedSearchEditDialogComponent } from '../dialog/edit/saved-search-edit-dialog.component';
|
||||||
import { SavedSearchDeleteDialogComponent } from '../dialog/delete/saved-search-delete-dialog.component';
|
import { SavedSearchDeleteDialogComponent } from '../dialog/delete/saved-search-delete-dialog.component';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
|
||||||
describe('NodeTemplateService', () => {
|
describe('NodeTemplateService', () => {
|
||||||
let dialog: MatDialog;
|
let dialog: MatDialog;
|
||||||
let savedSearchesListUiService: SavedSearchesListUiService;
|
let savedSearchesListUiService: SavedSearchesListUiService;
|
||||||
|
let dialogRefSpy: jasmine.SpyObj<MatDialogRef<any>>;
|
||||||
|
|
||||||
const mockedSearch: SavedSearch = { name: 'test', encodedUrl: 'test', order: 1 };
|
const mockedSearch: SavedSearch = { name: 'test', encodedUrl: 'test', order: 1 };
|
||||||
|
|
||||||
@@ -42,19 +44,21 @@ describe('NodeTemplateService', () => {
|
|||||||
|
|
||||||
dialog = TestBed.inject(MatDialog);
|
dialog = TestBed.inject(MatDialog);
|
||||||
savedSearchesListUiService = TestBed.inject(SavedSearchesListUiService);
|
savedSearchesListUiService = TestBed.inject(SavedSearchesListUiService);
|
||||||
|
|
||||||
|
dialogRefSpy = jasmine.createSpyObj('MatDialogRef', ['afterClosed']);
|
||||||
|
dialogRefSpy.afterClosed.and.returnValue(of(null));
|
||||||
|
spyOn(dialog, 'open').and.returnValue(dialogRefSpy);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should open edit save search dialog with proper params', () => {
|
it('should open edit save search dialog with proper params', fakeAsync(() => {
|
||||||
spyOn(dialog, 'open');
|
|
||||||
savedSearchesListUiService.openEditSavedSearch(mockedSearch);
|
savedSearchesListUiService.openEditSavedSearch(mockedSearch);
|
||||||
|
|
||||||
expect(dialog.open).toHaveBeenCalledWith(SavedSearchEditDialogComponent, { data: mockedSearch, width: '600px' });
|
expect(dialog.open).toHaveBeenCalledWith(SavedSearchEditDialogComponent, { data: mockedSearch, width: '600px' });
|
||||||
});
|
}));
|
||||||
|
|
||||||
it('should open delete save search dialog with proper params', () => {
|
it('should open delete save search dialog with proper params', fakeAsync(() => {
|
||||||
spyOn(dialog, 'open');
|
|
||||||
savedSearchesListUiService.confirmDeleteSavedSearch(mockedSearch);
|
savedSearchesListUiService.confirmDeleteSavedSearch(mockedSearch);
|
||||||
|
|
||||||
expect(dialog.open).toHaveBeenCalledWith(SavedSearchDeleteDialogComponent, { data: mockedSearch, minWidth: '500px' });
|
expect(dialog.open).toHaveBeenCalledWith(SavedSearchDeleteDialogComponent, { data: mockedSearch, minWidth: '500px' });
|
||||||
});
|
}));
|
||||||
});
|
});
|
||||||
|
|||||||
+20
-8
@@ -33,16 +33,28 @@ export class SavedSearchesListUiService {
|
|||||||
private readonly dialog = inject(MatDialog);
|
private readonly dialog = inject(MatDialog);
|
||||||
|
|
||||||
openEditSavedSearch(savedSearch: SavedSearch): void {
|
openEditSavedSearch(savedSearch: SavedSearch): void {
|
||||||
this.dialog.open(SavedSearchEditDialogComponent, {
|
this.dialog
|
||||||
data: savedSearch,
|
.open(SavedSearchEditDialogComponent, {
|
||||||
width: '600px'
|
data: savedSearch,
|
||||||
});
|
width: '600px'
|
||||||
|
})
|
||||||
|
.afterClosed()
|
||||||
|
.subscribe(() => this.focusAfterClose(`.adf-datatable-cell--${savedSearch.name}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
confirmDeleteSavedSearch(savedSearch: SavedSearch): void {
|
confirmDeleteSavedSearch(savedSearch: SavedSearch): void {
|
||||||
this.dialog.open(SavedSearchDeleteDialogComponent, {
|
this.dialog
|
||||||
data: savedSearch,
|
.open(SavedSearchDeleteDialogComponent, {
|
||||||
minWidth: '500px'
|
data: savedSearch,
|
||||||
});
|
minWidth: '500px'
|
||||||
|
})
|
||||||
|
.afterClosed()
|
||||||
|
.subscribe(() => this.focusAfterClose(`.adf-datatable-cell--${savedSearch.name}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
private focusAfterClose(focusedElementSelector: string): void {
|
||||||
|
if (focusedElementSelector) {
|
||||||
|
document.querySelector<HTMLElement>(focusedElementSelector)?.closest<HTMLElement>('adf-datatable-row').focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+42
-4
@@ -23,14 +23,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { DataCellEvent, DataTableComponent, NoopTranslateModule, NotificationService } from '@alfresco/adf-core';
|
import { DataCellEvent, DataTableComponent, NoopTranslateModule, NotificationService, UnitTestingUtils } from '@alfresco/adf-core';
|
||||||
import { SavedSearchesListUiComponent } from './saved-searches-list.ui-component';
|
import { SavedSearchesListUiComponent } from './saved-searches-list.ui-component';
|
||||||
import { SavedSearchesListUiService } from '../saved-searches-list-ui.service';
|
import { SavedSearchesListUiService } from '../saved-searches-list-ui.service';
|
||||||
import { By } from '@angular/platform-browser';
|
|
||||||
import { SavedSearch } from '@alfresco/adf-content-services';
|
import { SavedSearch } from '@alfresco/adf-content-services';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { Clipboard } from '@angular/cdk/clipboard';
|
import { Clipboard } from '@angular/cdk/clipboard';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
import { provideNoopAnimations } from '@angular/platform-browser/animations';
|
||||||
|
|
||||||
describe('SavedSearchesListUiComponent ', () => {
|
describe('SavedSearchesListUiComponent ', () => {
|
||||||
let fixture: ComponentFixture<SavedSearchesListUiComponent>;
|
let fixture: ComponentFixture<SavedSearchesListUiComponent>;
|
||||||
@@ -39,11 +39,12 @@ describe('SavedSearchesListUiComponent ', () => {
|
|||||||
let savedSearchesListUiService: SavedSearchesListUiService;
|
let savedSearchesListUiService: SavedSearchesListUiService;
|
||||||
let clipboard: Clipboard;
|
let clipboard: Clipboard;
|
||||||
let router: Router;
|
let router: Router;
|
||||||
|
let unitTestingUtils: UnitTestingUtils;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [NoopTranslateModule, SavedSearchesListUiComponent],
|
imports: [NoopTranslateModule, SavedSearchesListUiComponent],
|
||||||
providers: [SavedSearchesListUiService]
|
providers: [SavedSearchesListUiService, provideNoopAnimations()]
|
||||||
});
|
});
|
||||||
|
|
||||||
notificationService = TestBed.inject(NotificationService);
|
notificationService = TestBed.inject(NotificationService);
|
||||||
@@ -53,6 +54,7 @@ describe('SavedSearchesListUiComponent ', () => {
|
|||||||
|
|
||||||
fixture = TestBed.createComponent(SavedSearchesListUiComponent);
|
fixture = TestBed.createComponent(SavedSearchesListUiComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
|
unitTestingUtils = new UnitTestingUtils(fixture.debugElement);
|
||||||
});
|
});
|
||||||
|
|
||||||
function getColumnDefinition(key: string, title: string) {
|
function getColumnDefinition(key: string, title: string) {
|
||||||
@@ -73,7 +75,7 @@ describe('SavedSearchesListUiComponent ', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
dataTable = fixture.debugElement.query(By.directive(DataTableComponent)).componentInstance;
|
dataTable = unitTestingUtils.getByDirective(DataTableComponent).componentInstance;
|
||||||
dataCellEvent = new DataCellEvent(
|
dataCellEvent = new DataCellEvent(
|
||||||
{
|
{
|
||||||
isSelected: true,
|
isSelected: true,
|
||||||
@@ -159,6 +161,42 @@ describe('SavedSearchesListUiComponent ', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should focus .adf-context-menu-source element when escape key is pressed and context menu exists', () => {
|
||||||
|
component.savedSearches = [{ name: '1', order: 0, encodedUrl: '123' }] as SavedSearch[];
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
dataTable.showRowContextMenu.emit(dataCellEvent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const tableCell = unitTestingUtils.getByCSS('adf-datatable-cell');
|
||||||
|
|
||||||
|
tableCell.nativeElement.dispatchEvent(
|
||||||
|
new MouseEvent('contextmenu', {
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
button: 2
|
||||||
|
})
|
||||||
|
);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const contextMenu = document.querySelector('.adf-context-menu');
|
||||||
|
const contextMenuSource: HTMLElement = unitTestingUtils.getByCSS('.adf-context-menu-source').nativeElement;
|
||||||
|
|
||||||
|
expect(contextMenu).toBeTruthy();
|
||||||
|
spyOn(contextMenuSource, 'focus');
|
||||||
|
|
||||||
|
document.dispatchEvent(
|
||||||
|
new KeyboardEvent('keydown', {
|
||||||
|
key: 'Escape',
|
||||||
|
code: 'Escape',
|
||||||
|
bubbles: true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(contextMenuSource.focus).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
describe('Context menu actions', () => {
|
describe('Context menu actions', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(savedSearchesListUiService, 'openEditSavedSearch');
|
spyOn(savedSearchesListUiService, 'openEditSavedSearch');
|
||||||
|
|||||||
+22
-2
@@ -22,7 +22,18 @@
|
|||||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { AfterContentInit, Component, DestroyRef, EventEmitter, inject, Input, Output, ViewEncapsulation } from '@angular/core';
|
import {
|
||||||
|
AfterContentInit,
|
||||||
|
Component,
|
||||||
|
DestroyRef,
|
||||||
|
ElementRef,
|
||||||
|
EventEmitter,
|
||||||
|
HostListener,
|
||||||
|
inject,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
ViewEncapsulation
|
||||||
|
} from '@angular/core';
|
||||||
import {
|
import {
|
||||||
AppConfigService,
|
AppConfigService,
|
||||||
DataCellEvent,
|
DataCellEvent,
|
||||||
@@ -93,7 +104,8 @@ export class SavedSearchesListUiComponent extends DataTableSchema implements Aft
|
|||||||
constructor(
|
constructor(
|
||||||
protected appConfig: AppConfigService,
|
protected appConfig: AppConfigService,
|
||||||
private readonly clipboard: Clipboard,
|
private readonly clipboard: Clipboard,
|
||||||
private readonly router: Router
|
private readonly router: Router,
|
||||||
|
private readonly hostElement: ElementRef<HTMLElement>
|
||||||
) {
|
) {
|
||||||
super(appConfig, '', savedSearchesListSchema);
|
super(appConfig, '', savedSearchesListSchema);
|
||||||
}
|
}
|
||||||
@@ -103,6 +115,14 @@ export class SavedSearchesListUiComponent extends DataTableSchema implements Aft
|
|||||||
this.contextMenuAction$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((action) => this.executeMenuOption(action.key, action.data));
|
this.contextMenuAction$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((action) => this.executeMenuOption(action.key, action.data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HostListener('document:keydown.escape', ['$event'])
|
||||||
|
onEscapeKeydown() {
|
||||||
|
const contextMenu = document.querySelector<HTMLElement>('.adf-context-menu');
|
||||||
|
if (contextMenu) {
|
||||||
|
this.hostElement.nativeElement.querySelector<HTMLElement>('.adf-context-menu-source')?.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onShowRowActionsMenu(event: DataCellEvent): void {
|
onShowRowActionsMenu(event: DataCellEvent): void {
|
||||||
event.value.actions = this.menuOptions;
|
event.value.actions = this.menuOptions;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user