Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(rxjs-core): add
isPageVisible$()
- Loading branch information
Showing
12 changed files
with
268 additions
and
102 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { IsPageVisibleHarness } from './is-page-visible.harness'; |
49 changes: 49 additions & 0 deletions
49
projects/ng-dev/src/lib/rxjs-core-harnesses/is-page-visible.harness.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { Component, DoCheck } from '@angular/core'; | ||
import { isPageVisible$ } from '@s-libs/rxjs-core'; | ||
import { ComponentContext } from '../component-context'; | ||
import { IsPageVisibleHarness } from './is-page-visible.harness'; | ||
|
||
describe('IsPageVisibleHarness', () => { | ||
it('triggers change detection in an `AngularContext`', () => { | ||
@Component({ template: 'Hi mom' }) | ||
class TestComponent implements DoCheck { | ||
change = jasmine.createSpy(); | ||
|
||
ngDoCheck(): void { | ||
this.change(); | ||
} | ||
} | ||
|
||
const ctx = new ComponentContext(TestComponent); | ||
ctx.run(async () => { | ||
const isPageVisibleHarness = new IsPageVisibleHarness(); | ||
const { change } = ctx.getComponentInstance(); | ||
change.calls.reset(); | ||
|
||
isPageVisibleHarness.setVisible(false); | ||
expect(change).toHaveBeenCalled(); | ||
}); | ||
}); | ||
|
||
describe('example from the docs', () => { | ||
it('works for example 1', () => { | ||
const isPageVisibleHarness = new IsPageVisibleHarness(); | ||
isPageVisibleHarness.setVisible(false); | ||
|
||
const next = jasmine.createSpy(); | ||
isPageVisible$().subscribe(next); | ||
expect(next).toHaveBeenCalledWith(false); | ||
|
||
isPageVisibleHarness.setVisible(true); | ||
expect(next).toHaveBeenCalledWith(true); | ||
}); | ||
|
||
it('works for example 2', () => { | ||
const isPageVisibleHarness = new IsPageVisibleHarness(); | ||
expect(document.visibilityState).toBe('visible'); | ||
|
||
isPageVisibleHarness.setVisible(false); | ||
expect(document.visibilityState).toBe('hidden'); | ||
}); | ||
}); | ||
}); |
54 changes: 54 additions & 0 deletions
54
projects/ng-dev/src/lib/rxjs-core-harnesses/is-page-visible.harness.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { AngularContext } from '../angular-context'; | ||
|
||
/** | ||
* Use to control {@link isPageVisible$()} in tests. Create only one per test, before anything calls `isPageVisible$()`. | ||
* | ||
* ```ts | ||
* const isPageVisibleHarness = new IsPageVisibleHarness(); | ||
* isPageVisibleHarness.setVisible(false); | ||
* | ||
* const next = jasmine.createSpy(); | ||
* isPageVisible$().subscribe(next); | ||
* expect(next).toHaveBeenCalledWith(false); | ||
* | ||
* isPageVisibleHarness.setVisible(true); | ||
* expect(next).toHaveBeenCalledWith(true); | ||
* ``` | ||
* | ||
* It also stubs `document.visibilityState` to match. | ||
* ```ts | ||
* const isPageVisibleHarness = new IsPageVisibleHarness(); | ||
* expect(document.visibilityState).toBe('visible'); | ||
* | ||
* isPageVisibleHarness.setVisible(false); | ||
* expect(document.visibilityState).toBe('hidden'); | ||
* ``` | ||
*/ | ||
export class IsPageVisibleHarness { | ||
#visibilityState: jasmine.Spy; | ||
#notifyVisibilityChange: VoidFunction | undefined; | ||
|
||
constructor() { | ||
this.#visibilityState = spyOnProperty( | ||
document, | ||
'visibilityState', | ||
).and.returnValue('visible'); | ||
|
||
spyOn(document, 'addEventListener') | ||
.withArgs('visibilitychange', jasmine.anything(), undefined) | ||
.and.callFake( | ||
(_: string, handler: EventListenerOrEventListenerObject) => { | ||
this.#notifyVisibilityChange = handler as VoidFunction; | ||
}, | ||
); | ||
} | ||
|
||
/** | ||
* Sets the page's visibility state, and triggers any subscriptions to `isPageVisible$()`. Automatically triggers change detection if running with an {@linkcode AngularContext}. | ||
*/ | ||
setVisible(visible: boolean): void { | ||
this.#visibilityState.and.returnValue(visible ? 'visible' : 'hidden'); | ||
this.#notifyVisibilityChange?.(); | ||
AngularContext.getCurrent()?.tick(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { logToReduxDevtoolsExtension } from './log-to-redux-devtools-extension'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { expectSingleCallAndReset, IsPageVisibleHarness } from '@s-libs/ng-dev'; | ||
import { isPageVisible$ } from './is-page-visible'; | ||
|
||
describe('isPageVisible$()', () => { | ||
let harness: IsPageVisibleHarness; | ||
beforeEach(() => { | ||
harness = new IsPageVisibleHarness(); | ||
}); | ||
|
||
it('emits changes to page visibility', () => { | ||
const next = jasmine.createSpy(); | ||
isPageVisible$().subscribe(next); | ||
next.calls.reset(); | ||
|
||
harness.setVisible(false); | ||
expectSingleCallAndReset(next, false); | ||
|
||
harness.setVisible(true); | ||
expectSingleCallAndReset(next, true); | ||
}); | ||
|
||
it('emits immediately upon subscription', () => { | ||
const next1 = jasmine.createSpy(); | ||
isPageVisible$().subscribe(next1); | ||
expectSingleCallAndReset(next1, true); | ||
|
||
harness.setVisible(false); | ||
const next2 = jasmine.createSpy(); | ||
isPageVisible$().subscribe(next2); | ||
expectSingleCallAndReset(next2, false); | ||
}); | ||
|
||
it('is quiet about events that do not change visibility', () => { | ||
const next = jasmine.createSpy(); | ||
isPageVisible$().subscribe(next); | ||
|
||
harness.setVisible(true); | ||
harness.setVisible(true); | ||
expectSingleCallAndReset(next, true); | ||
|
||
harness.setVisible(false); | ||
harness.setVisible(false); | ||
expectSingleCallAndReset(next, false); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { distinctUntilChanged, fromEvent, Observable } from 'rxjs'; | ||
import { map, startWith } from 'rxjs/operators'; | ||
|
||
/** | ||
* Creates an observable that emits when the [page visibility]{@link https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API} changes. It also emits the current visibility immediately upon subscription. | ||
* | ||
* ```ts | ||
* isPageVisible$().subscribe((isVisible) => { | ||
* if (isVisible) { | ||
* console.log('Page is visible'); | ||
* } else { | ||
* console.log('Page is hidden'); | ||
* } | ||
* }); | ||
* ``` | ||
* | ||
* Note that for Angular projects, there is a harness available to help with tests that use this function in `@s-libs/ng-dev`. | ||
*/ | ||
export function isPageVisible$(): Observable<boolean> { | ||
return fromEvent(document, 'visibilitychange').pipe( | ||
startWith(undefined), | ||
map(() => document.visibilityState === 'visible'), | ||
distinctUntilChanged(), | ||
); | ||
} |
Oops, something went wrong.