fix(overlays): fix contentNodes for local and global overlays

Co-authored by: Thijs Louisse <Thijs.Louisse@ing.com>
This commit is contained in:
Joren Broekema 2019-09-27 12:05:31 +02:00
parent 9e39c6c19c
commit 733991d92c
15 changed files with 178 additions and 56 deletions

View file

@ -26,12 +26,12 @@ const myCtrl = overlays.add(
);
```
### BottomsheetController
### BottomSheetController
A specific extension of GlobalOverlayController configured to create accessible dialogs at the bottom of the screen.
```js
import { BottomsheetController } from '@lion/overlays';
import { BottomSheetController } from '@lion/overlays';
```
### ModalDialogController

View file

@ -159,7 +159,7 @@ TODO:
},
```
### Bottomsheet Controller
### BottomSheetController
```js
{

View file

@ -2,7 +2,10 @@ export { DynamicOverlayController } from './src/DynamicOverlayController.js';
export { GlobalOverlayController } from './src/GlobalOverlayController.js';
export { globalOverlaysStyle } from './src/globalOverlaysStyle.js';
export { LocalOverlayController } from './src/LocalOverlayController.js';
export { BottomsheetController } from './src/BottomsheetController.js';
export { BottomSheetController } from './src/BottomSheetController.js';
export { ModalDialogController } from './src/ModalDialogController.js';
export { overlays } from './src/overlays.js';
export { OverlaysManager } from './src/OverlaysManager.js';
// deprecated
export { BottomSheetController as BottomsheetController } from './src/BottomSheetController.js';

View file

@ -133,7 +133,7 @@ export class BaseOverlayController {
switchOut() {}
// eslint-disable-next-line class-methods-use-this
contentTemplateUpdated() {}
onContentUpdated() {}
__setupContent(params) {
if (params.contentTemplate && params.contentNode) {
@ -175,23 +175,15 @@ export class BaseOverlayController {
if (this.isShown) {
render(this.contentTemplate(this.contentData), this.content);
this.__contentNode = this.content.firstElementChild;
this.contentTemplateUpdated();
this.onContentUpdated();
} else {
render(html``, this.content);
this.__contentNode = undefined;
}
}
__showHideViaCss() {
if (!this.contentNode) {
return;
}
if (this.isShown) {
this.contentNode.style.display = 'inline-block';
} else {
this.contentNode.style.display = 'none';
}
}
// eslint-disable-next-line class-methods-use-this
__showHideViaCss() {}
// TODO: this method has to be removed when EventTarget polyfill is available on IE11
__fakeExtendsEventTarget() {

View file

@ -1,6 +1,6 @@
import { GlobalOverlayController } from './GlobalOverlayController.js';
export class BottomsheetController extends GlobalOverlayController {
export class BottomSheetController extends GlobalOverlayController {
constructor(params) {
super({
hasBackdrop: true,
@ -13,4 +13,9 @@ export class BottomsheetController extends GlobalOverlayController {
...params,
});
}
onContentUpdated() {
super.onContentUpdated();
this.contentNode.classList.add('global-overlays__overlay--bottom-sheet');
}
}

View file

@ -85,10 +85,29 @@ export class GlobalOverlayController extends BaseOverlayController {
this.__enableFeatures();
}
contentTemplateUpdated() {
onContentUpdated() {
this.contentNode.classList.add('global-overlays__overlay');
}
get contentNode() {
return this.__contentNode;
}
set contentNode(node) {
const wrapper = document.createElement('div');
wrapper.appendChild(node);
this.__contentNode = node;
this.content = wrapper;
this.onContentUpdated();
// setting a contentNode means hide/show with css
this.__showHideMode = 'css';
if (this.isShown === false) {
this.content.style.display = 'none';
}
}
/**
* Hides the overlay.
*/
@ -115,6 +134,18 @@ export class GlobalOverlayController extends BaseOverlayController {
this.defaultHideDone();
}
__showHideViaCss() {
if (!this.contentNode) {
return;
}
if (this.isShown) {
this.content.style.display = '';
} else {
this.content.style.display = 'none';
}
}
/**
* Sets up flags.
*/

View file

@ -239,6 +239,18 @@ export class LocalOverlayController extends BaseOverlayController {
}
}
__showHideViaCss() {
if (!this.contentNode) {
return;
}
if (this.isShown) {
this.contentNode.style.display = 'inline-block';
} else {
this.contentNode.style.display = 'none';
}
}
// **********************************************************************************************
// FEATURE - hidesOnOutsideClick
// **********************************************************************************************

View file

@ -65,6 +65,10 @@ export const globalOverlaysStyle = css`
align-items: center;
}
.global-overlays__overlay--bottom-sheet {
width: 100%;
}
.global-overlays.global-overlays--blocking-opened .global-overlays__overlay {
display: none;
}

