mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-02 17:53:30 +00:00
[ACS-10232][ACS-10326] Fix focus trap for search filters (#11447)
* [ACS-10232][ACS-10326] Fix focus trap for search filters * sonar issue fix
This commit is contained in:
+34
-10
@@ -26,15 +26,20 @@ import { HarnessLoader, TestKey } from '@angular/cdk/testing';
|
||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
import { MatChipHarness } from '@angular/material/chips/testing';
|
||||
import { MatIconHarness } from '@angular/material/icon/testing';
|
||||
import { ConfigurableFocusTrapFactory } from '@angular/cdk/a11y';
|
||||
|
||||
describe('SearchFacetChipTabbedComponent', () => {
|
||||
let loader: HarnessLoader;
|
||||
let component: SearchFacetChipTabbedComponent;
|
||||
let fixture: ComponentFixture<SearchFacetChipTabbedComponent>;
|
||||
|
||||
const focusTrapFactory = jasmine.createSpyObj('ConfigurableFocusTrapFactory', ['create']);
|
||||
const focusTrap = jasmine.createSpyObj('ConfigurableFocusTrap', ['focusInitialElement', 'destroy']);
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ContentTestingModule],
|
||||
providers: [{ provide: ConfigurableFocusTrapFactory, useValue: focusTrapFactory }],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
});
|
||||
fixture = TestBed.createComponent(SearchFacetChipTabbedComponent);
|
||||
@@ -53,6 +58,7 @@ describe('SearchFacetChipTabbedComponent', () => {
|
||||
};
|
||||
fixture.detectChanges();
|
||||
loader = TestbedHarnessEnvironment.loader(fixture);
|
||||
focusTrapFactory.create.and.returnValue(focusTrap);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -76,6 +82,14 @@ describe('SearchFacetChipTabbedComponent', () => {
|
||||
fixture.detectChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get chip harness and click it
|
||||
*/
|
||||
async function clickChip(): Promise<void> {
|
||||
const chip = await loader.getHarness(MatChipHarness);
|
||||
await (await chip.host()).click();
|
||||
}
|
||||
|
||||
it('should display correct label for tabbed facet', () => {
|
||||
const label = fixture.debugElement.query(By.css('.adf-search-filter-placeholder')).nativeElement.innerText;
|
||||
expect(label).toBe(component.tabbedFacet.label + ':');
|
||||
@@ -105,16 +119,14 @@ describe('SearchFacetChipTabbedComponent', () => {
|
||||
});
|
||||
|
||||
it('should display correct title when facet is opened', async () => {
|
||||
const chip = await loader.getHarness(MatChipHarness);
|
||||
await (await chip.host()).click();
|
||||
await clickChip();
|
||||
|
||||
const title = fixture.debugElement.query(By.css('.adf-search-filter-title')).nativeElement.innerText.split('\n')[0];
|
||||
expect(title).toBe(component.tabbedFacet.label);
|
||||
});
|
||||
|
||||
it('should display adf-search-facet-tabbed-content component', async () => {
|
||||
const chip = await loader.getHarness(MatChipHarness);
|
||||
await (await chip.host()).click();
|
||||
await clickChip();
|
||||
|
||||
const activeTabLabel = fixture.debugElement.query(By.css('adf-search-facet-tabbed-content'));
|
||||
expect(activeTabLabel).toBeTruthy();
|
||||
@@ -156,8 +168,7 @@ describe('SearchFacetChipTabbedComponent', () => {
|
||||
|
||||
it('should update display value when new displayValue$ emitted', async () => {
|
||||
const displayValue = 'field_LABEL: test, test2';
|
||||
const chip = await loader.getHarness(MatChipHarness);
|
||||
await (await chip.host()).click();
|
||||
await clickChip();
|
||||
|
||||
emitChildEvent('displayValue$', displayValue);
|
||||
fixture.detectChanges();
|
||||
@@ -168,8 +179,7 @@ describe('SearchFacetChipTabbedComponent', () => {
|
||||
spyOn(component.menuTrigger, 'closeMenu').and.callThrough();
|
||||
spyOn(component, 'onApply').and.callThrough();
|
||||
|
||||
const chip = await loader.getHarness(MatChipHarness);
|
||||
await (await chip.host()).click();
|
||||
await clickChip();
|
||||
|
||||
const applyButton = fixture.debugElement.query(By.css('#apply-filter-button'));
|
||||
applyButton.triggerEventHandler('click', {});
|
||||
@@ -181,12 +191,26 @@ describe('SearchFacetChipTabbedComponent', () => {
|
||||
spyOn(component.menuTrigger, 'closeMenu').and.callThrough();
|
||||
spyOn(component, 'onRemove').and.callThrough();
|
||||
|
||||
const chip = await loader.getHarness(MatChipHarness);
|
||||
await (await chip.host()).click();
|
||||
await clickChip();
|
||||
|
||||
const applyButton = fixture.debugElement.query(By.css('#cancel-filter-button'));
|
||||
applyButton.triggerEventHandler('click', {});
|
||||
expect(component.menuTrigger.closeMenu).toHaveBeenCalled();
|
||||
expect(component.onRemove).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should create focus trap and focus initial element when menu opens', async () => {
|
||||
await clickChip();
|
||||
|
||||
expect(focusTrapFactory.create).toHaveBeenCalledWith(component.menuContainer.nativeElement);
|
||||
expect(focusTrap.focusInitialElement).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should destroy focus trap on menu closed', () => {
|
||||
component.focusTrap = focusTrap;
|
||||
component.onClosed();
|
||||
|
||||
expect(focusTrap.destroy).toHaveBeenCalled();
|
||||
expect(component.focusTrap).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
+11
-5
@@ -64,17 +64,23 @@ export class SearchFacetChipTabbedComponent {
|
||||
chipIcon = 'keyboard_arrow_down';
|
||||
isPopulated = false;
|
||||
|
||||
constructor(private focusTrapFactory: ConfigurableFocusTrapFactory, private changeDetectorRef: ChangeDetectorRef) {}
|
||||
constructor(
|
||||
private readonly focusTrapFactory: ConfigurableFocusTrapFactory,
|
||||
private readonly changeDetectorRef: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
onMenuOpen() {
|
||||
if (this.menuContainer && !this.focusTrap) {
|
||||
this.focusTrap = this.focusTrapFactory.create(this.menuContainer.nativeElement);
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (this.menuContainer && !this.focusTrap) {
|
||||
this.focusTrap = this.focusTrapFactory.create(this.menuContainer.nativeElement);
|
||||
this.focusTrap.focusInitialElement();
|
||||
}
|
||||
});
|
||||
this.chipIcon = 'keyboard_arrow_up';
|
||||
}
|
||||
|
||||
onClosed() {
|
||||
this.focusTrap.destroy();
|
||||
this.focusTrap?.destroy();
|
||||
this.focusTrap = null;
|
||||
this.chipIcon = 'keyboard_arrow_down';
|
||||
}
|
||||
|
||||
+26
-1
@@ -26,6 +26,7 @@ import { HarnessLoader } from '@angular/cdk/testing';
|
||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
import { MatChipHarness } from '@angular/material/chips/testing';
|
||||
import { MatIconHarness } from '@angular/material/icon/testing';
|
||||
import { ConfigurableFocusTrapFactory } from '@angular/cdk/a11y';
|
||||
|
||||
describe('SearchWidgetChipComponent', () => {
|
||||
let loader: HarnessLoader;
|
||||
@@ -33,9 +34,13 @@ describe('SearchWidgetChipComponent', () => {
|
||||
let fixture: ComponentFixture<SearchWidgetChipComponent>;
|
||||
let queryBuilder: SearchQueryBuilderService;
|
||||
|
||||
const focusTrapFactory = jasmine.createSpyObj('ConfigurableFocusTrapFactory', ['create']);
|
||||
const focusTrap = jasmine.createSpyObj('ConfigurableFocusTrap', ['focusInitialElement', 'destroy']);
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MatMenuModule, ContentTestingModule]
|
||||
imports: [MatMenuModule, ContentTestingModule],
|
||||
providers: [{ provide: ConfigurableFocusTrapFactory, useValue: focusTrapFactory }]
|
||||
});
|
||||
queryBuilder = TestBed.inject(SearchQueryBuilderService);
|
||||
fixture = TestBed.createComponent(SearchWidgetChipComponent);
|
||||
@@ -45,6 +50,7 @@ describe('SearchWidgetChipComponent', () => {
|
||||
component.category = simpleCategories[1];
|
||||
fixture.detectChanges();
|
||||
loader = TestbedHarnessEnvironment.loader(fixture);
|
||||
focusTrapFactory.create.and.returnValue(focusTrap);
|
||||
});
|
||||
|
||||
it('should update search query on apply click', async () => {
|
||||
@@ -80,4 +86,23 @@ describe('SearchWidgetChipComponent', () => {
|
||||
const icon = await chip.getHarness(MatIconHarness);
|
||||
expect(await icon.getName()).toBe('keyboard_arrow_up');
|
||||
});
|
||||
|
||||
it('should create focus trap and focus initial element when menu opens', async () => {
|
||||
const chip = await loader.getHarness(MatChipHarness);
|
||||
await (await chip.host()).click();
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(focusTrapFactory.create).toHaveBeenCalledWith(component.menuContainer.nativeElement);
|
||||
expect(focusTrap.focusInitialElement).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should destroy focus trap on main menu closed', () => {
|
||||
component.focusTrap = focusTrap;
|
||||
component.onClosed();
|
||||
|
||||
expect(focusTrap.destroy).toHaveBeenCalled();
|
||||
expect(component.focusTrap).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
+10
-4
@@ -66,7 +66,10 @@ export class SearchWidgetChipComponent implements AfterViewInit {
|
||||
focusTrap: ConfigurableFocusTrap;
|
||||
chipIcon = 'keyboard_arrow_down';
|
||||
|
||||
constructor(private readonly cd: ChangeDetectorRef, private readonly focusTrapFactory: ConfigurableFocusTrapFactory) {}
|
||||
constructor(
|
||||
private readonly cd: ChangeDetectorRef,
|
||||
private readonly focusTrapFactory: ConfigurableFocusTrapFactory
|
||||
) {}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.widgetContainerComponent
|
||||
@@ -78,9 +81,12 @@ export class SearchWidgetChipComponent implements AfterViewInit {
|
||||
}
|
||||
|
||||
onMenuOpen() {
|
||||
if (this.menuContainer && !this.focusTrap) {
|
||||
this.focusTrap = this.focusTrapFactory.create(this.menuContainer.nativeElement);
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (this.menuContainer && !this.focusTrap) {
|
||||
this.focusTrap = this.focusTrapFactory.create(this.menuContainer.nativeElement);
|
||||
this.focusTrap.focusInitialElement();
|
||||
}
|
||||
});
|
||||
this.chipIcon = 'keyboard_arrow_up';
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user