fix(overlays): support backdrop with local overlay

This commit is contained in:
Max Larionov 2020-03-09 12:42:41 +01:00 committed by Thomas Allmer
parent 5c4974146e
commit e472b64f7b
6 changed files with 286 additions and 5 deletions

View file

@ -169,7 +169,7 @@ The interaction states of the `<lion-checkbox-group>` are evaluated in order to
{() => {
loadDefaultFeedbackMessages();
const validate = () => {
const checkboxGroup = document.querySelector('#scientistsGroup');
const checkboxGroup = document.querySelector('#scientists');
checkboxGroup.submitted = !checkboxGroup.submitted;
};
return html`
@ -201,7 +201,7 @@ The interaction states of the `<lion-checkbox-group>` are evaluated in order to
import { Required, loadDefaultFeedbackMessages } from '@lion/validate';
loadDefaultFeedbackMessages();
const validate = () => {
const checkboxGroup = document.querySelector('#scientistsGroup');
const checkboxGroup = document.querySelector('#scientists');
checkboxGroup.submitted = !checkboxGroup.submitted;
};
```

View file

@ -24,6 +24,7 @@ export class OverlayController {
placementMode: null,
contentNode: config.contentNode,
invokerNode: config.invokerNode,
backdropNode: config.backdropNode,
referenceNode: null,
elementToFocusAfterHide: config.invokerNode,
inheritsReferenceWidth: '',
@ -423,10 +424,38 @@ export class OverlayController {
*/
_handleBackdrop({ animation = true, phase }) {
if (this.placementMode === 'local') {
return; // coming soon...
switch (phase) {
case 'init':
if (!this.backdropNode) {
this.backdropNode = document.createElement('div');
this.backdropNode.classList.add('local-overlays__backdrop');
}
this.backdropNode.slot = '_overlay-shadow-outlet';
this._contentNodeWrapper.parentElement.insertBefore(
this.backdropNode,
this._contentNodeWrapper,
);
break;
case 'show':
this.__hasActiveBackdrop = true;
break;
case 'hide':
if (!this.backdropNode) {
return;
}
this.__hasActiveBackdrop = false;
break;
case 'teardown':
if (!this.backdropNode) {
return;
}
this.backdropNode.parentNode.removeChild(this.backdropNode);
break;
/* no default */
}
return;
}
const { backdropNode } = this;
switch (phase) {
case 'init':
this.backdropNode = document.createElement('div');

View file

@ -50,10 +50,11 @@ export const OverlayMixin = dedupeMixin(
* @returns {OverlayController}
*/
// eslint-disable-next-line
_defineOverlay({ contentNode, invokerNode }) {
_defineOverlay({ contentNode, invokerNode, backdropNode }) {
return new OverlayController({
contentNode,
invokerNode,
backdropNode,
...this._defineOverlayConfig(), // wc provided in the class as defaults
...this.config, // user provided (e.g. in template)
popperConfig: {
@ -144,6 +145,10 @@ export const OverlayMixin = dedupeMixin(
return Array.from(this.children).find(child => child.slot === 'invoker');
}
get _overlayBackdropNode() {
return Array.from(this.children).find(child => child.slot === 'backdrop');
}
get _overlayContentNode() {
if (this._cachedOverlayContentNode) {
return this._cachedOverlayContentNode;
@ -176,6 +181,7 @@ export const OverlayMixin = dedupeMixin(
this._overlayCtrl = this._defineOverlay({
contentNode: this._overlayContentNode,
invokerNode: this._overlayInvokerNode,
backdropNode: this._overlayBackdropNode,
});
this.__syncToOverlayController();
this.__setupSyncFromOverlayController();

View file

@ -584,3 +584,162 @@ Below an example is shown with the `isBlocking` option, which makes use of the O
</div>
</demo-overlay-system>
```
## Local Backdrop
We provide a possibility to add a backdrop to a locally placed overlay.
You can pass your backdropNode as a configuration parameter and control its styling reacting upon OverlayController events.
Here is the example below
<Story name="Local backdrop">
{() => {
let backdropNode = document.createElement('div');
backdropNode.classList.add('local-backdrop-01');
return html`
<style>
.local-backdrop-01 {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
background-color: red;
opacity: 0.3;
display: none;
}
</style>
<demo-overlay-system
@before-opened=${(e) => backdropNode.style.display = 'block'}
@before-closed=${(e) => backdropNode.style.display = 'none'}
.config=${{ hasBackdrop: true, placementMode: 'local', backdropNode }}
>
<button slot="invoker">Click me to open the overlay!</button>
<div slot="content" class="demo-overlay">
Hello! You can close this notification here:
<button
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
>
</button>
</div>
</demo-overlay-system>
`}}
</Story>
```js
let backdropNode = document.createElement('div');
backdropNode.classList.add('local-backdrop-01');
return html`
<style>
.local-backdrop-01 {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
background-color: red;
opacity: 0.3;
display: none;
}
</style>
<demo-overlay-system
@before-opened=${(e) => backdropNode.style.display = 'block'}
@before-closed=${(e) => backdropNode.style.display = 'none'}
.config=${{ hasBackdrop: true, placementMode: 'local', backdropNode }}
>
<button slot="invoker">Click me to open the overlay!</button>
<div slot="content" class="demo-overlay">
Hello! You can close this notification here:
<button
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
>
</button>
</div>
</demo-overlay-system>
```
## Declarative Local Backdrop
Another way to add custom backdrop is declaratively add an element with `slot="backdrop"`.
<Story name="Declarative Local Backdrop">
{() => {
const beforeOpened = () => {
document.querySelector('.local-backdrop-02').style.display = 'block';
}
const beforeClosed = () => {
document.querySelector('.local-backdrop-02').style.display = 'none';
}
return html`
<style>
.local-backdrop-02 {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
background-color: red;
opacity: 0.3;
display: none;
}
</style>
<demo-overlay-system
@before-opened=${beforeOpened}
@before-closed=${beforeClosed}
.config=${{ hasBackdrop: true, placementMode: 'local' }}
>
<div slot="backdrop" class="local-backdrop-02"></div>
<button slot="invoker">Click me to open the overlay!</button>
<div slot="content" class="demo-overlay">
Hello! You can close this notification here:
<button
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
>
</button>
</div>
</demo-overlay-system>
`}}
</Story>
```js
const beforeOpened = () => {
document.querySelector('.local-backdrop-02').style.display = 'block';
}
const beforeClosed = () => {
document.querySelector('.local-backdrop-02').style.display = 'none';
}
return html`
<style>
.local-backdrop-02 {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
background-color: red;
opacity: 0.3;
display: none;
}
</style>
<demo-overlay-system
@before-opened=${beforeOpened}
@before-closed=${beforeClosed}
.config=${{ hasBackdrop: true, placementMode: 'local' }}
>
<div slot="backdrop" class="local-backdrop-02"></div>
<button slot="invoker">Click me to open the overlay!</button>
<div slot="content" class="demo-overlay">
Hello! You can close this notification here:
<button
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
>
</button>
</div>
</demo-overlay-system>
```

View file

@ -671,6 +671,88 @@ describe('OverlayController', () => {
});
});
describe('locally placed overlay with hasBackdrop', () => {
it('has no backdrop by default', async () => {
const ctrl = new OverlayController({
...withLocalTestConfig(),
});
await ctrl.show();
expect(ctrl.backdropNode).to.be.undefined;
});
it('supports a backdrop option', async () => {
const ctrl = new OverlayController({
...withLocalTestConfig(),
hasBackdrop: false,
});
await ctrl.show();
expect(ctrl.backdropNode).to.be.undefined;
await ctrl.hide();
const backdropNode = document.createElement('div');
backdropNode.classList.add('custom-backdrop');
const controllerWithBackdrop = new OverlayController({
...withLocalTestConfig(),
hasBackdrop: true,
backdropNode,
});
await controllerWithBackdrop.show();
expect(controllerWithBackdrop.backdropNode).to.have.class('custom-backdrop');
});
it('reenables the backdrop when shown/hidden/shown', async () => {
const backdropNode = document.createElement('div');
backdropNode.classList.add('custom-backdrop');
const ctrl = new OverlayController({
...withLocalTestConfig(),
hasBackdrop: true,
backdropNode,
});
await ctrl.show();
expect(ctrl.backdropNode).to.have.class('custom-backdrop');
await ctrl.hide();
await ctrl.show();
expect(ctrl.backdropNode).to.have.class('custom-backdrop');
});
it('adds and stacks backdrops if .hasBackdrop is enabled', async () => {
const backdropNode = document.createElement('div');
backdropNode.classList.add('custom-backdrop-zero');
const ctrl0 = new OverlayController({
...withLocalTestConfig(),
hasBackdrop: true,
backdropNode,
});
await ctrl0.show();
expect(ctrl0.backdropNode).to.have.class('custom-backdrop-zero');
const ctrl1 = new OverlayController({
...withLocalTestConfig(),
hasBackdrop: false,
});
await ctrl1.show();
expect(ctrl0.backdropNode).to.have.class('custom-backdrop-zero');
expect(ctrl1.backdropNode).to.be.undefined;
const anotherBackdropNode = document.createElement('div');
anotherBackdropNode.classList.add('custom-backdrop-two');
const ctrl2 = new OverlayController({
...withLocalTestConfig(),
hasBackdrop: true,
backdropNode: anotherBackdropNode,
});
await ctrl2.show();
expect(ctrl0.backdropNode).to.have.class('custom-backdrop-zero');
expect(ctrl1.backdropNode).to.be.undefined;
expect(ctrl2.backdropNode).to.have.class('custom-backdrop-two');
});
});
describe('isBlocking', () => {
it('prevents showing of other overlays', async () => {
const ctrl0 = new OverlayController({

View file

@ -2266,6 +2266,11 @@
resolved "https://registry.yarnpkg.com/@open-wc/dedupe-mixin/-/dedupe-mixin-1.1.1.tgz#3ac8e498422ef316276bbe4aa687e35bd10c6871"
integrity sha512-Y1+h5nQjJnDHP+8OceZB47I4D7iOiYnM0jXYLGEi96IusR93et30BIyEEQAJ4AvYfbuIrdbf0L5vQWfszU6/Jg==
"@open-wc/dedupe-mixin@^1.2.1":
version "1.2.10"
resolved "https://registry.yarnpkg.com/@open-wc/dedupe-mixin/-/dedupe-mixin-1.2.10.tgz#4992874e98b8c49ed71e7e17d2adc7538d62260b"
integrity sha512-I3/aKV8OJ5LkZLOvTiGRgKs+o7VVz3EUozbc7yeKJo7x8+j+NHWhVvtNHE8GXAXbN3s4KmMWQt1mXWCEZwNg7g==
"@open-wc/demoing-storybook@^1.10.4":
version "1.10.5"
resolved "https://registry.yarnpkg.com/@open-wc/demoing-storybook/-/demoing-storybook-1.10.5.tgz#3886e01fcc3b13485d5bdb4904f4ec627895609f"