fix(overlays): add transition hooks to overlays

This commit is contained in:
Thomas Allmer 2020-09-29 23:44:50 +02:00
parent cfa2daf674
commit 278798636c
6 changed files with 72 additions and 3 deletions

View file

@ -0,0 +1,5 @@
---
'@lion/overlays': patch
---
Adds `async transitionShow` and `async transitionHide` to OverlayController to enable basic support for transitions/animations

View file

@ -9,7 +9,7 @@
"build:docs": "wca analyze \"packages/tabs/**/*.js\"", "build:docs": "wca analyze \"packages/tabs/**/*.js\"",
"build:types": "tsc -p tsconfig.build.types.json", "build:types": "tsc -p tsconfig.build.types.json",
"bundlesize": "rollup -c bundlesize/rollup.config.js && bundlesize", "bundlesize": "rollup -c bundlesize/rollup.config.js && bundlesize",
"debug": "web-test-runner \"packages/input-datepicker/test/**/*.test.js\" --watch", "debug": "web-test-runner \"packages/overlays/test/**/*.test.js\" --watch",
"dev-server": "es-dev-server", "dev-server": "es-dev-server",
"format": "npm run format:eslint && npm run format:prettier", "format": "npm run format:eslint && npm run format:prettier",
"format:eslint": "eslint --ext .js,.html . --fix", "format:eslint": "eslint --ext .js,.html . --fix",

View file

@ -2,7 +2,7 @@ import { LionCalendar } from '@lion/calendar';
import { isSameDate } from '@lion/calendar/src/utils/isSameDate.js'; import { isSameDate } from '@lion/calendar/src/utils/isSameDate.js';
import { html, LitElement } from '@lion/core'; import { html, LitElement } from '@lion/core';
import { IsDateDisabled, MaxDate, MinDate, MinMaxDate } from '@lion/form-core'; import { IsDateDisabled, MaxDate, MinDate, MinMaxDate } from '@lion/form-core';
import { aTimeout, defineCE, expect, fixture } from '@open-wc/testing'; import { aTimeout, defineCE, expect, fixture, nextFrame } from '@open-wc/testing';
import sinon from 'sinon'; import sinon from 'sinon';
import '../lion-input-datepicker.js'; import '../lion-input-datepicker.js';
import { LionInputDatepicker } from '../src/LionInputDatepicker.js'; import { LionInputDatepicker } from '../src/LionInputDatepicker.js';
@ -73,6 +73,7 @@ describe('<lion-input-datepicker>', () => {
elObj.overlayController.contentNode.dispatchEvent( elObj.overlayController.contentNode.dispatchEvent(
new KeyboardEvent('keyup', { key: 'Escape' }), new KeyboardEvent('keyup', { key: 'Escape' }),
); );
await nextFrame();
expect(elObj.overlayController.isShown).to.equal(false); expect(elObj.overlayController.isShown).to.equal(false);
}); });
@ -83,6 +84,7 @@ describe('<lion-input-datepicker>', () => {
expect(elObj.overlayController.isShown).to.equal(true); expect(elObj.overlayController.isShown).to.equal(true);
elObj.overlayCloseButtonEl.click(); elObj.overlayCloseButtonEl.click();
await nextFrame();
expect(elObj.overlayController.isShown).to.equal(false); expect(elObj.overlayController.isShown).to.equal(false);
}); });

View file

@ -678,6 +678,7 @@ export class OverlayController extends EventTargetShim {
await this._handlePosition({ phase: 'show' }); await this._handlePosition({ phase: 'show' });
this.__elementToFocusAfterHide = elementToFocusAfterHide; this.__elementToFocusAfterHide = elementToFocusAfterHide;
this.dispatchEvent(new Event('show')); this.dispatchEvent(new Event('show'));
await this.transitionShow({ backdropNode: this.backdropNode, contentNode: this.contentNode });
} }
/** @type {function} */ (this._showResolve)(); /** @type {function} */ (this._showResolve)();
} }
@ -782,7 +783,7 @@ export class OverlayController extends EventTargetShim {
const event = new CustomEvent('before-hide', { cancelable: true }); const event = new CustomEvent('before-hide', { cancelable: true });
this.dispatchEvent(event); this.dispatchEvent(event);
if (!event.defaultPrevented) { if (!event.defaultPrevented) {
// await this.transitionHide({ backdropNode: this.backdropNode, contentNode: this.contentNode }); await this.transitionHide({ backdropNode: this.backdropNode, contentNode: this.contentNode });
this.contentWrapperNode.style.display = 'none'; this.contentWrapperNode.style.display = 'none';
this._handleFeatures({ phase: 'hide' }); this._handleFeatures({ phase: 'hide' });
this._keepBodySize({ phase: 'hide' }); this._keepBodySize({ phase: 'hide' });
@ -798,6 +799,12 @@ export class OverlayController extends EventTargetShim {
// eslint-disable-next-line class-methods-use-this, no-empty-function, no-unused-vars // eslint-disable-next-line class-methods-use-this, no-empty-function, no-unused-vars
async transitionHide(config) {} async transitionHide(config) {}
/**
* @param {{backdropNode:HTMLElement, contentNode:HTMLElement}} config
*/
// eslint-disable-next-line class-methods-use-this, no-empty-function, no-unused-vars
async transitionShow(config) {}
_restoreFocus() { _restoreFocus() {
// We only are allowed to move focus if we (still) 'own' it. // We only are allowed to move focus if we (still) 'own' it.
// Otherwise we assume the 'outside world' has, purposefully, taken over // Otherwise we assume the 'outside world' has, purposefully, taken over

View file

@ -206,6 +206,7 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) {
</${tag}> </${tag}>
`)); `));
closeBtn.click(); closeBtn.click();
await nextFrame(); // hide takes at least a frame
expect(el.opened).to.be.false; expect(el.opened).to.be.false;
}); });
}); });

View file

@ -1100,6 +1100,60 @@ describe('OverlayController', () => {
await ctrl0.show(); await ctrl0.show();
expect(getTopEl()).to.equal(ctrl0.contentNode); expect(getTopEl()).to.equal(ctrl0.contentNode);
}); });
it('awaits a "transitionHide" hook before hiding for real', done => {
const ctrl = new OverlayController({
...withGlobalTestConfig(),
});
ctrl.show();
/** @type {{ (): void; (value?: void | PromiseLike<void> | undefined): void; }} */
let hideTransitionFinished;
ctrl.transitionHide = () =>
new Promise(resolve => {
hideTransitionFinished = resolve;
});
ctrl.hide();
expect(getComputedStyle(ctrl.contentWrapperNode).display).to.equal('block');
setTimeout(() => {
hideTransitionFinished();
setTimeout(() => {
expect(getComputedStyle(ctrl.contentWrapperNode).display).to.equal('none');
done();
}, 0);
}, 0);
});
it('awaits a "transitionShow" hook before finishing the show method', done => {
const ctrl = new OverlayController({
...withGlobalTestConfig(),
});
/** @type {{ (): void; (value?: void | PromiseLike<void> | undefined): void; }} */
let showTransitionFinished;
ctrl.transitionShow = () =>
new Promise(resolve => {
showTransitionFinished = resolve;
});
ctrl.show();
let showIsDone = false;
/** @type {Promise<void>} */ (ctrl._showComplete).then(() => {
showIsDone = true;
});
expect(showIsDone).to.be.false;
setTimeout(() => {
showTransitionFinished();
setTimeout(() => {
expect(showIsDone).to.be.true;
done();
}, 0);
}, 0);
});
}); });
describe('Update Configuration', () => { describe('Update Configuration', () => {