fix(overlays): fix contentNodes for local and global overlays
Co-authored by: Thijs Louisse <Thijs.Louisse@ing.com>
This commit is contained in:
parent
9e39c6c19c
commit
733991d92c
15 changed files with 178 additions and 56 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ TODO:
|
|||
},
|
||||
```
|
||||
|
||||
### Bottomsheet Controller
|
||||
### BottomSheetController
|
||||
|
||||
```js
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// **********************************************************************************************
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -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', () => {
|
||||
|
|
|
|||
|
|
@ -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', () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue