feat(aio): support disabling `DocViewer` animations via class
This commit is contained in:
parent
74e3115686
commit
f8fe53aeb0
|
@ -13,7 +13,7 @@ import {
|
||||||
TestDocViewerComponent, TestModule, TestParentComponent
|
TestDocViewerComponent, TestModule, TestParentComponent
|
||||||
} from 'testing/doc-viewer-utils';
|
} from 'testing/doc-viewer-utils';
|
||||||
import { MockLogger } from 'testing/logger.service';
|
import { MockLogger } from 'testing/logger.service';
|
||||||
import { DocViewerComponent } from './doc-viewer.component';
|
import { DocViewerComponent, NO_ANIMATIONS } from './doc-viewer.component';
|
||||||
|
|
||||||
|
|
||||||
describe('DocViewerComponent', () => {
|
describe('DocViewerComponent', () => {
|
||||||
|
@ -656,122 +656,142 @@ describe('DocViewerComponent', () => {
|
||||||
beforeEach(() => DocViewerComponent.animationsEnabled = animationsEnabled);
|
beforeEach(() => DocViewerComponent.animationsEnabled = animationsEnabled);
|
||||||
afterEach(() => DocViewerComponent.animationsEnabled = true);
|
afterEach(() => DocViewerComponent.animationsEnabled = true);
|
||||||
|
|
||||||
it('should return an observable', done => {
|
[true, false].forEach(noAnimations => {
|
||||||
docViewer.swapViews().subscribe(done, done.fail);
|
describe(`(.${NO_ANIMATIONS}: ${noAnimations})`, () => {
|
||||||
});
|
beforeEach(() => docViewerEl.classList[noAnimations ? 'add' : 'remove'](NO_ANIMATIONS));
|
||||||
|
|
||||||
it('should swap the views', async () => {
|
it('should return an observable', done => {
|
||||||
await doSwapViews();
|
docViewer.swapViews().subscribe(done, done.fail);
|
||||||
|
});
|
||||||
|
|
||||||
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false);
|
it('should swap the views', async () => {
|
||||||
expect(docViewerEl.contains(oldNextViewContainer)).toBe(true);
|
await doSwapViews();
|
||||||
expect(docViewer.currViewContainer).toBe(oldNextViewContainer);
|
|
||||||
expect(docViewer.nextViewContainer).toBe(oldCurrViewContainer);
|
|
||||||
|
|
||||||
await doSwapViews();
|
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false);
|
||||||
|
expect(docViewerEl.contains(oldNextViewContainer)).toBe(true);
|
||||||
|
expect(docViewer.currViewContainer).toBe(oldNextViewContainer);
|
||||||
|
expect(docViewer.nextViewContainer).toBe(oldCurrViewContainer);
|
||||||
|
|
||||||
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(true);
|
await doSwapViews();
|
||||||
expect(docViewerEl.contains(oldNextViewContainer)).toBe(false);
|
|
||||||
expect(docViewer.currViewContainer).toBe(oldCurrViewContainer);
|
|
||||||
expect(docViewer.nextViewContainer).toBe(oldNextViewContainer);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should emit `docRemoved` after removing the leaving view', async () => {
|
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(true);
|
||||||
const onDocRemovedSpy = jasmine.createSpy('onDocRemoved').and.callFake(() => {
|
expect(docViewerEl.contains(oldNextViewContainer)).toBe(false);
|
||||||
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false);
|
expect(docViewer.currViewContainer).toBe(oldCurrViewContainer);
|
||||||
expect(docViewerEl.contains(oldNextViewContainer)).toBe(false);
|
expect(docViewer.nextViewContainer).toBe(oldNextViewContainer);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit `docRemoved` after removing the leaving view', async () => {
|
||||||
|
const onDocRemovedSpy = jasmine.createSpy('onDocRemoved').and.callFake(() => {
|
||||||
|
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false);
|
||||||
|
expect(docViewerEl.contains(oldNextViewContainer)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
docViewer.docRemoved.subscribe(onDocRemovedSpy);
|
||||||
|
|
||||||
|
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(true);
|
||||||
|
expect(docViewerEl.contains(oldNextViewContainer)).toBe(false);
|
||||||
|
|
||||||
|
await doSwapViews();
|
||||||
|
|
||||||
|
expect(onDocRemovedSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false);
|
||||||
|
expect(docViewerEl.contains(oldNextViewContainer)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not emit `docRemoved` if the leaving view is already removed', async () => {
|
||||||
|
const onDocRemovedSpy = jasmine.createSpy('onDocRemoved');
|
||||||
|
|
||||||
|
docViewer.docRemoved.subscribe(onDocRemovedSpy);
|
||||||
|
docViewerEl.removeChild(oldCurrViewContainer);
|
||||||
|
|
||||||
|
await doSwapViews();
|
||||||
|
|
||||||
|
expect(onDocRemovedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit `docInserted` after inserting the entering view', async () => {
|
||||||
|
const onDocInsertedSpy = jasmine.createSpy('onDocInserted').and.callFake(() => {
|
||||||
|
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false);
|
||||||
|
expect(docViewerEl.contains(oldNextViewContainer)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
docViewer.docInserted.subscribe(onDocInsertedSpy);
|
||||||
|
|
||||||
|
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(true);
|
||||||
|
expect(docViewerEl.contains(oldNextViewContainer)).toBe(false);
|
||||||
|
|
||||||
|
await doSwapViews();
|
||||||
|
|
||||||
|
expect(onDocInsertedSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false);
|
||||||
|
expect(docViewerEl.contains(oldNextViewContainer)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call the callback after inserting the entering view', async () => {
|
||||||
|
const onInsertedCb = jasmine.createSpy('onInsertedCb').and.callFake(() => {
|
||||||
|
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false);
|
||||||
|
expect(docViewerEl.contains(oldNextViewContainer)).toBe(true);
|
||||||
|
});
|
||||||
|
const onDocInsertedSpy = jasmine.createSpy('onDocInserted');
|
||||||
|
|
||||||
|
docViewer.docInserted.subscribe(onDocInsertedSpy);
|
||||||
|
|
||||||
|
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(true);
|
||||||
|
expect(docViewerEl.contains(oldNextViewContainer)).toBe(false);
|
||||||
|
|
||||||
|
await doSwapViews(onInsertedCb);
|
||||||
|
|
||||||
|
expect(onInsertedCb).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onInsertedCb).toHaveBeenCalledBefore(onDocInsertedSpy);
|
||||||
|
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false);
|
||||||
|
expect(docViewerEl.contains(oldNextViewContainer)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should empty the previous view', async () => {
|
||||||
|
await doSwapViews();
|
||||||
|
|
||||||
|
expect(docViewer.currViewContainer.innerHTML).toBe('Next view');
|
||||||
|
expect(docViewer.nextViewContainer.innerHTML).toBe('');
|
||||||
|
|
||||||
|
docViewer.nextViewContainer.innerHTML = 'Next view 2';
|
||||||
|
await doSwapViews();
|
||||||
|
|
||||||
|
expect(docViewer.currViewContainer.innerHTML).toBe('Next view 2');
|
||||||
|
expect(docViewer.nextViewContainer.innerHTML).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (animationsEnabled && !noAnimations) {
|
||||||
|
// Only test this when there are animations. Without animations, the views are swapped
|
||||||
|
// synchronously, so there is no need (or way) to abort.
|
||||||
|
it('should abort swapping if the returned observable is unsubscribed from', async () => {
|
||||||
|
docViewer.swapViews().subscribe().unsubscribe();
|
||||||
|
await doSwapViews();
|
||||||
|
|
||||||
|
// Since the first call was cancelled, only one swapping should have taken place.
|
||||||
|
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false);
|
||||||
|
expect(docViewerEl.contains(oldNextViewContainer)).toBe(true);
|
||||||
|
expect(docViewer.currViewContainer).toBe(oldNextViewContainer);
|
||||||
|
expect(docViewer.nextViewContainer).toBe(oldCurrViewContainer);
|
||||||
|
expect(docViewer.currViewContainer.innerHTML).toBe('Next view');
|
||||||
|
expect(docViewer.nextViewContainer.innerHTML).toBe('');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
it('should swap views synchronously when animations are disabled', () => {
|
||||||
|
const cbSpy = jasmine.createSpy('cb');
|
||||||
|
|
||||||
|
docViewer.swapViews(cbSpy).subscribe();
|
||||||
|
|
||||||
|
expect(cbSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false);
|
||||||
|
expect(docViewerEl.contains(oldNextViewContainer)).toBe(true);
|
||||||
|
expect(docViewer.currViewContainer).toBe(oldNextViewContainer);
|
||||||
|
expect(docViewer.nextViewContainer).toBe(oldCurrViewContainer);
|
||||||
|
expect(docViewer.currViewContainer.innerHTML).toBe('Next view');
|
||||||
|
expect(docViewer.nextViewContainer.innerHTML).toBe('');
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
docViewer.docRemoved.subscribe(onDocRemovedSpy);
|
|
||||||
|
|
||||||
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(true);
|
|
||||||
expect(docViewerEl.contains(oldNextViewContainer)).toBe(false);
|
|
||||||
|
|
||||||
await doSwapViews();
|
|
||||||
|
|
||||||
expect(onDocRemovedSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false);
|
|
||||||
expect(docViewerEl.contains(oldNextViewContainer)).toBe(true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not emit `docRemoved` if the leaving view is already removed', async () => {
|
|
||||||
const onDocRemovedSpy = jasmine.createSpy('onDocRemoved');
|
|
||||||
|
|
||||||
docViewer.docRemoved.subscribe(onDocRemovedSpy);
|
|
||||||
docViewerEl.removeChild(oldCurrViewContainer);
|
|
||||||
|
|
||||||
await doSwapViews();
|
|
||||||
|
|
||||||
expect(onDocRemovedSpy).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should emit `docInserted` after inserting the entering view', async () => {
|
|
||||||
const onDocInsertedSpy = jasmine.createSpy('onDocInserted').and.callFake(() => {
|
|
||||||
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false);
|
|
||||||
expect(docViewerEl.contains(oldNextViewContainer)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
docViewer.docInserted.subscribe(onDocInsertedSpy);
|
|
||||||
|
|
||||||
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(true);
|
|
||||||
expect(docViewerEl.contains(oldNextViewContainer)).toBe(false);
|
|
||||||
|
|
||||||
await doSwapViews();
|
|
||||||
|
|
||||||
expect(onDocInsertedSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false);
|
|
||||||
expect(docViewerEl.contains(oldNextViewContainer)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call the callback after inserting the entering view', async () => {
|
|
||||||
const onInsertedCb = jasmine.createSpy('onInsertedCb').and.callFake(() => {
|
|
||||||
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false);
|
|
||||||
expect(docViewerEl.contains(oldNextViewContainer)).toBe(true);
|
|
||||||
});
|
|
||||||
const onDocInsertedSpy = jasmine.createSpy('onDocInserted');
|
|
||||||
|
|
||||||
docViewer.docInserted.subscribe(onDocInsertedSpy);
|
|
||||||
|
|
||||||
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(true);
|
|
||||||
expect(docViewerEl.contains(oldNextViewContainer)).toBe(false);
|
|
||||||
|
|
||||||
await doSwapViews(onInsertedCb);
|
|
||||||
|
|
||||||
expect(onInsertedCb).toHaveBeenCalledTimes(1);
|
|
||||||
expect(onInsertedCb).toHaveBeenCalledBefore(onDocInsertedSpy);
|
|
||||||
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false);
|
|
||||||
expect(docViewerEl.contains(oldNextViewContainer)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should empty the previous view', async () => {
|
|
||||||
await doSwapViews();
|
|
||||||
|
|
||||||
expect(docViewer.currViewContainer.innerHTML).toBe('Next view');
|
|
||||||
expect(docViewer.nextViewContainer.innerHTML).toBe('');
|
|
||||||
|
|
||||||
docViewer.nextViewContainer.innerHTML = 'Next view 2';
|
|
||||||
await doSwapViews();
|
|
||||||
|
|
||||||
expect(docViewer.currViewContainer.innerHTML).toBe('Next view 2');
|
|
||||||
expect(docViewer.nextViewContainer.innerHTML).toBe('');
|
|
||||||
});
|
|
||||||
|
|
||||||
if (animationsEnabled) {
|
|
||||||
// Without animations, the views are swapped synchronously,
|
|
||||||
// so there is no need (or way) to abort.
|
|
||||||
it('should abort swapping if the returned observable is unsubscribed from', async () => {
|
|
||||||
docViewer.swapViews().subscribe().unsubscribe();
|
|
||||||
await doSwapViews();
|
|
||||||
|
|
||||||
// Since the first call was cancelled, only one swapping should have taken place.
|
|
||||||
expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false);
|
|
||||||
expect(docViewerEl.contains(oldNextViewContainer)).toBe(true);
|
|
||||||
expect(docViewer.currViewContainer).toBe(oldNextViewContainer);
|
|
||||||
expect(docViewer.nextViewContainer).toBe(oldCurrViewContainer);
|
|
||||||
expect(docViewer.currViewContainer.innerHTML).toBe('Next view');
|
|
||||||
expect(docViewer.nextViewContainer.innerHTML).toBe('');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,6 +15,9 @@ import { Logger } from 'app/shared/logger.service';
|
||||||
import { TocService } from 'app/shared/toc.service';
|
import { TocService } from 'app/shared/toc.service';
|
||||||
|
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
export const NO_ANIMATIONS = 'no-animations';
|
||||||
|
|
||||||
// Initialization prevents flicker once pre-rendering is on
|
// Initialization prevents flicker once pre-rendering is on
|
||||||
const initialDocViewerElement = document.querySelector('aio-doc-viewer');
|
const initialDocViewerElement = document.querySelector('aio-doc-viewer');
|
||||||
const initialDocViewerContent = initialDocViewerElement ? initialDocViewerElement.innerHTML : '';
|
const initialDocViewerContent = initialDocViewerElement ? initialDocViewerElement.innerHTML : '';
|
||||||
|
@ -186,8 +189,11 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
|
||||||
};
|
};
|
||||||
const animateProp =
|
const animateProp =
|
||||||
(elem: HTMLElement, prop: string, from: string, to: string, duration = 333) => {
|
(elem: HTMLElement, prop: string, from: string, to: string, duration = 333) => {
|
||||||
|
const animationsDisabled = !DocViewerComponent.animationsEnabled
|
||||||
|
|| this.hostElement.classList.contains(NO_ANIMATIONS);
|
||||||
|
|
||||||
elem.style.transition = '';
|
elem.style.transition = '';
|
||||||
return !DocViewerComponent.animationsEnabled
|
return animationsDisabled
|
||||||
? this.void$.do(() => elem.style[prop] = to)
|
? this.void$.do(() => elem.style[prop] = to)
|
||||||
: this.void$
|
: this.void$
|
||||||
// In order to ensure that the `from` value will be applied immediately (i.e.
|
// In order to ensure that the `from` value will be applied immediately (i.e.
|
||||||
|
|
Loading…
Reference in New Issue