From 44c02c934221f2e9e60bfd55a9e2a356c0c97076 Mon Sep 17 00:00:00 2001 From: Conor Wright Date: Tue, 25 Sep 2018 13:50:48 -0400 Subject: [PATCH 01/22] moved over changes from skyux2 PR --- .../wait/fixtures/wait.component.fixture.html | 15 +++- .../wait/fixtures/wait.component.fixture.ts | 10 ++- .../modules/wait/wait-adapter.service.ts | 78 ++++++++++++++++- .../modules/wait/wait.component.spec.ts | 86 ++++++++++++++++++- src/app/public/modules/wait/wait.component.ts | 6 +- .../public/modules/wait/wait.service.spec.ts | 19 ++++ 6 files changed, 206 insertions(+), 8 deletions(-) diff --git a/src/app/public/modules/wait/fixtures/wait.component.fixture.html b/src/app/public/modules/wait/fixtures/wait.component.fixture.html index 0ec5fb84..83bbc798 100644 --- a/src/app/public/modules/wait/fixtures/wait.component.fixture.html +++ b/src/app/public/modules/wait/fixtures/wait.component.fixture.html @@ -1,4 +1,17 @@ -
+ + Anchor tag 1 + +
+
+ + Anchor tag 2 + diff --git a/src/app/public/modules/wait/fixtures/wait.component.fixture.ts b/src/app/public/modules/wait/fixtures/wait.component.fixture.ts index c1f5f2a2..725da432 100644 --- a/src/app/public/modules/wait/fixtures/wait.component.fixture.ts +++ b/src/app/public/modules/wait/fixtures/wait.component.fixture.ts @@ -1,8 +1,13 @@ import { Component, - Input + Input, + ViewChild } from '@angular/core'; +import { + SkyWaitComponent +} from '..'; + @Component({ selector: 'sky-test-cmp', templateUrl: './wait.component.fixture.html' @@ -16,4 +21,7 @@ export class SkyWaitTestComponent { @Input() public isNonBlocking: boolean; + + @ViewChild(SkyWaitComponent) + public waitComponent: SkyWaitComponent; } diff --git a/src/app/public/modules/wait/wait-adapter.service.ts b/src/app/public/modules/wait/wait-adapter.service.ts index 40679bac..3820bcef 100644 --- a/src/app/public/modules/wait/wait-adapter.service.ts +++ b/src/app/public/modules/wait/wait-adapter.service.ts @@ -1,13 +1,28 @@ import { ElementRef, Injectable, - Renderer + Renderer, + OnDestroy } from '@angular/core'; +import { + SkyWindowRefService +} from '@skyux/core'; @Injectable() -export class SkyWaitAdapterService { +export class SkyWaitAdapterService implements OnDestroy { + private parentListeners: {[key: string]: Function} = {}; + + constructor( + private renderer: Renderer, + private windowRef: SkyWindowRefService + ) { } - constructor(private renderer: Renderer) { } + public ngOnDestroy() { + for (let key of Object.keys(this.parentListeners)) { + this.parentListeners[key](); + delete this.parentListeners[key]; + } + } public setWaitBounds(waitEl: ElementRef) { this.renderer.setElementStyle(waitEl.nativeElement.parentElement, 'position', 'relative'); @@ -17,9 +32,64 @@ export class SkyWaitAdapterService { this.renderer.setElementStyle(waitEl.nativeElement.parentElement, 'position', undefined); } - public setBusyState(waitEl: ElementRef, isFullPage: boolean, isWaiting: boolean) { + public setBusyState(waitEl: ElementRef, isFullPage: boolean, isWaiting: boolean, id: string) { let busyEl = isFullPage ? document.body : waitEl.nativeElement.parentElement; let state = isWaiting ? 'true' : undefined; this.renderer.setElementAttribute(busyEl, 'aria-busy', state); + + if (isWaiting) { + // Propagate tab navigation if attempted into waited element + if (!isFullPage) { + let focusListenerFunc = this.renderer.listen(this.windowRef.getWindow(), 'keyup', (event: any) => { + if (event.key.toLowerCase() === 'tab' && busyEl.contains(document.activeElement)) { + this.focusNextElement(busyEl, event.shiftKey); + } + }); + this.parentListeners[id] = focusListenerFunc; + } else { + // Prevent tab navigation within the waited page + let endListenerFunc = this.renderer.listen(busyEl, 'keydown', (event: KeyboardEvent) => { + if (event.key.toLowerCase() === 'tab') { + event.preventDefault(); + } + }); + this.parentListeners[id] = endListenerFunc; + } + } else if (id in this.parentListeners) { + // Clean up existing listener + this.parentListeners[id](); + delete this.parentListeners[id]; + } + } + + private focusNextElement(parentElement: any, shiftKey: boolean): void { + // Select all possible focussable elements + let focussableElements = + 'a[href], ' + + 'area[href], ' + + 'input:not([disabled]):not([tabindex=\'-1\']), ' + + 'button:not([disabled]):not([tabindex=\'-1\']), ' + + 'select:not([disabled]):not([tabindex=\'-1\']), ' + + 'textarea:not([disabled]):not([tabindex=\'-1\']), ' + + 'iframe, object, embed, ' + + '*[tabindex]:not([tabindex=\'-1\']), ' + + '*[contenteditable=true]'; + let focussable = Array.prototype.filter.call(document.body.querySelectorAll(focussableElements), + (element: any) => { + return element.offsetWidth > 0 || element.offsetHeight > 0 || element === document.activeElement; + }); + // If shift tab, go in the other direction + let modifier = 1; + if (shiftKey) { + modifier = -1; + } + // Find the next navigable element that isn't waiting + let curIndex = focussable.indexOf(document.activeElement) + modifier; + while (!focussable[curIndex] || parentElement.contains(focussable[curIndex]) || parentElement === focussable[curIndex]) { + curIndex += modifier; + } + if (focussable[curIndex]) { + focussable[curIndex].focus(); + } } } diff --git a/src/app/public/modules/wait/wait.component.spec.ts b/src/app/public/modules/wait/wait.component.spec.ts index ec1668e6..bca1c1f2 100644 --- a/src/app/public/modules/wait/wait.component.spec.ts +++ b/src/app/public/modules/wait/wait.component.spec.ts @@ -1,8 +1,15 @@ import { async, - TestBed + TestBed, + ComponentFixture, + tick, + fakeAsync } from '@angular/core/testing'; +import { + SkyAppTestUtility +} from '@blackbaud/skyux-builder/runtime/testing/browser'; + import { expect } from '@skyux-sdk/testing'; @@ -106,6 +113,52 @@ describe('Wait component', () => { expect(el.querySelector('.sky-wait-mask-loading-blocking')).toBeNull(); }); + it('should propagate tab navigation forward and backward avoiding waited element', fakeAsync(() => { + let fixture = TestBed.createComponent(SkyWaitTestComponent); + fixture.detectChanges(); + + fixture.componentInstance.isFullPage = false; + fixture.componentInstance.isWaiting = true; + fixture.detectChanges(); + + let anchor1: any = document.body.querySelector('#anchor-1'); + let anchor2: any = document.body.querySelector('#anchor-2'); + let button: any = document.querySelector('.sky-btn'); + button.focus(); + expect(document.activeElement).toBe(button); + + SkyAppTestUtility.fireDomEvent(window, 'keyup', { + keyboardEventInit: { key: 'Tab' } + }); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + expect(document.activeElement).toBe(anchor2); + + button.focus(); + SkyAppTestUtility.fireDomEvent(window, 'keyup', { + keyboardEventInit: { key: 'Tab', shiftKey: true } + }); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + expect(document.activeElement).toBe(anchor1); + + fixture.componentInstance.isWaiting = false; + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + button.focus(); + SkyAppTestUtility.fireDomEvent(window, 'keyup', { + keyboardEventInit: { key: 'Tab' } + }); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + expect(document.activeElement).toBe(button); + })); + it('should set aria-busy on document body when fullPage is true', async(() => { let fixture = TestBed.createComponent(SkyWaitTestComponent); @@ -139,4 +192,35 @@ describe('Wait component', () => { fixture.detectChanges(); expect(el.querySelector('.sky-wait-test-component').getAttribute('aria-busy')).toBeNull(); }); + + let testlistenerCreated = (fixture: ComponentFixture) => { + fixture.detectChanges(); + + let waitComponent: any = fixture.componentInstance.waitComponent; + let adapter: any = waitComponent.adapterService; + expect(waitComponent.id in adapter.parentListeners).toBeTruthy(); + expect(Object.keys(adapter.parentListeners).length).toBe(1); + + fixture.componentInstance.isWaiting = false; + fixture.detectChanges(); + expect(waitComponent.id in adapter.parentListeners).toBeFalsy(); + expect(Object.keys(adapter.parentListeners).length).toBe(0); + }; + + it('should create listener on document body when fullPage is true', () => { + let fixture = TestBed.createComponent(SkyWaitTestComponent); + fixture.detectChanges(); + + fixture.componentInstance.isFullPage = true; + fixture.componentInstance.isWaiting = true; + testlistenerCreated(fixture); + }); + + it('should create listener on containing div when fullPage is set to false', () => { + let fixture = TestBed.createComponent(SkyWaitTestComponent); + fixture.detectChanges(); + + fixture.componentInstance.isWaiting = true; + testlistenerCreated(fixture); + }); }); diff --git a/src/app/public/modules/wait/wait.component.ts b/src/app/public/modules/wait/wait.component.ts index f05a2807..7e1503b1 100644 --- a/src/app/public/modules/wait/wait.component.ts +++ b/src/app/public/modules/wait/wait.component.ts @@ -8,6 +8,8 @@ import { SkyWaitAdapterService } from './wait-adapter.service'; +let nextId = 0; + @Component({ selector: 'sky-wait', templateUrl: './wait.component.html', @@ -16,6 +18,8 @@ import { }) export class SkyWaitComponent { + private id: string = `sky-wait-${++nextId}`; + @Input() public set isWaiting(value: boolean) { if (value && !this._isFullPage) { @@ -23,7 +27,7 @@ export class SkyWaitComponent { } else if (!value && !this._isFullPage) { this.adapterService.removeWaitBounds(this.elRef); } - this.adapterService.setBusyState(this.elRef, this._isFullPage, value); + this.adapterService.setBusyState(this.elRef, this._isFullPage, value, this.id); this._isWaiting = value; } diff --git a/src/app/public/modules/wait/wait.service.spec.ts b/src/app/public/modules/wait/wait.service.spec.ts index 69ac5237..56671137 100644 --- a/src/app/public/modules/wait/wait.service.spec.ts +++ b/src/app/public/modules/wait/wait.service.spec.ts @@ -8,6 +8,10 @@ import { tick } from '@angular/core/testing'; +import { + SkyAppTestUtility +} from '@blackbaud/skyux-builder/runtime/testing/browser'; + import { SkyWaitFixturesModule } from './fixtures/wait-fixtures.module'; @@ -97,6 +101,21 @@ describe('Wait service', () => { })); + it('should block tab navigation when a blocking page wait is active', fakeAsync(() => { + waitService.beginBlockingPageWait(); + tick(); + applicationRef.tick(); + verifyBlockingPageWaitExists(true); + let button = document.body.querySelector('button'); + button.focus(); + expect(document.activeElement).toBe(button); + SkyAppTestUtility.fireDomEvent(document.body, 'keydown', { + keyboardEventInit: { key: 'tab' } + }); + button = document.body.querySelector('button'); + expect(document.activeElement).toBe(button); + })); + it('should add a nonblocking page wait when beginPageWait is called with isBlocking false', fakeAsync(() => { waitService.beginNonBlockingPageWait(); From 80f6e5ceec19fab31bc618dfe550049b7bad57ef Mon Sep 17 00:00:00 2001 From: Conor Wright Date: Tue, 25 Sep 2018 13:56:48 -0400 Subject: [PATCH 02/22] fixed a11y issue with test fixture for wait --- .../wait/fixtures/wait.component.fixture.html | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/app/public/modules/wait/fixtures/wait.component.fixture.html b/src/app/public/modules/wait/fixtures/wait.component.fixture.html index 83bbc798..24fc81c8 100644 --- a/src/app/public/modules/wait/fixtures/wait.component.fixture.html +++ b/src/app/public/modules/wait/fixtures/wait.component.fixture.html @@ -1,17 +1,17 @@ Anchor tag 1 -
- - - +
+ + +
- Anchor tag 2 From ef2ca416d0ef207a7e4c1bb4009b522d6af1fae2 Mon Sep 17 00:00:00 2001 From: Conor Wright Date: Wed, 17 Oct 2018 14:49:25 -0400 Subject: [PATCH 03/22] fixed while condition and addressed pr comments --- .../wait/fixtures/wait.component.fixture.html | 2 +- .../wait/fixtures/wait.component.fixture.ts | 2 ++ .../public/modules/wait/wait-adapter.service.ts | 16 +++++++++------- .../public/modules/wait/wait.component.spec.ts | 16 +++++++++++++++- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/app/public/modules/wait/fixtures/wait.component.fixture.html b/src/app/public/modules/wait/fixtures/wait.component.fixture.html index a2f37b25..d1391951 100644 --- a/src/app/public/modules/wait/fixtures/wait.component.fixture.html +++ b/src/app/public/modules/wait/fixtures/wait.component.fixture.html @@ -20,7 +20,7 @@ >
-
- Wait inside this element with text + Wait link inside this element with text Date: Tue, 30 Oct 2018 09:49:13 -0400 Subject: [PATCH 11/22] cleaned up the visual demo a little for wait --- e2e/wait.e2e-spec.ts | 4 +--- ...-Absolute-Behind14-chrome-1044x788-dpr-1.png | Bin 4561 -> 0 bytes ...-Absolute-Behind18-chrome-1044x788-dpr-1.png | Bin 4561 -> 0 bytes ...it-Demo-Absolute13-chrome-1044x788-dpr-1.png | Bin 6336 -> 0 bytes ...it-Demo-Absolute17-chrome-1044x788-dpr-1.png | Bin 6336 -> 0 bytes ...ull-Page-Non-Block-chrome-1044x788-dpr-1.png | Bin 19247 -> 0 bytes ...ait-Demo-Full-Page-chrome-1044x788-dpr-1.png | Bin 18197 -> 0 bytes ...arent-Non-Blocking-chrome-1044x788-dpr-1.png | Bin 7092 -> 0 bytes ...Wait-Demo-Parent12-chrome-1044x788-dpr-1.png | Bin 6711 -> 0 bytes ...Wait-Demo-Parent16-chrome-1044x788-dpr-1.png | Bin 6711 -> 0 bytes src/app/visual/wait/wait-demo.component.html | 15 ++++++++++----- src/app/visual/wait/wait-demo.component.ts | 2 +- 12 files changed, 12 insertions(+), 9 deletions(-) delete mode 100644 screenshots-baseline/sky-Wait-Demo-Absolute-Behind14-chrome-1044x788-dpr-1.png delete mode 100644 screenshots-baseline/sky-Wait-Demo-Absolute-Behind18-chrome-1044x788-dpr-1.png delete mode 100644 screenshots-baseline/sky-Wait-Demo-Absolute13-chrome-1044x788-dpr-1.png delete mode 100644 screenshots-baseline/sky-Wait-Demo-Absolute17-chrome-1044x788-dpr-1.png delete mode 100644 screenshots-baseline/sky-Wait-Demo-Full-Page-Non-Block-chrome-1044x788-dpr-1.png delete mode 100644 screenshots-baseline/sky-Wait-Demo-Full-Page-chrome-1044x788-dpr-1.png delete mode 100644 screenshots-baseline/sky-Wait-Demo-Parent-Non-Blocking-chrome-1044x788-dpr-1.png delete mode 100644 screenshots-baseline/sky-Wait-Demo-Parent12-chrome-1044x788-dpr-1.png delete mode 100644 screenshots-baseline/sky-Wait-Demo-Parent16-chrome-1044x788-dpr-1.png diff --git a/e2e/wait.e2e-spec.ts b/e2e/wait.e2e-spec.ts index fdca25e6..6a5aaf32 100644 --- a/e2e/wait.e2e-spec.ts +++ b/e2e/wait.e2e-spec.ts @@ -46,7 +46,6 @@ describe('Wait', () => { SkyHostBrowser.get('visual/wait'); SkyHostBrowser.setWindowBreakpoint('lg'); element(by.css('.sky-test-full-page')).click(); - element(by.css('.sky-test-wait')).click(); expect('.sky-wait-demo').toMatchBaselineScreenshot(done, { screenshotName: 'sky-wait-demo-full-page' }); @@ -55,9 +54,8 @@ describe('Wait', () => { it('should display non-blocking wait on full page', (done) => { SkyHostBrowser.get('visual/wait'); SkyHostBrowser.setWindowBreakpoint('lg'); - element(by.css('.sky-test-full-page')).click(); element(by.css('.sky-test-non-blocking')).click(); - element(by.css('.sky-test-wait')).click(); + element(by.css('.sky-test-full-page')).click(); expect('.sky-wait-demo').toMatchBaselineScreenshot(done, { screenshotName: 'sky-wait-demo-full-page-non-block' }); diff --git a/screenshots-baseline/sky-Wait-Demo-Absolute-Behind14-chrome-1044x788-dpr-1.png b/screenshots-baseline/sky-Wait-Demo-Absolute-Behind14-chrome-1044x788-dpr-1.png deleted file mode 100644 index 9ac986246011278c04b5a3fec0de29edfcf8e344..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4561 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 diff --git a/screenshots-baseline/sky-Wait-Demo-Absolute-Behind18-chrome-1044x788-dpr-1.png b/screenshots-baseline/sky-Wait-Demo-Absolute-Behind18-chrome-1044x788-dpr-1.png deleted file mode 100644 index 9ac986246011278c04b5a3fec0de29edfcf8e344..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4561 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 diff --git a/screenshots-baseline/sky-Wait-Demo-Absolute13-chrome-1044x788-dpr-1.png b/screenshots-baseline/sky-Wait-Demo-Absolute13-chrome-1044x788-dpr-1.png deleted file mode 100644 index dd48a7ccf532f29d9b97436293035fe93fd5f9e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6336 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 X;X7wNkA~_0Oq?(>FuPKJ84>?)qp{u? diff --git a/screenshots-baseline/sky-Wait-Demo-Absolute17-chrome-1044x788-dpr-1.png b/screenshots-baseline/sky-Wait-Demo-Absolute17-chrome-1044x788-dpr-1.png deleted file mode 100644 index dd48a7ccf532f29d9b97436293035fe93fd5f9e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6336 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 X;X7wNkA~_0Oq?(>FuPKJ84>?)qp{u? diff --git a/screenshots-baseline/sky-Wait-Demo-Full-Page-Non-Block-chrome-1044x788-dpr-1.png b/screenshots-baseline/sky-Wait-Demo-Full-Page-Non-Block-chrome-1044x788-dpr-1.png deleted file mode 100644 index 7a8b5fc17ed803ba6ad6d643737db8bfd683fbe9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19247 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 diff --git a/screenshots-baseline/sky-Wait-Demo-Full-Page-chrome-1044x788-dpr-1.png b/screenshots-baseline/sky-Wait-Demo-Full-Page-chrome-1044x788-dpr-1.png deleted file mode 100644 index 16ecc82764a32d49d3d36c9aaadcab00b3e51a00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18197 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 diff --git a/screenshots-baseline/sky-Wait-Demo-Parent12-chrome-1044x788-dpr-1.png b/screenshots-baseline/sky-Wait-Demo-Parent12-chrome-1044x788-dpr-1.png deleted file mode 100644 index 82c4a28373cb9266f7b0d71074e950e959ef36fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6711 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 diff --git a/screenshots-baseline/sky-Wait-Demo-Parent16-chrome-1044x788-dpr-1.png b/screenshots-baseline/sky-Wait-Demo-Parent16-chrome-1044x788-dpr-1.png deleted file mode 100644 index 82c4a28373cb9266f7b0d71074e950e959ef36fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6711 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 diff --git a/src/app/visual/wait/wait-demo.component.html b/src/app/visual/wait/wait-demo.component.html index c99c6294..f49943b0 100644 --- a/src/app/visual/wait/wait-demo.component.html +++ b/src/app/visual/wait/wait-demo.component.html @@ -17,12 +17,12 @@ class="sky-btn sky-btn-secondary sky-test-full-page" type="button" [ngClass]="{ - 'sky-btn-secondary': !isFullPage, - 'sky-btn-primary': isFullPage + 'sky-btn-secondary': !isFullPageWaiting, + 'sky-btn-primary': isFullPageWaiting }" - (click)="isFullPage = !isFullPage" + (click)="isFullPageWaiting = !isFullPageWaiting" > - Toggle Full page + Toggle full page wait
+ + diff --git a/src/app/visual/wait/wait-demo.component.ts b/src/app/visual/wait/wait-demo.component.ts index e2a4aa04..3f883fc6 100644 --- a/src/app/visual/wait/wait-demo.component.ts +++ b/src/app/visual/wait/wait-demo.component.ts @@ -9,6 +9,6 @@ import { }) export class SkyWaitDemoComponent { public isWaiting: boolean; - public isFullPage: boolean; + public isFullPageWaiting: boolean; public isNonBlocking: boolean; } From 5cd9fbc71f0c311bf3b5cc9fbdae2fea50ba6105 Mon Sep 17 00:00:00 2001 From: Conor Wright Date: Tue, 30 Oct 2018 10:29:23 -0400 Subject: [PATCH 12/22] fixed the issue Alex found with shift-tab nav --- src/app/public/modules/wait/wait-adapter.service.ts | 7 ++++--- src/app/public/modules/wait/wait.component.spec.ts | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/app/public/modules/wait/wait-adapter.service.ts b/src/app/public/modules/wait/wait-adapter.service.ts index 172a7ea9..33a3705c 100644 --- a/src/app/public/modules/wait/wait-adapter.service.ts +++ b/src/app/public/modules/wait/wait-adapter.service.ts @@ -101,20 +101,21 @@ export class SkyWaitAdapterService implements OnDestroy { } if (focussable[curIndex] && !parentElement.contains(focussable[curIndex])) { - console.log(focussable[curIndex]); focussable[curIndex].focus(); } else { // Try wrapping the navigation curIndex = modifier > 0 ? 0 : focussable.length - 1; while ( - curIndex < startingIndex && + curIndex !== startingIndex && focussable[curIndex] && parentElement.contains(focussable[curIndex]) ) { curIndex += modifier; } + + /* istanbul ignore else */ + /* sanity check */ if (focussable[curIndex] && !parentElement.contains(focussable[curIndex])) { - console.log(focussable[curIndex]); focussable[curIndex].focus(); } else { // No valid target, wipe focus diff --git a/src/app/public/modules/wait/wait.component.spec.ts b/src/app/public/modules/wait/wait.component.spec.ts index fdd6f16a..f931f7ee 100644 --- a/src/app/public/modules/wait/wait.component.spec.ts +++ b/src/app/public/modules/wait/wait.component.spec.ts @@ -178,7 +178,8 @@ describe('Wait component', () => { fixture.detectChanges(); tick(); fixture.detectChanges(); - expect(document.activeElement).toBe(document.body); + console.log(document.activeElement); + expect(document.activeElement).toBe(anchor1); fixture.componentInstance.isWaiting = false; fixture.detectChanges(); From 96f85ac63995f6ec6f376761f3597cb5d548559d Mon Sep 17 00:00:00 2001 From: Conor Wright Date: Thu, 1 Nov 2018 00:56:45 -0400 Subject: [PATCH 13/22] reimplemented to only maintain 1 listener --- .../wait/fixtures/wait.component.fixture.ts | 15 +-- .../modules/wait/wait-adapter.service.ts | 119 +++++++++++------- .../modules/wait/wait.component.spec.ts | 85 ++++++++++--- src/app/public/modules/wait/wait.component.ts | 4 +- .../help-inline/help-inline-demo.component.ts | 1 - src/app/visual/wait/wait-demo.component.html | 4 +- 6 files changed, 150 insertions(+), 78 deletions(-) diff --git a/src/app/public/modules/wait/fixtures/wait.component.fixture.ts b/src/app/public/modules/wait/fixtures/wait.component.fixture.ts index c11af67c..c6f42ba8 100644 --- a/src/app/public/modules/wait/fixtures/wait.component.fixture.ts +++ b/src/app/public/modules/wait/fixtures/wait.component.fixture.ts @@ -1,6 +1,5 @@ import { Component, - Input, ViewChild } from '@angular/core'; @@ -13,18 +12,12 @@ import { templateUrl: './wait.component.fixture.html' }) export class SkyWaitTestComponent { - @Input() - public isWaiting: boolean; - - @Input() - public isFullPage: boolean; - - @Input() - public isNonBlocking: boolean; - - @Input() public ariaLabel: string; + public isWaiting: boolean = false; + public isFullPage: boolean = false; + public isNonBlocking: boolean = false; + public showAnchor0 = true; public showAnchor2 = true; diff --git a/src/app/public/modules/wait/wait-adapter.service.ts b/src/app/public/modules/wait/wait-adapter.service.ts index 33a3705c..62a5ee55 100644 --- a/src/app/public/modules/wait/wait-adapter.service.ts +++ b/src/app/public/modules/wait/wait-adapter.service.ts @@ -5,30 +5,25 @@ import { OnDestroy } from '@angular/core'; -import { - SkyWaitComponent -} from './wait.component'; - @Injectable() export class SkyWaitAdapterService implements OnDestroy { - private parentListeners: {[key: string]: Function} = {}; + private static activeListener: Function; + private static isPageWaitActive = false; + private static busyElements: {[key: string]: any} = {}; constructor( private renderer: Renderer ) { } - public ngOnDestroy() { - for (let key of Object.keys(this.parentListeners)) { - this.parentListeners[key](); - delete this.parentListeners[key]; - } + public ngOnDestroy(): void { + SkyWaitAdapterService.clearListener(); } - public setWaitBounds(waitEl: ElementRef) { + public setWaitBounds(waitEl: ElementRef): void { this.renderer.setElementStyle(waitEl.nativeElement.parentElement, 'position', 'relative'); } - public removeWaitBounds(waitEl: ElementRef) { + public removeWaitBounds(waitEl: ElementRef): void { this.renderer.setElementStyle(waitEl.nativeElement.parentElement, 'position', undefined); } @@ -37,44 +32,60 @@ export class SkyWaitAdapterService implements OnDestroy { isFullPage: boolean, isWaiting: boolean, isNonBlocking = false, - waitCmp: SkyWaitComponent = undefined + id: string = '' ): void { let busyEl = isFullPage ? document.body : waitEl.nativeElement.parentElement; let state = isWaiting ? 'true' : undefined; - if (!(waitCmp && isNonBlocking)) { + if (!isNonBlocking) { this.renderer.setElementAttribute(busyEl, 'aria-busy', state); - // Remove focus from page when full page blocking wait - if (isFullPage || busyEl.contains(document.activeElement)) { - this.clearDocumentFocus(); - } - if (isWaiting) { - // Prevent tab navigation within the waited page - let endListenerFunc = this.renderer.listen(document.body, 'keydown', (event: KeyboardEvent) => { - if (event.key.toLowerCase() === 'tab') { - event.preventDefault(); - event.stopPropagation(); - event.stopImmediatePropagation(); - if (!waitCmp.isFullPage) { - // Propagate tab navigation if attempted into waited element - this.focusNextElement(busyEl, event.shiftKey); - } else { - this.clearDocumentFocus(); + // Remove focus from page when full page blocking wait + if (isFullPage || busyEl.contains(document.activeElement)) { + this.clearDocumentFocus(); + } + + if (isFullPage) { + SkyWaitAdapterService.isPageWaitActive = true; + } else { + SkyWaitAdapterService.busyElements[id] = busyEl; + } + + if (!SkyWaitAdapterService.activeListener) { + // Prevent tab navigation within the waited page + let endListenerFunc = this.renderer.listen(document.body, 'keydown', (event: KeyboardEvent) => { + if (event.key.toLowerCase() === 'tab') { + event.preventDefault(); + event.stopPropagation(); + event.stopImmediatePropagation(); + + if (SkyWaitAdapterService.isPageWaitActive) { + this.clearDocumentFocus(); + } else { + // Propagate tab navigation if attempted into waited element + this.focusNextElement(event.shiftKey); + } } - } - }); - this.parentListeners[waitCmp.id] = endListenerFunc; - } else if (waitCmp.id in this.parentListeners) { - // Clean up existing listener - this.parentListeners[waitCmp.id](); - delete this.parentListeners[waitCmp.id]; + }); + SkyWaitAdapterService.activeListener = endListenerFunc; + } + } else { + if (isFullPage) { + SkyWaitAdapterService.isPageWaitActive = false; + } else if (id in SkyWaitAdapterService.busyElements) { + delete SkyWaitAdapterService.busyElements[id]; + } + + // Clear the listener if we are done waiting all elements + if (Object.keys(SkyWaitAdapterService.busyElements).length === 0 && !SkyWaitAdapterService.isPageWaitActive) { + SkyWaitAdapterService.clearListener(); + } } } } - private focusNextElement(parentElement: any, shiftKey: boolean): void { + private focusNextElement(shiftKey: boolean): void { // Select all possible focussable elements let focussableElements = 'a[href], ' + @@ -96,11 +107,11 @@ export class SkyWaitAdapterService implements OnDestroy { // Find the next navigable element that isn't waiting let startingIndex = focussable.indexOf(document.activeElement); let curIndex = startingIndex + modifier; - while (focussable[curIndex] && parentElement.contains(focussable[curIndex])) { + while (focussable[curIndex] && this.isElementBusy(focussable[curIndex])) { curIndex += modifier; } - if (focussable[curIndex] && !parentElement.contains(focussable[curIndex])) { + if (focussable[curIndex] && !this.isElementBusy(focussable[curIndex])) { focussable[curIndex].focus(); } else { // Try wrapping the navigation @@ -108,14 +119,14 @@ export class SkyWaitAdapterService implements OnDestroy { while ( curIndex !== startingIndex && focussable[curIndex] && - parentElement.contains(focussable[curIndex]) + this.isElementBusy(focussable[curIndex]) ) { curIndex += modifier; } /* istanbul ignore else */ /* sanity check */ - if (focussable[curIndex] && !parentElement.contains(focussable[curIndex])) { + if (focussable[curIndex] && !this.isElementBusy(focussable[curIndex])) { focussable[curIndex].focus(); } else { // No valid target, wipe focus @@ -124,10 +135,32 @@ export class SkyWaitAdapterService implements OnDestroy { } } - private clearDocumentFocus() { + private isElementBusy(element: any) { + for (let key of Object.keys(SkyWaitAdapterService.busyElements)) { + const parentElement = SkyWaitAdapterService.busyElements[key]; + if (parentElement.contains(element)) { + return true; + } + } + return false; + } + + private clearDocumentFocus(): void { if (document.activeElement && (document.activeElement as any).blur) { (document.activeElement as any).blur(); } document.body.focus(); } + + private static clearListener(): void { + if (SkyWaitAdapterService.activeListener) { + SkyWaitAdapterService.activeListener(); + SkyWaitAdapterService.activeListener = undefined; + } + + this.isPageWaitActive = false; + for (let key of Object.keys(SkyWaitAdapterService.busyElements)) { + delete SkyWaitAdapterService.busyElements[key]; + } + } } diff --git a/src/app/public/modules/wait/wait.component.spec.ts b/src/app/public/modules/wait/wait.component.spec.ts index f931f7ee..e3625009 100644 --- a/src/app/public/modules/wait/wait.component.spec.ts +++ b/src/app/public/modules/wait/wait.component.spec.ts @@ -1,7 +1,6 @@ import { async, TestBed, - ComponentFixture, tick, fakeAsync } from '@angular/core/testing'; @@ -28,6 +27,7 @@ import { import { SkyWaitComponent } from './wait.component'; +import { SkyWaitAdapterService } from './wait-adapter.service'; describe('Wait component', () => { beforeEach(() => { @@ -44,6 +44,10 @@ describe('Wait component', () => { }); }); + afterEach(() => { + (SkyWaitAdapterService as any).clearListener(); + }); + it('should show the wait element when isWaiting is set to true', async(() => { const fixture = TestBed.createComponent(SkyWaitComponent); @@ -125,9 +129,16 @@ describe('Wait component', () => { let fixture = TestBed.createComponent(SkyWaitTestComponent); fixture.detectChanges(); + fixture.componentInstance.isNonBlocking = false; fixture.componentInstance.isFullPage = false; + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + fixture.componentInstance.isWaiting = true; fixture.detectChanges(); + tick(); + fixture.detectChanges(); let anchor0: any = document.body.querySelector('#anchor-0'); let anchor1: any = document.body.querySelector('#anchor-1'); @@ -165,6 +176,22 @@ describe('Wait component', () => { fixture.detectChanges(); expect(document.activeElement).toBe(anchor0); + // Wrapping navigation + fixture.componentInstance.showAnchor0 = true; + fixture.componentInstance.showAnchor2 = false; + anchor0.focus(); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + SkyAppTestUtility.fireDomEvent(document.body, 'keydown', { + keyboardEventInit: { key: 'Tab', shiftKey: true } + }); + + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + expect(document.activeElement).toBe(anchor1); + fixture.componentInstance.showAnchor0 = false; fixture.componentInstance.showAnchor2 = false; anchor1.focus(); @@ -178,7 +205,6 @@ describe('Wait component', () => { fixture.detectChanges(); tick(); fixture.detectChanges(); - console.log(document.activeElement); expect(document.activeElement).toBe(anchor1); fixture.componentInstance.isWaiting = false; @@ -230,36 +256,57 @@ describe('Wait component', () => { expect(el.querySelector('.sky-wait-test-component').getAttribute('aria-busy')).toBeNull(); }); - let testlistenerCreated = (fixture: ComponentFixture) => { + it('should create listener on document body when fullPage is true', fakeAsync(() => { + let fixture = TestBed.createComponent(SkyWaitTestComponent); + const waitCmp = fixture.componentInstance.waitComponent; + + fixture.componentInstance.isNonBlocking = false; + fixture.componentInstance.isFullPage = true; + fixture.detectChanges(); + tick(); fixture.detectChanges(); - let waitComponent: any = fixture.componentInstance.waitComponent; - let adapter: any = waitComponent.adapterService; - expect(waitComponent.id in adapter.parentListeners).toBeTruthy(); - expect(Object.keys(adapter.parentListeners).length).toBe(1); + fixture.componentInstance.isWaiting = true; + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + expect((SkyWaitAdapterService as any).activeListener).toBeTruthy(); + expect(waitCmp.id in (SkyWaitAdapterService as any).busyElements).toBeFalsy(); fixture.componentInstance.isWaiting = false; fixture.detectChanges(); - expect(waitComponent.id in adapter.parentListeners).toBeFalsy(); - expect(Object.keys(adapter.parentListeners).length).toBe(0); - }; + tick(); + fixture.detectChanges(); + + expect((SkyWaitAdapterService as any).activeListener).toBeFalsy(); + expect(waitCmp.id in (SkyWaitAdapterService as any).busyElements).toBeFalsy(); + })); - it('should create listener on document body when fullPage is true', () => { + it('should create listener on containing div when fullPage is set to false', fakeAsync(() => { let fixture = TestBed.createComponent(SkyWaitTestComponent); + const waitCmp = fixture.componentInstance.waitComponent; + + fixture.componentInstance.isNonBlocking = false; + fixture.componentInstance.isFullPage = false; + fixture.detectChanges(); + tick(); fixture.detectChanges(); - fixture.componentInstance.isFullPage = true; fixture.componentInstance.isWaiting = true; - testlistenerCreated(fixture); - }); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); - it('should create listener on containing div when fullPage is set to false', () => { - let fixture = TestBed.createComponent(SkyWaitTestComponent); + expect((SkyWaitAdapterService as any).activeListener).toBeTruthy(); + expect(waitCmp.id in (SkyWaitAdapterService as any).busyElements).toBeTruthy(); + + fixture.componentInstance.isWaiting = false; fixture.detectChanges(); - fixture.componentInstance.isWaiting = true; - testlistenerCreated(fixture); - }); + expect((SkyWaitAdapterService as any).activeListener).toBeFalsy(); + expect(waitCmp.id in (SkyWaitAdapterService as any).busyElements).toBeFalsy(); + })); function getAriaLabel(): string { return document.body.querySelector('.sky-wait-mask').getAttribute('aria-label'); diff --git a/src/app/public/modules/wait/wait.component.ts b/src/app/public/modules/wait/wait.component.ts index 03159f01..9c328927 100644 --- a/src/app/public/modules/wait/wait.component.ts +++ b/src/app/public/modules/wait/wait.component.ts @@ -42,10 +42,10 @@ export class SkyWaitComponent implements OnInit { this.adapterService.setBusyState( this.elRef, - this._isFullPage, + this.isFullPage, value, this.isNonBlocking, - this + this.id ); this._isWaiting = value; diff --git a/src/app/visual/help-inline/help-inline-demo.component.ts b/src/app/visual/help-inline/help-inline-demo.component.ts index 22b4905a..795e36b9 100644 --- a/src/app/visual/help-inline/help-inline-demo.component.ts +++ b/src/app/visual/help-inline/help-inline-demo.component.ts @@ -9,6 +9,5 @@ export class SkyHelpInlineDemoComponent { public onActionClick(): void { this.buttonIsClicked = true; - console.log('Help inline clicked!'); } } diff --git a/src/app/visual/wait/wait-demo.component.html b/src/app/visual/wait/wait-demo.component.html index f49943b0..2263d8a8 100644 --- a/src/app/visual/wait/wait-demo.component.html +++ b/src/app/visual/wait/wait-demo.component.html @@ -62,7 +62,7 @@

- A large area that can be waited with the sky-wait directive. + A large area that can be waited with the sky-wait directive and a link.

@@ -79,7 +79,7 @@

- A large area that can be waited with the sky-wait directive. + A large area that can be waited with the sky-wait directive and a link.

From dd142f42df4a681ed512c8152e3cda8b10de74e9 Mon Sep 17 00:00:00 2001 From: Conor Wright Date: Sat, 3 Nov 2018 19:51:09 -0400 Subject: [PATCH 14/22] fixed the focus nav not ignoring hidden elements --- .../wait/fixtures/wait.component.fixture.html | 4 +++ .../wait/fixtures/wait.component.fixture.ts | 3 ++ .../modules/wait/wait-adapter.service.ts | 14 +++++--- .../modules/wait/wait.component.spec.ts | 35 +++++++++++++++++++ 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/app/public/modules/wait/fixtures/wait.component.fixture.html b/src/app/public/modules/wait/fixtures/wait.component.fixture.html index 9ff5fcb4..0d6e694b 100644 --- a/src/app/public/modules/wait/fixtures/wait.component.fixture.html +++ b/src/app/public/modules/wait/fixtures/wait.component.fixture.html @@ -31,6 +31,10 @@ class="sky-btn sky-btn-secondary" id="anchor-2" href="/" + [ngStyle]="{ + 'display': anchor2Display, + 'visibility': anchor2Visibility + }" > Anchor tag 2 diff --git a/src/app/public/modules/wait/fixtures/wait.component.fixture.ts b/src/app/public/modules/wait/fixtures/wait.component.fixture.ts index c6f42ba8..2a77b501 100644 --- a/src/app/public/modules/wait/fixtures/wait.component.fixture.ts +++ b/src/app/public/modules/wait/fixtures/wait.component.fixture.ts @@ -21,6 +21,9 @@ export class SkyWaitTestComponent { public showAnchor0 = true; public showAnchor2 = true; + public anchor2Visibility: string = ''; + public anchor2Display: string = ''; + @ViewChild(SkyWaitComponent) public waitComponent: SkyWaitComponent; } diff --git a/src/app/public/modules/wait/wait-adapter.service.ts b/src/app/public/modules/wait/wait-adapter.service.ts index 62a5ee55..935b84b7 100644 --- a/src/app/public/modules/wait/wait-adapter.service.ts +++ b/src/app/public/modules/wait/wait-adapter.service.ts @@ -107,11 +107,11 @@ export class SkyWaitAdapterService implements OnDestroy { // Find the next navigable element that isn't waiting let startingIndex = focussable.indexOf(document.activeElement); let curIndex = startingIndex + modifier; - while (focussable[curIndex] && this.isElementBusy(focussable[curIndex])) { + while (focussable[curIndex] && this.isElementBusyOrHidden(focussable[curIndex])) { curIndex += modifier; } - if (focussable[curIndex] && !this.isElementBusy(focussable[curIndex])) { + if (focussable[curIndex] && !this.isElementBusyOrHidden(focussable[curIndex])) { focussable[curIndex].focus(); } else { // Try wrapping the navigation @@ -119,14 +119,14 @@ export class SkyWaitAdapterService implements OnDestroy { while ( curIndex !== startingIndex && focussable[curIndex] && - this.isElementBusy(focussable[curIndex]) + this.isElementBusyOrHidden(focussable[curIndex]) ) { curIndex += modifier; } /* istanbul ignore else */ /* sanity check */ - if (focussable[curIndex] && !this.isElementBusy(focussable[curIndex])) { + if (focussable[curIndex] && !this.isElementBusyOrHidden(focussable[curIndex])) { focussable[curIndex].focus(); } else { // No valid target, wipe focus @@ -135,7 +135,11 @@ export class SkyWaitAdapterService implements OnDestroy { } } - private isElementBusy(element: any) { + private isElementBusyOrHidden(element: any) { + const style = window.getComputedStyle(element); + if (style.display === 'none' || style.visibility === 'hidden') { + return true; + } for (let key of Object.keys(SkyWaitAdapterService.busyElements)) { const parentElement = SkyWaitAdapterService.busyElements[key]; if (parentElement.contains(element)) { diff --git a/src/app/public/modules/wait/wait.component.spec.ts b/src/app/public/modules/wait/wait.component.spec.ts index e3625009..e4532f91 100644 --- a/src/app/public/modules/wait/wait.component.spec.ts +++ b/src/app/public/modules/wait/wait.component.spec.ts @@ -192,6 +192,41 @@ describe('Wait component', () => { fixture.detectChanges(); expect(document.activeElement).toBe(anchor1); + // test display:none + fixture.componentInstance.showAnchor0 = true; + fixture.componentInstance.showAnchor2 = true; + fixture.componentInstance.anchor2Display = 'none'; + anchor0.focus(); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + SkyAppTestUtility.fireDomEvent(document.body, 'keydown', { + keyboardEventInit: { key: 'Tab', shiftKey: true } + }); + + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + expect(document.activeElement).toBe(anchor1); + + // test display:none + fixture.componentInstance.showAnchor0 = true; + fixture.componentInstance.showAnchor2 = true; + fixture.componentInstance.anchor2Display = ''; + fixture.componentInstance.anchor2Visibility = 'hidden'; + anchor0.focus(); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + SkyAppTestUtility.fireDomEvent(document.body, 'keydown', { + keyboardEventInit: { key: 'Tab', shiftKey: true } + }); + + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + expect(document.activeElement).toBe(anchor1); + fixture.componentInstance.showAnchor0 = false; fixture.componentInstance.showAnchor2 = false; anchor1.focus(); From b588b0fd88b8d1a93d65171cad3e1e22957f1590 Mon Sep 17 00:00:00 2001 From: Conor Wright Date: Thu, 6 Dec 2018 16:03:06 -0500 Subject: [PATCH 15/22] implemented focusin strategy (#21) * implemented focusin strategy * finished implementing the focusin strategy --- .../modules/wait/wait-adapter.service.ts | 163 ++++++++++++------ .../modules/wait/wait.component.spec.ts | 115 +++++------- src/app/public/modules/wait/wait.component.ts | 2 +- .../public/modules/wait/wait.service.spec.ts | 15 +- src/app/visual/wait/wait-demo.component.html | 6 + 5 files changed, 163 insertions(+), 138 deletions(-) diff --git a/src/app/public/modules/wait/wait-adapter.service.ts b/src/app/public/modules/wait/wait-adapter.service.ts index 935b84b7..ec325975 100644 --- a/src/app/public/modules/wait/wait-adapter.service.ts +++ b/src/app/public/modules/wait/wait-adapter.service.ts @@ -5,18 +5,23 @@ import { OnDestroy } from '@angular/core'; +import { + SkyWaitComponent +} from './wait.component'; + @Injectable() export class SkyWaitAdapterService implements OnDestroy { - private static activeListener: Function; - private static isPageWaitActive = false; - private static busyElements: {[key: string]: any} = {}; + private static isPageWaitActive: boolean = false; + private static busyElements: {[key: string]: {busyEl: HTMLElement, listener: any}} = {}; + + private focussableElements: HTMLElement[]; constructor( private renderer: Renderer ) { } public ngOnDestroy(): void { - SkyWaitAdapterService.clearListener(); + this.clearListeners(); } public setWaitBounds(waitEl: ElementRef): void { @@ -32,7 +37,7 @@ export class SkyWaitAdapterService implements OnDestroy { isFullPage: boolean, isWaiting: boolean, isNonBlocking = false, - id: string = '' + waitCmp: SkyWaitComponent = undefined ): void { let busyEl = isFullPage ? document.body : waitEl.nativeElement.parentElement; let state = isWaiting ? 'true' : undefined; @@ -48,64 +53,67 @@ export class SkyWaitAdapterService implements OnDestroy { if (isFullPage) { SkyWaitAdapterService.isPageWaitActive = true; - } else { - SkyWaitAdapterService.busyElements[id] = busyEl; - } - - if (!SkyWaitAdapterService.activeListener) { - // Prevent tab navigation within the waited page - let endListenerFunc = this.renderer.listen(document.body, 'keydown', (event: KeyboardEvent) => { - if (event.key.toLowerCase() === 'tab') { - event.preventDefault(); - event.stopPropagation(); - event.stopImmediatePropagation(); - - if (SkyWaitAdapterService.isPageWaitActive) { + let endListenerFunc = this.renderer.listen( + document.body, + 'keydown', + (event: KeyboardEvent) => { + if (event.key.toLowerCase() === 'tab') { + (event.target as any).blur(); + event.preventDefault(); + event.stopPropagation(); + event.stopImmediatePropagation(); this.clearDocumentFocus(); - } else { - // Propagate tab navigation if attempted into waited element - this.focusNextElement(event.shiftKey); } - } }); - SkyWaitAdapterService.activeListener = endListenerFunc; + SkyWaitAdapterService.busyElements[waitCmp.id] = { + listener: endListenerFunc, + busyEl: undefined + }; + } else { + let endListenerFunc = this.renderer.listen( + busyEl, + 'focusin', + (event: KeyboardEvent) => { + if (!waitCmp.isNonBlocking) { + event.preventDefault(); + event.stopPropagation(); + event.stopImmediatePropagation(); + + if (SkyWaitAdapterService.isPageWaitActive) { + this.clearDocumentFocus(); + } else { + // Propagate tab navigation if attempted into waited element + let target: any = event.target; + target.blur(); + this.focusNextElement(target, this.isShift(event), busyEl); + } + } + }); + SkyWaitAdapterService.busyElements[waitCmp.id] = { + listener: endListenerFunc, + busyEl: busyEl + }; } } else { if (isFullPage) { SkyWaitAdapterService.isPageWaitActive = false; - } else if (id in SkyWaitAdapterService.busyElements) { - delete SkyWaitAdapterService.busyElements[id]; } - - // Clear the listener if we are done waiting all elements - if (Object.keys(SkyWaitAdapterService.busyElements).length === 0 && !SkyWaitAdapterService.isPageWaitActive) { - SkyWaitAdapterService.clearListener(); + if (waitCmp.id in SkyWaitAdapterService.busyElements) { + SkyWaitAdapterService.busyElements[waitCmp.id].listener(); + delete SkyWaitAdapterService.busyElements[waitCmp.id]; } } } } - private focusNextElement(shiftKey: boolean): void { - // Select all possible focussable elements - let focussableElements = - 'a[href], ' + - 'area[href], ' + - 'input:not([disabled]):not([tabindex=\'-1\']), ' + - 'button:not([disabled]):not([tabindex=\'-1\']), ' + - 'select:not([disabled]):not([tabindex=\'-1\']), ' + - 'textarea:not([disabled]):not([tabindex=\'-1\']), ' + - 'iframe, object, embed, ' + - '*[tabindex]:not([tabindex=\'-1\']), ' + - '*[contenteditable=true]'; - let focussable = Array.prototype.filter.call(document.body.querySelectorAll(focussableElements), - (element: any) => { - return element.offsetWidth > 0 || element.offsetHeight > 0 || element === document.activeElement; - }); - // If shift tab, go in the other direction + private focusNextElement(targetElement: HTMLElement, shiftKey: boolean, busyEl: Element): void { + let focussable = this.getFocussableElements(); + + // If shift tab, go in the other direction let modifier = shiftKey ? -1 : 1; // Find the next navigable element that isn't waiting - let startingIndex = focussable.indexOf(document.activeElement); + let startingIndex = focussable.indexOf(targetElement); let curIndex = startingIndex + modifier; while (focussable[curIndex] && this.isElementBusyOrHidden(focussable[curIndex])) { curIndex += modifier; @@ -133,16 +141,37 @@ export class SkyWaitAdapterService implements OnDestroy { this.clearDocumentFocus(); } } + + // clear focussableElements list + this.focussableElements = undefined; + } + + private isShift(event: Event): boolean { + // Determine if shift+tab was used based on element order + let elements = this.getFocussableElements().filter(elem => !this.isElementHidden(elem)); + + let previousInd = elements.indexOf((event as any).relatedTarget); + let currentInd = elements.indexOf(event.target as HTMLElement); + + return previousInd === currentInd + 1 + || (previousInd === 0 && currentInd === elements.length - 1) + || (previousInd > currentInd) + || !(event as any).relatedTarget; } - private isElementBusyOrHidden(element: any) { + private isElementHidden(element: any): boolean { const style = window.getComputedStyle(element); - if (style.display === 'none' || style.visibility === 'hidden') { + return style.display === 'none' || style.visibility === 'hidden'; + } + + private isElementBusyOrHidden(element: any): boolean { + if (this.isElementHidden(element)) { return true; } + for (let key of Object.keys(SkyWaitAdapterService.busyElements)) { - const parentElement = SkyWaitAdapterService.busyElements[key]; - if (parentElement.contains(element)) { + const parentElement = SkyWaitAdapterService.busyElements[key].busyEl; + if (parentElement && parentElement.contains(element)) { return true; } } @@ -156,14 +185,36 @@ export class SkyWaitAdapterService implements OnDestroy { document.body.focus(); } - private static clearListener(): void { - if (SkyWaitAdapterService.activeListener) { - SkyWaitAdapterService.activeListener(); - SkyWaitAdapterService.activeListener = undefined; + private getFocussableElements(): HTMLElement[] { + // Keep this cached so we can reduce querys + if (this.focussableElements) { + return this.focussableElements; } - this.isPageWaitActive = false; + // Select all possible focussable elements + let focussableElements = + 'a[href], ' + + 'area[href], ' + + 'input:not([disabled]):not([tabindex=\'-1\']), ' + + 'button:not([disabled]):not([tabindex=\'-1\']), ' + + 'select:not([disabled]):not([tabindex=\'-1\']), ' + + 'textarea:not([disabled]):not([tabindex=\'-1\']), ' + + 'iframe, object, embed, ' + + '*[tabindex]:not([tabindex=\'-1\']), ' + + '*[contenteditable=true]'; + + this.focussableElements = Array.prototype.filter.call( + document.body.querySelectorAll(focussableElements), + (element: any) => { + return element.offsetWidth > 0 || element.offsetHeight > 0 || element === document.activeElement; + }); + return this.focussableElements; + } + + private clearListeners(): void { + SkyWaitAdapterService.isPageWaitActive = false; for (let key of Object.keys(SkyWaitAdapterService.busyElements)) { + SkyWaitAdapterService.busyElements[key].listener(); delete SkyWaitAdapterService.busyElements[key]; } } diff --git a/src/app/public/modules/wait/wait.component.spec.ts b/src/app/public/modules/wait/wait.component.spec.ts index e4532f91..f140fd17 100644 --- a/src/app/public/modules/wait/wait.component.spec.ts +++ b/src/app/public/modules/wait/wait.component.spec.ts @@ -8,12 +8,13 @@ import { import { SkyLibResourcesService } from '@skyux/i18n'; + import { SkyLibResourcesTestService } from '@skyux/i18n/testing'; + import { - expect, - SkyAppTestUtility + expect } from '@skyux-sdk/testing'; import { @@ -27,7 +28,10 @@ import { import { SkyWaitComponent } from './wait.component'; -import { SkyWaitAdapterService } from './wait-adapter.service'; + +import { + SkyWaitAdapterService +} from './wait-adapter.service'; describe('Wait component', () => { beforeEach(() => { @@ -44,13 +48,8 @@ describe('Wait component', () => { }); }); - afterEach(() => { - (SkyWaitAdapterService as any).clearListener(); - }); - it('should show the wait element when isWaiting is set to true', async(() => { const fixture = TestBed.createComponent(SkyWaitComponent); - fixture.detectChanges(); let el = fixture.nativeElement; @@ -60,7 +59,6 @@ describe('Wait component', () => { fixture.detectChanges(); expect(el.querySelector('.sky-wait')).not.toBeNull(); - fixture.whenStable().then(() => { expect(fixture.nativeElement).toBeAccessible(); }); @@ -140,70 +138,68 @@ describe('Wait component', () => { tick(); fixture.detectChanges(); + let waitButton = document.body.querySelector('#inside-button'); let anchor0: any = document.body.querySelector('#anchor-0'); let anchor1: any = document.body.querySelector('#anchor-1'); let anchor2: any = document.body.querySelector('#anchor-2'); - anchor1.focus(); - SkyAppTestUtility.fireDomEvent(document.body, 'keydown', { - keyboardEventInit: { key: 'Tab' } - }); + anchor1.focus(); fixture.detectChanges(); tick(); fixture.detectChanges(); - expect(document.activeElement).toBe(anchor2); - anchor2.focus(); - SkyAppTestUtility.fireDomEvent(document.body, 'keydown', { - keyboardEventInit: { key: 'Tab', shiftKey: true } - }); + let event = Object.assign(document.createEvent('CustomEvent'), { relatedTarget: anchor1 }); + event.initEvent('focusin', true, true); + waitButton.dispatchEvent(event); fixture.detectChanges(); tick(); fixture.detectChanges(); - expect(document.activeElement).toBe(anchor1); + expect(document.activeElement).toBe(anchor2); - fixture.componentInstance.showAnchor2 = false; - anchor1.focus(); + anchor2.focus(); fixture.detectChanges(); tick(); fixture.detectChanges(); - SkyAppTestUtility.fireDomEvent(document.body, 'keydown', { - keyboardEventInit: { key: 'Tab', shiftKey: false } - }); + + event = Object.assign(document.createEvent('CustomEvent'), { relatedTarget: anchor2 }); + event.initEvent('focusin', true, true); + waitButton.dispatchEvent(event); fixture.detectChanges(); tick(); fixture.detectChanges(); - expect(document.activeElement).toBe(anchor0); + expect(document.activeElement).toBe(anchor1); // Wrapping navigation fixture.componentInstance.showAnchor0 = true; fixture.componentInstance.showAnchor2 = false; - anchor0.focus(); + anchor1.focus(); fixture.detectChanges(); tick(); fixture.detectChanges(); - SkyAppTestUtility.fireDomEvent(document.body, 'keydown', { - keyboardEventInit: { key: 'Tab', shiftKey: true } - }); + event = Object.assign(document.createEvent('CustomEvent'), { relatedTarget: anchor1 }); + event.initEvent('focusin', true, true); + waitButton.dispatchEvent(event); fixture.detectChanges(); tick(); fixture.detectChanges(); - expect(document.activeElement).toBe(anchor1); + expect(document.activeElement).toBe(anchor0); + // Invisible elements // test display:none fixture.componentInstance.showAnchor0 = true; fixture.componentInstance.showAnchor2 = true; fixture.componentInstance.anchor2Display = 'none'; + anchor0.focus(); fixture.detectChanges(); tick(); fixture.detectChanges(); - SkyAppTestUtility.fireDomEvent(document.body, 'keydown', { - keyboardEventInit: { key: 'Tab', shiftKey: true } - }); + event = Object.assign(document.createEvent('CustomEvent'), { relatedTarget: anchor0 }); + event.initEvent('focusin', true, true); + waitButton.dispatchEvent(event); fixture.detectChanges(); tick(); fixture.detectChanges(); @@ -218,10 +214,10 @@ describe('Wait component', () => { fixture.detectChanges(); tick(); fixture.detectChanges(); - SkyAppTestUtility.fireDomEvent(document.body, 'keydown', { - keyboardEventInit: { key: 'Tab', shiftKey: true } - }); + event = Object.assign(document.createEvent('CustomEvent'), { relatedTarget: anchor0 }); + event.initEvent('focusin', true, true); + waitButton.dispatchEvent(event); fixture.detectChanges(); tick(); fixture.detectChanges(); @@ -233,10 +229,10 @@ describe('Wait component', () => { fixture.detectChanges(); tick(); fixture.detectChanges(); - SkyAppTestUtility.fireDomEvent(document.body, 'keydown', { - keyboardEventInit: { key: 'Tab', shiftKey: false } - }); + event = Object.assign(document.createEvent('CustomEvent'), { relatedTarget: anchor1 }); + event.initEvent('focusin', true, true); + waitButton.dispatchEvent(event); fixture.detectChanges(); tick(); fixture.detectChanges(); @@ -248,9 +244,9 @@ describe('Wait component', () => { fixture.detectChanges(); anchor1.focus(); - SkyAppTestUtility.fireDomEvent(document.body, 'keydown', { - keyboardEventInit: { key: 'Tab' } - }); + event = Object.assign(document.createEvent('CustomEvent'), { relatedTarget: anchor1 }); + event.initEvent('focusin', true, true); + waitButton.dispatchEvent(event); fixture.detectChanges(); tick(); fixture.detectChanges(); @@ -278,7 +274,6 @@ describe('Wait component', () => { it('should set aria-busy on containing div when fullPage is set to false', () => { const fixture = TestBed.createComponent(SkyWaitTestComponent); - fixture.detectChanges(); let el = fixture.nativeElement; @@ -291,9 +286,8 @@ describe('Wait component', () => { expect(el.querySelector('.sky-wait-test-component').getAttribute('aria-busy')).toBeNull(); }); - it('should create listener on document body when fullPage is true', fakeAsync(() => { + it('should set isPageWaitActive when fullPage is true', fakeAsync(() => { let fixture = TestBed.createComponent(SkyWaitTestComponent); - const waitCmp = fixture.componentInstance.waitComponent; fixture.componentInstance.isNonBlocking = false; fixture.componentInstance.isFullPage = true; @@ -306,41 +300,14 @@ describe('Wait component', () => { tick(); fixture.detectChanges(); - expect((SkyWaitAdapterService as any).activeListener).toBeTruthy(); - expect(waitCmp.id in (SkyWaitAdapterService as any).busyElements).toBeFalsy(); + expect((SkyWaitAdapterService as any).isPageWaitActive).toBeTruthy(); fixture.componentInstance.isWaiting = false; fixture.detectChanges(); tick(); fixture.detectChanges(); - expect((SkyWaitAdapterService as any).activeListener).toBeFalsy(); - expect(waitCmp.id in (SkyWaitAdapterService as any).busyElements).toBeFalsy(); - })); - - it('should create listener on containing div when fullPage is set to false', fakeAsync(() => { - let fixture = TestBed.createComponent(SkyWaitTestComponent); - const waitCmp = fixture.componentInstance.waitComponent; - - fixture.componentInstance.isNonBlocking = false; - fixture.componentInstance.isFullPage = false; - fixture.detectChanges(); - tick(); - fixture.detectChanges(); - - fixture.componentInstance.isWaiting = true; - fixture.detectChanges(); - tick(); - fixture.detectChanges(); - - expect((SkyWaitAdapterService as any).activeListener).toBeTruthy(); - expect(waitCmp.id in (SkyWaitAdapterService as any).busyElements).toBeTruthy(); - - fixture.componentInstance.isWaiting = false; - fixture.detectChanges(); - - expect((SkyWaitAdapterService as any).activeListener).toBeFalsy(); - expect(waitCmp.id in (SkyWaitAdapterService as any).busyElements).toBeFalsy(); + expect((SkyWaitAdapterService as any).isPageWaitActive).toBeFalsy(); })); function getAriaLabel(): string { diff --git a/src/app/public/modules/wait/wait.component.ts b/src/app/public/modules/wait/wait.component.ts index 9c328927..cbd790d5 100644 --- a/src/app/public/modules/wait/wait.component.ts +++ b/src/app/public/modules/wait/wait.component.ts @@ -45,7 +45,7 @@ export class SkyWaitComponent implements OnInit { this.isFullPage, value, this.isNonBlocking, - this.id + this ); this._isWaiting = value; diff --git a/src/app/public/modules/wait/wait.service.spec.ts b/src/app/public/modules/wait/wait.service.spec.ts index df1e2500..ad68db71 100644 --- a/src/app/public/modules/wait/wait.service.spec.ts +++ b/src/app/public/modules/wait/wait.service.spec.ts @@ -1,6 +1,7 @@ import { ApplicationRef } from '@angular/core'; + import { TestBed, inject, @@ -19,6 +20,7 @@ import { import { SkyWaitService } from './wait.service'; + import { SkyWindowRefService } from '@skyux/core'; @@ -105,14 +107,13 @@ describe('Wait service', () => { waitService.beginBlockingPageWait(); tick(); applicationRef.tick(); - verifyBlockingPageWaitExists(true); + verifyBlockingPageWaitExists(true); + let button = document.body.querySelector('button'); - button.focus(); - expect(document.activeElement).toBe(button); - SkyAppTestUtility.fireDomEvent(document.body, 'keydown', { - keyboardEventInit: { key: 'tab' } - }); - button = document.body.querySelector('button'); + event = Object.assign(document.createEvent('CustomEvent'), { relatedTarget: document.body }); + event.initEvent('focusin', true, true); + button.dispatchEvent(event); + expect(document.activeElement).toBe(document.body); })); diff --git a/src/app/visual/wait/wait-demo.component.html b/src/app/visual/wait/wait-demo.component.html index 2263d8a8..1441b574 100644 --- a/src/app/visual/wait/wait-demo.component.html +++ b/src/app/visual/wait/wait-demo.component.html @@ -90,3 +90,9 @@ [isNonBlocking]="isNonBlocking" > + + From 4d63c8954030fbbcf58d7c7186fed87b7990c90c Mon Sep 17 00:00:00 2001 From: Conor Wright Date: Thu, 6 Dec 2018 16:25:52 -0500 Subject: [PATCH 16/22] fixed failing test --- src/app/public/modules/wait/wait.service.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/public/modules/wait/wait.service.spec.ts b/src/app/public/modules/wait/wait.service.spec.ts index ad68db71..2419c01a 100644 --- a/src/app/public/modules/wait/wait.service.spec.ts +++ b/src/app/public/modules/wait/wait.service.spec.ts @@ -110,7 +110,7 @@ describe('Wait service', () => { verifyBlockingPageWaitExists(true); let button = document.body.querySelector('button'); - event = Object.assign(document.createEvent('CustomEvent'), { relatedTarget: document.body }); + let event = Object.assign(document.createEvent('CustomEvent'), { relatedTarget: document.body }); event.initEvent('focusin', true, true); button.dispatchEvent(event); From 6ff12895e7afb1964b5087972d1e92cae2c2b035 Mon Sep 17 00:00:00 2001 From: Conor Wright Date: Fri, 7 Dec 2018 10:01:19 -0500 Subject: [PATCH 17/22] removed unused import --- src/app/public/modules/wait/wait.service.spec.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/app/public/modules/wait/wait.service.spec.ts b/src/app/public/modules/wait/wait.service.spec.ts index 2419c01a..4f71f623 100644 --- a/src/app/public/modules/wait/wait.service.spec.ts +++ b/src/app/public/modules/wait/wait.service.spec.ts @@ -9,10 +9,6 @@ import { tick } from '@angular/core/testing'; -import { - SkyAppTestUtility -} from '@blackbaud/skyux-builder/runtime/testing/browser'; - import { SkyWaitFixturesModule } from './fixtures/wait-fixtures.module'; From c34ba8776020bb75ff8815a9f1519512588caddd Mon Sep 17 00:00:00 2001 From: Conor Wright Date: Tue, 11 Dec 2018 13:39:39 -0500 Subject: [PATCH 18/22] switched back to using id instead of waitcmp --- .../modules/wait/wait-adapter.service.ts | 31 +++++++++---------- src/app/public/modules/wait/wait.component.ts | 2 +- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/app/public/modules/wait/wait-adapter.service.ts b/src/app/public/modules/wait/wait-adapter.service.ts index ec325975..1b4688b4 100644 --- a/src/app/public/modules/wait/wait-adapter.service.ts +++ b/src/app/public/modules/wait/wait-adapter.service.ts @@ -5,10 +5,6 @@ import { OnDestroy } from '@angular/core'; -import { - SkyWaitComponent -} from './wait.component'; - @Injectable() export class SkyWaitAdapterService implements OnDestroy { private static isPageWaitActive: boolean = false; @@ -37,7 +33,7 @@ export class SkyWaitAdapterService implements OnDestroy { isFullPage: boolean, isWaiting: boolean, isNonBlocking = false, - waitCmp: SkyWaitComponent = undefined + id?: string ): void { let busyEl = isFullPage ? document.body : waitEl.nativeElement.parentElement; let state = isWaiting ? 'true' : undefined; @@ -58,14 +54,17 @@ export class SkyWaitAdapterService implements OnDestroy { 'keydown', (event: KeyboardEvent) => { if (event.key.toLowerCase() === 'tab') { - (event.target as any).blur(); - event.preventDefault(); - event.stopPropagation(); - event.stopImmediatePropagation(); - this.clearDocumentFocus(); + let focussable = this.getFocussableElements(); + let edgeElem: any = focussable[focussable.length - 1]; + if (event.shiftKey) { + edgeElem = focussable[0]; + } + edgeElem.focus({ + preventScroll: true + }); } }); - SkyWaitAdapterService.busyElements[waitCmp.id] = { + SkyWaitAdapterService.busyElements[id] = { listener: endListenerFunc, busyEl: undefined }; @@ -74,7 +73,7 @@ export class SkyWaitAdapterService implements OnDestroy { busyEl, 'focusin', (event: KeyboardEvent) => { - if (!waitCmp.isNonBlocking) { + if (!isNonBlocking) { event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); @@ -89,7 +88,7 @@ export class SkyWaitAdapterService implements OnDestroy { } } }); - SkyWaitAdapterService.busyElements[waitCmp.id] = { + SkyWaitAdapterService.busyElements[id] = { listener: endListenerFunc, busyEl: busyEl }; @@ -98,9 +97,9 @@ export class SkyWaitAdapterService implements OnDestroy { if (isFullPage) { SkyWaitAdapterService.isPageWaitActive = false; } - if (waitCmp.id in SkyWaitAdapterService.busyElements) { - SkyWaitAdapterService.busyElements[waitCmp.id].listener(); - delete SkyWaitAdapterService.busyElements[waitCmp.id]; + if (id in SkyWaitAdapterService.busyElements) { + SkyWaitAdapterService.busyElements[id].listener(); + delete SkyWaitAdapterService.busyElements[id]; } } } diff --git a/src/app/public/modules/wait/wait.component.ts b/src/app/public/modules/wait/wait.component.ts index cbd790d5..9c328927 100644 --- a/src/app/public/modules/wait/wait.component.ts +++ b/src/app/public/modules/wait/wait.component.ts @@ -45,7 +45,7 @@ export class SkyWaitComponent implements OnInit { this.isFullPage, value, this.isNonBlocking, - this + this.id ); this._isWaiting = value; From 14f23b142ca0aa50a7260b3a37d5255a50008ffb Mon Sep 17 00:00:00 2001 From: Conor Wright Date: Thu, 13 Dec 2018 09:06:00 -0500 Subject: [PATCH 19/22] renamed id parameter --- src/app/public/modules/wait/wait-adapter.service.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/app/public/modules/wait/wait-adapter.service.ts b/src/app/public/modules/wait/wait-adapter.service.ts index 1b4688b4..156d2593 100644 --- a/src/app/public/modules/wait/wait-adapter.service.ts +++ b/src/app/public/modules/wait/wait-adapter.service.ts @@ -33,7 +33,7 @@ export class SkyWaitAdapterService implements OnDestroy { isFullPage: boolean, isWaiting: boolean, isNonBlocking = false, - id?: string + waitComponentId?: string ): void { let busyEl = isFullPage ? document.body : waitEl.nativeElement.parentElement; let state = isWaiting ? 'true' : undefined; @@ -64,7 +64,7 @@ export class SkyWaitAdapterService implements OnDestroy { }); } }); - SkyWaitAdapterService.busyElements[id] = { + SkyWaitAdapterService.busyElements[waitComponentId] = { listener: endListenerFunc, busyEl: undefined }; @@ -88,7 +88,7 @@ export class SkyWaitAdapterService implements OnDestroy { } } }); - SkyWaitAdapterService.busyElements[id] = { + SkyWaitAdapterService.busyElements[waitComponentId] = { listener: endListenerFunc, busyEl: busyEl }; @@ -97,9 +97,9 @@ export class SkyWaitAdapterService implements OnDestroy { if (isFullPage) { SkyWaitAdapterService.isPageWaitActive = false; } - if (id in SkyWaitAdapterService.busyElements) { - SkyWaitAdapterService.busyElements[id].listener(); - delete SkyWaitAdapterService.busyElements[id]; + if (waitComponentId in SkyWaitAdapterService.busyElements) { + SkyWaitAdapterService.busyElements[waitComponentId].listener(); + delete SkyWaitAdapterService.busyElements[waitComponentId]; } } } From e2c20b490350a14f2d4375889df5abf8ef67a06d Mon Sep 17 00:00:00 2001 From: Conor Wright Date: Thu, 13 Dec 2018 09:22:34 -0500 Subject: [PATCH 20/22] Made id private --- src/app/public/modules/wait/wait.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/public/modules/wait/wait.component.ts b/src/app/public/modules/wait/wait.component.ts index 9c328927..e803076a 100644 --- a/src/app/public/modules/wait/wait.component.ts +++ b/src/app/public/modules/wait/wait.component.ts @@ -30,7 +30,7 @@ export class SkyWaitComponent implements OnInit { @Input() public ariaLabel: string; - public id: string = `sky-wait-${++nextId}`; + private id: string = `sky-wait-${++nextId}`; @Input() public set isWaiting(value: boolean) { From 284d524deb824b1922934dab26994f70f241ad91 Mon Sep 17 00:00:00 2001 From: Conor Wright Date: Thu, 13 Dec 2018 09:23:10 -0500 Subject: [PATCH 21/22] moved to private area --- src/app/public/modules/wait/wait.component.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/public/modules/wait/wait.component.ts b/src/app/public/modules/wait/wait.component.ts index e803076a..8a8aa940 100644 --- a/src/app/public/modules/wait/wait.component.ts +++ b/src/app/public/modules/wait/wait.component.ts @@ -30,8 +30,6 @@ export class SkyWaitComponent implements OnInit { @Input() public ariaLabel: string; - private id: string = `sky-wait-${++nextId}`; - @Input() public set isWaiting(value: boolean) { if (value && !this._isFullPage) { @@ -75,6 +73,7 @@ export class SkyWaitComponent implements OnInit { public ariaLabelStream = new BehaviorSubject(''); + private id: string = `sky-wait-${++nextId}`; private _isFullPage: boolean; private _isWaiting: boolean; From 836d73fef2ff995bf399fe3cb0f21ffd0cb16545 Mon Sep 17 00:00:00 2001 From: Conor Wright Date: Thu, 13 Dec 2018 10:54:22 -0500 Subject: [PATCH 22/22] removed leftover code. Added additional coverage --- package.json | 3 +- .../wait/fixtures/wait.component.fixture.html | 34 ++-- .../wait/fixtures/wait.component.fixture.ts | 5 + .../modules/wait/wait-adapter.service.ts | 15 +- .../modules/wait/wait.component.spec.ts | 163 +++++++++++++++--- 5 files changed, 175 insertions(+), 45 deletions(-) diff --git a/package.json b/package.json index 4bd87774..0042c191 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "devDependencies": { "@blackbaud/skyux": "2.30.0", "@blackbaud/skyux-builder": "1.29.0", - "@skyux-sdk/builder-plugin-skyux": "1.0.0-rc.5" + "@skyux-sdk/builder-plugin-skyux": "1.0.0-rc.5", + "@skyux-sdk/testing": "^3.1.0" } } diff --git a/src/app/public/modules/wait/fixtures/wait.component.fixture.html b/src/app/public/modules/wait/fixtures/wait.component.fixture.html index 0d6e694b..b001715a 100644 --- a/src/app/public/modules/wait/fixtures/wait.component.fixture.html +++ b/src/app/public/modules/wait/fixtures/wait.component.fixture.html @@ -2,6 +2,10 @@ class="sky-btn sky-btn-secondary" id="anchor-0" href="/" + [ngStyle]="{ + 'display': anchor0Display, + 'visibility': anchor0Visibility + }" > Anchor tag 0 @@ -27,14 +31,22 @@ > - +
+ + + +
diff --git a/src/app/public/modules/wait/fixtures/wait.component.fixture.ts b/src/app/public/modules/wait/fixtures/wait.component.fixture.ts index 2a77b501..2ca04f65 100644 --- a/src/app/public/modules/wait/fixtures/wait.component.fixture.ts +++ b/src/app/public/modules/wait/fixtures/wait.component.fixture.ts @@ -21,9 +21,14 @@ export class SkyWaitTestComponent { public showAnchor0 = true; public showAnchor2 = true; + public anchor0Visibility: string = ''; + public anchor0Display: string = ''; + public anchor2Visibility: string = ''; public anchor2Display: string = ''; + public secondWaitIsWaiting: boolean = false; + @ViewChild(SkyWaitComponent) public waitComponent: SkyWaitComponent; } diff --git a/src/app/public/modules/wait/wait-adapter.service.ts b/src/app/public/modules/wait/wait-adapter.service.ts index 156d2593..ccc5b117 100644 --- a/src/app/public/modules/wait/wait-adapter.service.ts +++ b/src/app/public/modules/wait/wait-adapter.service.ts @@ -54,14 +54,11 @@ export class SkyWaitAdapterService implements OnDestroy { 'keydown', (event: KeyboardEvent) => { if (event.key.toLowerCase() === 'tab') { - let focussable = this.getFocussableElements(); - let edgeElem: any = focussable[focussable.length - 1]; - if (event.shiftKey) { - edgeElem = focussable[0]; - } - edgeElem.focus({ - preventScroll: true - }); + (event.target as any).blur(); + event.preventDefault(); + event.stopPropagation(); + event.stopImmediatePropagation(); + this.clearDocumentFocus(); } }); SkyWaitAdapterService.busyElements[waitComponentId] = { @@ -128,6 +125,8 @@ export class SkyWaitAdapterService implements OnDestroy { focussable[curIndex] && this.isElementBusyOrHidden(focussable[curIndex]) ) { + /* istanbul ignore next */ + /* sanity check */ curIndex += modifier; } diff --git a/src/app/public/modules/wait/wait.component.spec.ts b/src/app/public/modules/wait/wait.component.spec.ts index f140fd17..5724aa8f 100644 --- a/src/app/public/modules/wait/wait.component.spec.ts +++ b/src/app/public/modules/wait/wait.component.spec.ts @@ -14,7 +14,8 @@ import { } from '@skyux/i18n/testing'; import { - expect + expect, + SkyAppTestUtility } from '@skyux-sdk/testing'; import { @@ -123,6 +124,50 @@ describe('Wait component', () => { expect(el.querySelector('.sky-wait-mask-loading-blocking')).toBeNull(); }); + it('should prevent tab navigation and focus when fullPage is true', fakeAsync(() => { + let fixture = TestBed.createComponent(SkyWaitTestComponent); + fixture.detectChanges(); + + fixture.componentInstance.isNonBlocking = false; + fixture.componentInstance.isFullPage = true; + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + fixture.componentInstance.isWaiting = true; + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + SkyAppTestUtility.fireDomEvent(document.body, 'keydown', { + keyboardEventInit: { + key: 'tab' + } + }); + + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + expect(document.activeElement).toBe(document.body); + + let anchor2: any = document.body.querySelector('#anchor-2'); + fixture.componentInstance.secondWaitIsWaiting = true; + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + SkyAppTestUtility.fireDomEvent(anchor2, 'focusin', { + customEventInit: { + relatedTarget: document.body + } + }); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + expect(document.activeElement).toBe(document.body); + })); + it('should propagate tab navigation forward and backward avoiding waited element', fakeAsync(() => { let fixture = TestBed.createComponent(SkyWaitTestComponent); fixture.detectChanges(); @@ -148,9 +193,11 @@ describe('Wait component', () => { tick(); fixture.detectChanges(); - let event = Object.assign(document.createEvent('CustomEvent'), { relatedTarget: anchor1 }); - event.initEvent('focusin', true, true); - waitButton.dispatchEvent(event); + SkyAppTestUtility.fireDomEvent(waitButton, 'focusin', { + customEventInit: { + relatedTarget: anchor1 + } + }); fixture.detectChanges(); tick(); fixture.detectChanges(); @@ -161,9 +208,11 @@ describe('Wait component', () => { tick(); fixture.detectChanges(); - event = Object.assign(document.createEvent('CustomEvent'), { relatedTarget: anchor2 }); - event.initEvent('focusin', true, true); - waitButton.dispatchEvent(event); + SkyAppTestUtility.fireDomEvent(waitButton, 'focusin', { + customEventInit: { + relatedTarget: anchor2 + } + }); fixture.detectChanges(); tick(); @@ -178,9 +227,11 @@ describe('Wait component', () => { tick(); fixture.detectChanges(); - event = Object.assign(document.createEvent('CustomEvent'), { relatedTarget: anchor1 }); - event.initEvent('focusin', true, true); - waitButton.dispatchEvent(event); + SkyAppTestUtility.fireDomEvent(waitButton, 'focusin', { + customEventInit: { + relatedTarget: anchor1 + } + }); fixture.detectChanges(); tick(); fixture.detectChanges(); @@ -192,32 +243,55 @@ describe('Wait component', () => { fixture.componentInstance.showAnchor2 = true; fixture.componentInstance.anchor2Display = 'none'; - anchor0.focus(); + anchor1.focus(); fixture.detectChanges(); tick(); fixture.detectChanges(); - event = Object.assign(document.createEvent('CustomEvent'), { relatedTarget: anchor0 }); - event.initEvent('focusin', true, true); - waitButton.dispatchEvent(event); + SkyAppTestUtility.fireDomEvent(waitButton, 'focusin', { + customEventInit: { + relatedTarget: anchor1 + } + }); fixture.detectChanges(); tick(); fixture.detectChanges(); - expect(document.activeElement).toBe(anchor1); + expect(document.activeElement).toBe(anchor0); // test display:none fixture.componentInstance.showAnchor0 = true; fixture.componentInstance.showAnchor2 = true; fixture.componentInstance.anchor2Display = ''; fixture.componentInstance.anchor2Visibility = 'hidden'; - anchor0.focus(); + anchor1.focus(); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + SkyAppTestUtility.fireDomEvent(waitButton, 'focusin', { + customEventInit: { + relatedTarget: anchor1 + } + }); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + expect(document.activeElement).toBe(anchor0); + + fixture.componentInstance.showAnchor0 = true; + fixture.componentInstance.showAnchor2 = true; + fixture.componentInstance.anchor0Visibility = 'hidden'; + fixture.componentInstance.anchor2Display = 'none'; + anchor1.focus(); fixture.detectChanges(); tick(); fixture.detectChanges(); - event = Object.assign(document.createEvent('CustomEvent'), { relatedTarget: anchor0 }); - event.initEvent('focusin', true, true); - waitButton.dispatchEvent(event); + SkyAppTestUtility.fireDomEvent(waitButton, 'focusin', { + customEventInit: { + relatedTarget: anchor1 + } + }); fixture.detectChanges(); tick(); fixture.detectChanges(); @@ -230,9 +304,11 @@ describe('Wait component', () => { tick(); fixture.detectChanges(); - event = Object.assign(document.createEvent('CustomEvent'), { relatedTarget: anchor1 }); - event.initEvent('focusin', true, true); - waitButton.dispatchEvent(event); + SkyAppTestUtility.fireDomEvent(waitButton, 'focusin', { + customEventInit: { + relatedTarget: anchor1 + } + }); fixture.detectChanges(); tick(); fixture.detectChanges(); @@ -244,15 +320,52 @@ describe('Wait component', () => { fixture.detectChanges(); anchor1.focus(); - event = Object.assign(document.createEvent('CustomEvent'), { relatedTarget: anchor1 }); - event.initEvent('focusin', true, true); - waitButton.dispatchEvent(event); + SkyAppTestUtility.fireDomEvent(waitButton, 'focusin', { + customEventInit: { + relatedTarget: anchor1 + } + }); fixture.detectChanges(); tick(); fixture.detectChanges(); expect(document.activeElement).toBe(anchor1); })); + it('should ignore other blocking wait when propagating tab navigation', fakeAsync(() => { + let fixture = TestBed.createComponent(SkyWaitTestComponent); + fixture.detectChanges(); + + fixture.componentInstance.isNonBlocking = false; + fixture.componentInstance.isFullPage = false; + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + fixture.componentInstance.isWaiting = true; + fixture.componentInstance.secondWaitIsWaiting = true; + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + let waitButton = document.body.querySelector('#inside-button'); + let anchor0: any = document.body.querySelector('#anchor-0'); + let anchor1: any = document.body.querySelector('#anchor-1'); + anchor1.focus(); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + SkyAppTestUtility.fireDomEvent(waitButton, 'focusin', { + customEventInit: { + relatedTarget: anchor1 + } + }); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + expect(document.activeElement).toBe(anchor0); + })); + it('should set aria-busy on document body when fullPage is true', async(() => { const fixture = TestBed.createComponent(SkyWaitTestComponent);