View file

@ -1,24 +1,23 @@
import { storiesOf, html } from '@open-wc/demoing-storybook';
import { css } from '@lion/core';
import { overlays, BottomsheetController } from '../index.js';
import { overlays, BottomSheetController } from '../index.js';
const bottomsheetDemoStyle = css`
const bottomSheetDemoStyle = css`
.demo-overlay {
width: 100%;
background-color: white;
border: 1px solid lightgrey;
text-align: center;
}
`;
storiesOf('Global Overlay System|Bottomsheet', module).add('Default', () => {
const bottomsheetCtrl = overlays.add(
new BottomsheetController({
storiesOf('Global Overlay System|BottomSheet', module).add('Default', () => {
const bottomSheetCtrl = overlays.add(
new BottomSheetController({
contentTemplate: () => html`
<div class="demo-overlay">
<p>Bottomsheet</p>
<button @click="${() => bottomsheetCtrl.hide()}">Close</button>
<p>BottomSheet</p>
<button @click="${() => bottomSheetCtrl.hide()}">Close</button>
</div>
`,
}),
@ -26,11 +25,11 @@ storiesOf('Global Overlay System|Bottomsheet', module).add('Default', () => {
return html`
<style>
${bottomsheetDemoStyle}
${bottomSheetDemoStyle}
</style>
<a href="#">Anchor 1</a>
<button
@click="${event => bottomsheetCtrl.show(event.target)}"
@click="${event => bottomSheetCtrl.show(event.target)}"
aria-haspopup="dialog"
aria-expanded="false"
>

View file

@ -5,6 +5,7 @@ import {
GlobalOverlayController,
LocalOverlayController,
DynamicOverlayController,
BottomSheetController,
} from '../index.js';
import { overlays } from '../src/overlays.js';
@ -17,11 +18,9 @@ const dynamicOverlayDemoStyle = css`
}
.demo-overlay__global--small {
bottom: 0;
left: 0;
width: 100vw;
height: 90%;
background: #ccc;
height: 400px;
width: 100%;
background: #eee;
}
.demo-overlay__global--big {
@ -50,7 +49,7 @@ storiesOf('Dynamic Overlay System|Switching Overlays', module)
const ctrl = new DynamicOverlayController();
const global1 = overlays.add(
new GlobalOverlayController({
new BottomSheetController({
contentTemplate: () => html`
<div class="demo-overlay demo-overlay__global demo-overlay__global--small">
<p>I am for small screens < 600px</p>

View file

@ -1,6 +1,6 @@
import './global-overlay.stories.js';
import './modal-dialog.stories.js';
import './bottomsheet.stories.js';
import './bottom-sheet.stories.js';
import './local-overlay.stories.js';
import './local-overlay-placement.stories.js';
import './dynamic-overlay.stories.js';

View file

