Closed constCD closed 2 years ago
test-until-destroy.directive.ts: `import { AfterViewInit, Directive, ElementRef, Input } from '@angular/core'; import { Dropdown } from 'primeng/dropdown/dropdown'; import { fromEvent } from 'rxjs'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { debounceTime, filter } from 'rxjs/operators'; import { KeyBoardName } from '@type/common.types';
@UntilDestroy() @Directive({ selector: '[appTestUntilDestroy]', }) export class TestUntilDestroyDirective implements AfterViewInit { @Input() appDropdownAssistant: Dropdown; private mutationOb: MutationObserver;
constructor(private el: ElementRef) {}
ngAfterViewInit(): void { this.el.nativeElement.querySelector('div.s-p-dropdown input')?.removeAttribute('readonly'); this.registerInputEvents();
this.mutationOb = new MutationObserver(() => { if (this.appDropdownAssistant.overlayVisible) { this.callbackWhenOverlayVisible(); } }); this.mutationOb.observe(this.appDropdownAssistant.accessibleViewChild.nativeElement, { attributes: true, attributeFilter: ['aria-expanded'], });
}
private callbackWhenOverlayVisible(): void { this.registerFilterInputEvents(); }
private registerFilterInputEvents(): void { if (!this.appDropdownAssistant.filter) { return; }
const filterInput = this.appDropdownAssistant.filterViewChild.nativeElement; fromEvent(filterInput, 'input') .pipe(untilDestroyed(this), debounceTime(300)) .subscribe((_event: Event) => { this.appDropdownAssistant.cd.detectChanges(); }); fromEvent(filterInput, 'keydown') .pipe( untilDestroyed(this), filter((event: KeyboardEvent) => event.key === KeyBoardName.ENTER) ) .subscribe((event: Event) => { event.stopPropagation(); this.appDropdownAssistant.hide(); this.el.nativeElement.querySelector('div.s-p-dropdown input')?.focus(); });
private registerInputEvents(): void { const input = this.appDropdownAssistant.accessibleViewChild.nativeElement; input.removeAttribute('role');
fromEvent(input, 'keydown') .pipe( untilDestroyed(this), filter((event: KeyboardEvent) => (event.code === KeyBoardName.ENTER || event.key === KeyBoardName.ARROW_DOWN) && !event.repeat) ) .subscribe(() => { this.appDropdownAssistant.show(); this.appDropdownAssistant.cd.detectChanges(); });
} } test-until-destroy.directive.spec.ts: import * as rxjs from 'rxjs'; import { KeyBoardName } from '@type/common.types'; import { ElementRef } from '@angular/core'; import { TestUntilDestroyDirective } from '@directive/accessibility/test-until-destroy.directive';
test-until-destroy.directive.spec.ts:
describe('TestUntilDestroyDirective', () => { let div: Element; let mockAppDropdownAssistant: any;
beforeEach(() => { let html = <div class="p-dropdown"><div class="p-hidden-accessible"><input aria-expanded="false" /></div></div>; html += <div class="p-dropdown-panel"><input class="p-dropdown-filter" /><ul class="p-dropdown-items">; html += <li class="p-dropdown-item" aria-label="item" aria-selected="true">item</li></ul></div>;
<div class="p-dropdown"><div class="p-hidden-accessible"><input aria-expanded="false" /></div></div>
<div class="p-dropdown-panel"><input class="p-dropdown-filter" /><ul class="p-dropdown-items">
<li class="p-dropdown-item" aria-label="item" aria-selected="true">item</li></ul></div>
div = document.createElement('div'); div.innerHTML = html; mockAppDropdownAssistant = { overlay: div.querySelector('.p-dropdown-panel'), overlayVisible: true, filter: false, accessibleViewChild: { nativeElement: div.querySelector('.p-hidden-accessible > input'), }, filterViewChild: { nativeElement: div.querySelector('input.p-dropdown-filter'), }, optionsToDisplay: [], onFilter: jest.fn(), show: jest.fn(), hide: jest.fn(), focus: jest.fn(), onInputFocus: jest.fn(), onInputBlur: jest.fn(), selectItem: jest.fn(), cd: { detectChanges: jest.fn(), }, updateSelectedOption: jest.fn(), }; jest.spyOn(rxjs, 'fromEvent').mockReturnValue(rxjs.of({}));
});
it('registerFilterInputEvents -> filter is true -> keydown event', async () => { jest.spyOn(rxjs, 'fromEvent').mockReturnValue(rxjs.of({ key: KeyBoardName.ENTER, stopPropagation: jest.fn() } as any));
mockAppDropdownAssistant.filter = true; const directive = new TestUntilDestroyDirective(new ElementRef<any>(div)); directive.appDropdownAssistant = mockAppDropdownAssistant; directive.ngAfterViewInit(); await div.querySelector('.p-hidden-accessible > input')?.setAttribute('aria-expanded', 'true'); expect(directive.appDropdownAssistant.hide).toHaveBeenCalled();
}); }); `
test-until-destroy.directive.ts: `import { AfterViewInit, Directive, ElementRef, Input } from '@angular/core'; import { Dropdown } from 'primeng/dropdown/dropdown'; import { fromEvent } from 'rxjs'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { debounceTime, filter } from 'rxjs/operators'; import { KeyBoardName } from '@type/common.types';
@UntilDestroy() @Directive({ selector: '[appTestUntilDestroy]', }) export class TestUntilDestroyDirective implements AfterViewInit { @Input() appDropdownAssistant: Dropdown; private mutationOb: MutationObserver;
constructor(private el: ElementRef) {}
ngAfterViewInit(): void { this.el.nativeElement.querySelector('div.s-p-dropdown input')?.removeAttribute('readonly'); this.registerInputEvents();
}
private callbackWhenOverlayVisible(): void { this.registerFilterInputEvents(); }
private registerFilterInputEvents(): void { if (!this.appDropdownAssistant.filter) { return; }
}
private registerInputEvents(): void { const input = this.appDropdownAssistant.accessibleViewChild.nativeElement; input.removeAttribute('role');
} }
test-until-destroy.directive.spec.ts:
import * as rxjs from 'rxjs'; import { KeyBoardName } from '@type/common.types'; import { ElementRef } from '@angular/core'; import { TestUntilDestroyDirective } from '@directive/accessibility/test-until-destroy.directive';describe('TestUntilDestroyDirective', () => { let div: Element; let mockAppDropdownAssistant: any;
beforeEach(() => { let html =
<div class="p-dropdown"><div class="p-hidden-accessible"><input aria-expanded="false" /></div></div>
; html +=<div class="p-dropdown-panel"><input class="p-dropdown-filter" /><ul class="p-dropdown-items">
; html +=<li class="p-dropdown-item" aria-label="item" aria-selected="true">item</li></ul></div>
;});
it('registerFilterInputEvents -> filter is true -> keydown event', async () => { jest.spyOn(rxjs, 'fromEvent').mockReturnValue(rxjs.of({ key: KeyBoardName.ENTER, stopPropagation: jest.fn() } as any));
}); }); `