@ -181,21 +181,6 @@ export const runBaseOverlaySuite = createCtrlFn => {
});
describe('_showHideMode="css" (auto selected with .contentNode)', () => {
it('hides .contentNode via css on hide', async () => {
const ctrl = createCtrlFn({
contentNode: await fixture('<p>direct node</p>'),
});
await ctrl.show();
expect(ctrl.contentNode).to.be.displayed;
await ctrl.hide();
expect(ctrl.contentNode).not.to.be.displayed;
await ctrl.show();
expect(ctrl.contentNode).to.be.displayed;
});
// do we even want to support contentTemplate?
it.skip('hides .contentNode from a .contentTemplate via css on hide', async () => {
const ctrl = createCtrlFn({

View file

@ -1,9 +1,9 @@
import { expect, html } from '@open-wc/testing';
import { GlobalOverlayController } from '../src/GlobalOverlayController.js';
import { BottomsheetController } from '../src/BottomsheetController.js';
import { BottomSheetController } from '../src/BottomSheetController.js';
describe('BottomsheetController', () => {
describe('BottomSheetController', () => {
let defaultOptions;
before(() => {
@ -15,11 +15,11 @@ describe('BottomsheetController', () => {
});
it('extends GlobalOverlayController', () => {
expect(new BottomsheetController(defaultOptions)).to.be.instanceof(GlobalOverlayController);
expect(new BottomSheetController(defaultOptions)).to.be.instanceof(GlobalOverlayController);
});
it('has correct defaults', () => {
const controller = new BottomsheetController(defaultOptions);
const controller = new BottomSheetController(defaultOptions);
expect(controller.hasBackdrop).to.equal(true);
expect(controller.isBlocking).to.equal(false);
expect(controller.preventsScroll).to.equal(true);

View file

@ -154,6 +154,50 @@ describe('GlobalOverlayController', () => {
await ctrl.sync({ isShown: false, data: { text: 'goodbye world' } });
expect(getRenderedContainers().length).to.equal(0);
});
describe('contentNode instead of a lit template', () => {
it('accepts HTML Element (contentNode) as content', async () => {
const contentNode = await fixture(
html`
<p>my content</p>
`,
);
const ctrl = overlays.add(
new GlobalOverlayController({
contentNode,
}),
);
await ctrl.show();
// container, which contains only the contentNode and nothing more
expect(getRootNode().children.length).to.equal(1);
expect(getRootNode().children[0].classList.contains('global-overlays__overlay-container'))
.to.be.true;
expect(getRootNode().children[0]).to.have.trimmed.text('my content');
// overlay (the contentNode)
expect(getRootNode().children[0].children[0].classList.contains('global-overlays__overlay'))
.to.be.true;
});
it('sets contentNode styling to display flex by default', async () => {
const contentNode = await fixture(
html`
<p>my content</p>
`,
);
const ctrl = overlays.add(
new GlobalOverlayController({
contentNode,
}),
);
await ctrl.show();
expect(
window.getComputedStyle(getRootNode().children[0]).getPropertyValue('display'),
).to.equal('flex');
});
});
});
describe('elementToFocusAfterHide', () => {

View file

@ -49,7 +49,7 @@ describe('LocalOverlayController', () => {
it('renders holders for invoker and content', async () => {
const invokerNode = await fixture(html`
<div role="button" id="invoke">Invoker</div>
<div role="button" id="invoker">Invoker</div>
`);
const ctrl = new LocalOverlayController({
contentTemplate: () => html`
@ -63,7 +63,7 @@ describe('LocalOverlayController', () => {
</div>
`);
expect(el.querySelector('#invoke').textContent.trim()).to.equal('Invoker');
expect(el.querySelector('#invoker').textContent.trim()).to.equal('Invoker');
await ctrl.show();
expect(el.querySelector('#content').textContent.trim()).to.equal('Content');
});
@ -122,6 +122,54 @@ describe('LocalOverlayController', () => {
});
});
describe('nodes', () => {
it('accepts HTML Elements (contentNode) to render content', async () => {
const invokerNode = await fixture(html`
<div role="button" id="invoker">Invoker</div>
`);
const node = document.createElement('div');
node.innerHTML = '<div id="content">Content</div>';
const ctrl = new LocalOverlayController({
contentNode: node,
invokerNode,
});
const el = await fixture(html`
<div>
${ctrl.invoker} ${ctrl.content}
</div>
`);
expect(el.querySelector('#invoker').textContent.trim()).to.equal('Invoker');
await ctrl.show();
expect(el.querySelector('#content').textContent.trim()).to.equal('Content');
});
it('sets display to inline-block for contentNode by default', async () => {
const invokerNode = await fixture(html`
<div role="button" id="invoker">Invoker</div>
`);
const node = document.createElement('div');
node.innerHTML = '<div id="content">Content</div>';
const ctrl = new LocalOverlayController({
contentNode: node,
invokerNode,
});
const el = await fixture(html`
<div>
${ctrl.invoker} ${ctrl.content}
</div>
`);
await ctrl.show();
const contentWrapper = el.querySelector('#content').parentElement;
expect(contentWrapper.style.display).to.equal('inline-block');
});
});
// Please use absolute positions in the tests below to prevent the HTML generated by
// the test runner from interfering.
describe('positioning', () => {