fix: no longer use overlay templates
This commit is contained in:
parent
c899cf26d2
commit
49974bd2b8
24 changed files with 334 additions and 467 deletions
|
|
@ -34,8 +34,6 @@
|
||||||
"@lion/overlays": "^0.6.4"
|
"@lion/overlays": "^0.6.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lion/button": "^0.3.43",
|
|
||||||
"@lion/icon": "^0.2.9",
|
|
||||||
"@open-wc/demoing-storybook": "^0.2.0",
|
"@open-wc/demoing-storybook": "^0.2.0",
|
||||||
"@open-wc/testing": "^2.3.4"
|
"@open-wc/testing": "^2.3.4"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,21 +15,18 @@ export class LionDialog extends OverlayMixin(LitElement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
_setupOpenCloseListeners() {
|
_setupOpenCloseListeners() {
|
||||||
this.__close = () => {
|
|
||||||
this.opened = false;
|
|
||||||
};
|
|
||||||
this.__toggle = () => {
|
this.__toggle = () => {
|
||||||
this.opened = !this.opened;
|
this.opened = !this.opened;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this._overlayCtrl.invokerNode) {
|
if (this._overlayInvokerNode) {
|
||||||
this._overlayCtrl.invokerNode.addEventListener('click', this.__toggle);
|
this._overlayInvokerNode.addEventListener('click', this.__toggle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_teardownOpenCloseListeners() {
|
_teardownOpenCloseListeners() {
|
||||||
if (this._overlayCtrl.invokerNode) {
|
if (this._overlayInvokerNode) {
|
||||||
this._overlayCtrl.invokerNode.removeEventListener('click', this.__toggle);
|
this._overlayInvokerNode.removeEventListener('click', this.__toggle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
import { storiesOf, html, withKnobs, object } from '@open-wc/demoing-storybook';
|
import { storiesOf, html, withKnobs, object } from '@open-wc/demoing-storybook';
|
||||||
import { css } from '@lion/core';
|
import { css } from '@lion/core';
|
||||||
import '@lion/icon/lion-icon.js';
|
|
||||||
import '@lion/button/lion-button.js';
|
|
||||||
import '../lion-dialog.js';
|
import '../lion-dialog.js';
|
||||||
|
|
||||||
const dialogDemoStyle = css`
|
const dialogDemoStyle = css`
|
||||||
|
|
@ -67,24 +65,25 @@ storiesOf('Overlays Specific WC | Dialog', module)
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
To close your dialog from some action performed inside the content slot, fire a
|
To close your dialog from some action performed inside the content slot, fire a
|
||||||
<code>close</code> event.
|
<code>hide</code> event.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
For the dialog to close, it will need to bubble to the content slot (use
|
For the dialog to close, it will need to bubble to the content slot (use
|
||||||
<code>bubbles: true</code>. Also <code>composed: true</code> if it needs to traverse shadow
|
<code>bubbles: true</code>. If absolutely needed <code>composed: true</code> can be used to
|
||||||
boundaries)
|
traverse shadow boundaries)
|
||||||
</p>
|
</p>
|
||||||
<p>The demo below demonstrates this</p>
|
<p>The demo below demonstrates this</p>
|
||||||
<div class="demo-box">
|
<div class="demo-box">
|
||||||
<lion-dialog>
|
<lion-dialog>
|
||||||
<lion-button slot="invoker">Dialog</lion-button>
|
<button slot="invoker">Dialog</button>
|
||||||
<div slot="content" class="dialog">
|
<div slot="content" class="dialog">
|
||||||
Hello! You can close this notification here:
|
Hello! You can close this notification here:
|
||||||
<lion-button
|
<button
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click=${e => e.target.dispatchEvent(new Event('dialog-close', { bubbles: true }))}
|
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
||||||
>⨯</lion-button
|
|
||||||
>
|
>
|
||||||
|
⨯
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</lion-dialog>
|
</lion-dialog>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -93,14 +92,15 @@ storiesOf('Overlays Specific WC | Dialog', module)
|
||||||
.add('Custom configuration', () => {
|
.add('Custom configuration', () => {
|
||||||
const dialog = placement => html`
|
const dialog = placement => html`
|
||||||
<lion-dialog .config=${{ viewportConfig: { placement } }}>
|
<lion-dialog .config=${{ viewportConfig: { placement } }}>
|
||||||
<lion-button slot="invoker">Dialog ${placement}</lion-button>
|
<button slot="invoker">Dialog ${placement}</button>
|
||||||
<div slot="content" class="dialog">
|
<div slot="content" class="dialog">
|
||||||
Hello! You can close this notification here:
|
Hello! You can close this notification here:
|
||||||
<lion-button
|
<button
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click=${e => e.target.dispatchEvent(new Event('dialog-close', { bubbles: true }))}
|
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
||||||
>⨯</lion-button
|
|
||||||
>
|
>
|
||||||
|
⨯
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</lion-dialog>
|
</lion-dialog>
|
||||||
`;
|
`;
|
||||||
|
|
@ -118,14 +118,15 @@ storiesOf('Overlays Specific WC | Dialog', module)
|
||||||
.add('Toggle placement with knobs', () => {
|
.add('Toggle placement with knobs', () => {
|
||||||
const dialog = html`
|
const dialog = html`
|
||||||
<lion-dialog .config=${object('config', { viewportConfig: { placement: 'center' } })}>
|
<lion-dialog .config=${object('config', { viewportConfig: { placement: 'center' } })}>
|
||||||
<lion-button slot="invoker">Dialog</lion-button>
|
<button slot="invoker">Dialog</button>
|
||||||
<div slot="content" class="dialog">
|
<div slot="content" class="dialog">
|
||||||
Hello! You can close this notification here:
|
Hello! You can close this notification here:
|
||||||
<lion-button
|
<button
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click=${e => e.target.dispatchEvent(new Event('dialog-close', { bubbles: true }))}
|
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
||||||
>⨯</lion-button
|
|
||||||
>
|
>
|
||||||
|
⨯
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</lion-dialog>
|
</lion-dialog>
|
||||||
`;
|
`;
|
||||||
|
|
|
||||||
|
|
@ -1,77 +1,34 @@
|
||||||
import { expect, fixture, html } from '@open-wc/testing';
|
import { expect, fixture, html, unsafeStatic } from '@open-wc/testing';
|
||||||
|
import { runOverlayMixinSuite } from '@lion/overlays/test-suites/OverlayMixin.suite.js';
|
||||||
|
|
||||||
import '../lion-dialog.js';
|
import '../lion-dialog.js';
|
||||||
|
|
||||||
// Smoke tests dialog
|
|
||||||
describe('lion-dialog', () => {
|
describe('lion-dialog', () => {
|
||||||
describe('Basic', () => {
|
describe('Integration tests', () => {
|
||||||
it('should not be shown by default', async () => {
|
const tagString = 'lion-dialog';
|
||||||
const el = await fixture(html`
|
const tag = unsafeStatic(tagString);
|
||||||
<lion-dialog>
|
|
||||||
<div slot="content" class="dialog">Hey there</div>
|
runOverlayMixinSuite({
|
||||||
<lion-button slot="invoker">Popup button</lion-button>
|
tagString,
|
||||||
</lion-dialog>
|
tag,
|
||||||
`);
|
suffix: ' for lion-dialog',
|
||||||
expect(el._overlayCtrl.isShown).to.be.false;
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Basic', () => {
|
||||||
it('should show content on invoker click', async () => {
|
it('should show content on invoker click', async () => {
|
||||||
const el = await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-dialog>
|
<lion-dialog>
|
||||||
<div slot="content" class="dialog">
|
<div slot="content" class="dialog">
|
||||||
Hey there
|
Hey there
|
||||||
</div>
|
</div>
|
||||||
<lion-button slot="invoker">Popup button</lion-button>
|
<button slot="invoker">Popup button</button>
|
||||||
</lion-dialog>
|
</lion-dialog>
|
||||||
`);
|
`);
|
||||||
const invoker = el.querySelector('[slot="invoker"]');
|
const invoker = el.querySelector('[slot="invoker"]');
|
||||||
invoker.click();
|
invoker.click();
|
||||||
|
|
||||||
expect(el._overlayCtrl.isShown).to.be.true;
|
expect(el.opened).to.be.true;
|
||||||
});
|
|
||||||
|
|
||||||
it('should hide content on close event', async () => {
|
|
||||||
const el = await fixture(html`
|
|
||||||
<lion-dialog>
|
|
||||||
<div slot="content" class="dialog">
|
|
||||||
Hey there
|
|
||||||
<button
|
|
||||||
@click=${e => e.target.dispatchEvent(new Event('dialog-close', { bubbles: true }))}
|
|
||||||
>
|
|
||||||
x
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<lion-button slot="invoker">Popup button</lion-button>
|
|
||||||
</lion-dialog>
|
|
||||||
`);
|
|
||||||
const invoker = el.querySelector('[slot="invoker"]');
|
|
||||||
invoker.click();
|
|
||||||
|
|
||||||
expect(el._overlayCtrl.isShown).to.be.true;
|
|
||||||
|
|
||||||
const closeBtn = el._overlayCtrl.contentNode.querySelector('button');
|
|
||||||
closeBtn.click();
|
|
||||||
|
|
||||||
expect(el._overlayCtrl.isShown).to.be.false;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should respond to initially and dynamically setting the config', async () => {
|
|
||||||
const el = await fixture(html`
|
|
||||||
<lion-dialog .config=${{ trapsKeyboardFocus: false, viewportConfig: { placement: 'top' } }}>
|
|
||||||
<div slot="content" class="dialog">Hey there</div>
|
|
||||||
<lion-button slot="invoker">Popup button</lion-button>
|
|
||||||
</lion-dialog>
|
|
||||||
`);
|
|
||||||
await el._overlayCtrl.show();
|
|
||||||
expect(el._overlayCtrl.trapsKeyboardFocus).to.be.false;
|
|
||||||
|
|
||||||
el.config = { viewportConfig: { placement: 'left' } };
|
|
||||||
expect(el._overlayCtrl.viewportConfig.placement).to.equal('left');
|
|
||||||
expect(
|
|
||||||
el._overlayCtrl._contentNodeWrapper.classList.contains(
|
|
||||||
'global-overlays__overlay-container--left',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -90,14 +90,9 @@ export class LionCalendarOverlayFrame extends LocalizeMixin(LitElement) {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
__dispatchHideEvent() {
|
||||||
super();
|
|
||||||
this.__dispatchCloseEvent = this.__dispatchCloseEvent.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
__dispatchCloseEvent() {
|
|
||||||
// Designed to work in conjunction with ModalDialogController
|
// Designed to work in conjunction with ModalDialogController
|
||||||
this.dispatchEvent(new CustomEvent('dialog-close'), { bubbles: true, composed: true });
|
this.dispatchEvent(new CustomEvent('hide'), { bubbles: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
@ -109,7 +104,7 @@ export class LionCalendarOverlayFrame extends LocalizeMixin(LitElement) {
|
||||||
<slot name="heading"></slot>
|
<slot name="heading"></slot>
|
||||||
</h1>
|
</h1>
|
||||||
<button
|
<button
|
||||||
@click="${this.__dispatchCloseEvent}"
|
@click="${this.__dispatchHideEvent}"
|
||||||
id="close-button"
|
id="close-button"
|
||||||
title="${this.msgLit('lion-calendar-overlay-frame:close')}"
|
title="${this.msgLit('lion-calendar-overlay-frame:close')}"
|
||||||
aria-label="${this.msgLit('lion-calendar-overlay-frame:close')}"
|
aria-label="${this.msgLit('lion-calendar-overlay-frame:close')}"
|
||||||
|
|
|
||||||
|
|
@ -204,16 +204,23 @@ export class LionInputDatepicker extends OverlayMixin(LionInputDate) {
|
||||||
* this is our source to give as .contentNode to OverlayController.
|
* this is our source to give as .contentNode to OverlayController.
|
||||||
* Important: do not change the name of this method.
|
* Important: do not change the name of this method.
|
||||||
*/
|
*/
|
||||||
// TODO: Refactor to new overlay system public API --> @close=${() => { this.opened = false; }}
|
|
||||||
_overlayTemplate() {
|
_overlayTemplate() {
|
||||||
|
// TODO: add performance optimization to only render the calendar if needed
|
||||||
return html`
|
return html`
|
||||||
<lion-calendar-overlay-frame @dialog-close=${() => this._overlayCtrl.hide()}>
|
<lion-calendar-overlay-frame>
|
||||||
<span slot="heading">${this.calendarHeading}</span>
|
<span slot="heading">${this.calendarHeading}</span>
|
||||||
${this._calendarTemplate()}
|
${this._calendarTemplate()}
|
||||||
</lion-calendar-overlay-frame>
|
</lion-calendar-overlay-frame>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
${this.labelTemplate()} ${this.helpTextTemplate()} ${this.inputGroupTemplate()}
|
||||||
|
${this.feedbackTemplate()} ${this._overlayTemplate()}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclassers can replace this with their custom extension of
|
* Subclassers can replace this with their custom extension of
|
||||||
* LionCalendar, like `<my-calendar id="calendar"></my-calendar>`
|
* LionCalendar, like `<my-calendar id="calendar"></my-calendar>`
|
||||||
|
|
@ -331,4 +338,11 @@ export class LionInputDatepicker extends OverlayMixin(LionInputDate) {
|
||||||
get _overlayInvokerNode() {
|
get _overlayInvokerNode() {
|
||||||
return this._invokerElement;
|
return this._invokerElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override Configures OverlayMixin
|
||||||
|
*/
|
||||||
|
get _overlayContentNode() {
|
||||||
|
return this.shadowRoot.querySelector('lion-calendar-overlay-frame');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
[//]: # 'AUTO INSERT HEADER PREPUBLISH'
|
[//]: # 'AUTO INSERT HEADER PREPUBLISH'
|
||||||
|
|
||||||
> Note: Migrating from the old system (`overlays.add(new SomeController({...}))`)? Please check out our [migration guidelines](./docs/migration.md)
|
|
||||||
|
|
||||||
Supports different types of overlays like dialogs, toasts, tooltips, dropdown, etc.
|
Supports different types of overlays like dialogs, toasts, tooltips, dropdown, etc.
|
||||||
|
|
||||||
Manages their position on the screen relative to other elements, including other overlays.
|
Manages their position on the screen relative to other elements, including other overlays.
|
||||||
|
|
@ -16,7 +14,6 @@ Its purpose is to make it easy to use our Overlay System declaratively. It can b
|
||||||
- lion-overlay web component:
|
- lion-overlay web component:
|
||||||
|
|
||||||
- Show content when clicking the invoker
|
- Show content when clicking the invoker
|
||||||
- Respond to overlay-close event in the slot="content" element, to close the content
|
|
||||||
- Have a `.config` object to set or update the OverlayController's configuration
|
- Have a `.config` object to set or update the OverlayController's configuration
|
||||||
|
|
||||||
- [**OverlaysManager**](./docs/OverlaysManager.md), a global repository keeping track of all different types of overlays
|
- [**OverlaysManager**](./docs/OverlaysManager.md), a global repository keeping track of all different types of overlays
|
||||||
|
|
@ -37,14 +34,14 @@ npm i --save @lion/overlays
|
||||||
import '@lion/overlays/lion-overlay.js';
|
import '@lion/overlays/lion-overlay.js';
|
||||||
|
|
||||||
html`
|
html`
|
||||||
<lion-overlay.config=${{
|
<lion-overlay .config=${{
|
||||||
placementMode: 'global',
|
placementMode: 'global',
|
||||||
viewportConfig: { placement: 'bottom-right' },
|
viewportConfig: { placement: 'bottom-right' },
|
||||||
}}>
|
}}>
|
||||||
<div slot="content">
|
<div slot="content">
|
||||||
This is an overlay
|
This is an overlay
|
||||||
<button
|
<button
|
||||||
@click=${e => e.target.dispatchEvent(new Event('overlay-close', { bubbles: true }))}
|
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
||||||
>x</button>
|
>x</button>
|
||||||
<div>
|
<div>
|
||||||
<button slot="invoker">
|
<button slot="invoker">
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ All boolean flags default to 'false'.
|
||||||
```text
|
```text
|
||||||
- {Boolean} trapsKeyboardFocus - rotates tab.
|
- {Boolean} trapsKeyboardFocus - rotates tab.
|
||||||
- {Boolean} hidesOnEsc - hides the overlay when pressing [esc].
|
- {Boolean} hidesOnEsc - hides the overlay when pressing [esc].
|
||||||
|
- {Boolean} hidesOnHideEventInContentNode - (defaults to true) hides if an event called "hide" is fired within the content
|
||||||
|
- {Boolean} hidesOnOutsideClick - hides if user clicks outside of the overlay
|
||||||
- {Element} elementToFocusAfterHide - the element that should be called `.focus()` on after dialog closes.
|
- {Element} elementToFocusAfterHide - the element that should be called `.focus()` on after dialog closes.
|
||||||
- {Boolean} hasBackdrop - whether it should have a backdrop. (local mode only)
|
- {Boolean} hasBackdrop - whether it should have a backdrop. (local mode only)
|
||||||
- {Boolean} isBlocking - hides other overlays when multiple are opened.
|
- {Boolean} isBlocking - hides other overlays when multiple are opened.
|
||||||
|
|
|
||||||
|
|
@ -1,141 +0,0 @@
|
||||||
# Migration Guidelines Overlay System
|
|
||||||
|
|
||||||
If you are still using the old overlay system, we encourage you to migrate. The new way is more reliable, less error-prone and a lot easier to maintain. In addition, we now have a web component `lion-dialog` which is a declarative way of adding a modal dialog inside your template!
|
|
||||||
|
|
||||||
## Declaratively (encouraged)
|
|
||||||
|
|
||||||
Using generic `lion-overlay`:
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { withBottomSheetConfig } from '@lion/overlays';
|
|
||||||
import '@lion/overlays/lion-overlay.js';
|
|
||||||
|
|
||||||
const template = html`
|
|
||||||
<lion-overlay
|
|
||||||
.config=${{
|
|
||||||
...withBottomSheetConfig(),
|
|
||||||
viewportConfig: { placement: 'top-right' },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<button slot="invoker">Click me!</button>
|
|
||||||
<div slot="content">
|
|
||||||
<div>Hello, World!</div>
|
|
||||||
<button @click=${e => e.target.dispatchEvent(new Event('overlay-close', { bubbles: true }))}>
|
|
||||||
Close
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</lion-overlay>
|
|
||||||
`;
|
|
||||||
```
|
|
||||||
|
|
||||||
Or using a more specific component like `lion-tooltip`, which toggles on-hover:
|
|
||||||
|
|
||||||
```js
|
|
||||||
import '@lion/tooltip/lion-tooltip.js';
|
|
||||||
|
|
||||||
const template = html`
|
|
||||||
<lion-tooltip .config=${{ popperConfig: { placement: 'top-right' } }}>
|
|
||||||
<button slot="invoker">Hover me!</button>
|
|
||||||
<div slot="content">
|
|
||||||
<div>Hello, World!</div>
|
|
||||||
</div>
|
|
||||||
</lion-tooltip>
|
|
||||||
`;
|
|
||||||
```
|
|
||||||
|
|
||||||
Or `lion-dialog` which uses modal dialog configuration defaults
|
|
||||||
|
|
||||||
```js
|
|
||||||
import '@lion/dialog/lion-dialog.js';
|
|
||||||
|
|
||||||
const template = html`
|
|
||||||
<lion-dialog .config=${{ viewportConfig: { placement: 'top-right' } }}>
|
|
||||||
<button slot="invoker">Click me!</button>
|
|
||||||
<div slot="content">
|
|
||||||
<div>Hello, World!</div>
|
|
||||||
<button @click=${e => e.target.dispatchEvent(new Event('dialog-close', { bubbles: true }))}>
|
|
||||||
Close
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</lion-dialog>
|
|
||||||
`;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Instantiating an overlay controller (discouraged)
|
|
||||||
|
|
||||||
### Old
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { overlays, GlobalOverlayController } from '@lion/overlays';
|
|
||||||
|
|
||||||
const ctrl = overlays.add(
|
|
||||||
new GlobalOverlayController({
|
|
||||||
contentTemplate: () => html`
|
|
||||||
<div>My content</div>
|
|
||||||
`,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const template = html`
|
|
||||||
<lion-button @click="${event => ctrl.show(event.target)}">
|
|
||||||
Open dialog
|
|
||||||
</lion-button>
|
|
||||||
`;
|
|
||||||
```
|
|
||||||
|
|
||||||
### New
|
|
||||||
|
|
||||||
> Note: The OverlayController is render-system agnostic, you are responsible for passing a node (and rendering it prior).
|
|
||||||
> For lit-html, we will use a simple helper. Let us know if you think we should export this.
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { render } from '@lion/core';
|
|
||||||
|
|
||||||
function renderOffline(litHtmlTemplate) {
|
|
||||||
const offlineRenderContainer = document.createElement('div');
|
|
||||||
render(litHtmlTemplate, offlineRenderContainer);
|
|
||||||
return offlineRenderContainer.firstElementChild;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This example shows how you can use our configuration generators.
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { OverlayController, withModalDialogConfig } from '@lion/overlays';
|
|
||||||
|
|
||||||
const ctrl = new OverlayController({
|
|
||||||
...withModalDialogConfig(),
|
|
||||||
contentTemplate: renderOffline(html`
|
|
||||||
<div>My content</div>
|
|
||||||
`),
|
|
||||||
});
|
|
||||||
|
|
||||||
const template = html`
|
|
||||||
<lion-button @click="${event => ctrl.show(event.target)}">
|
|
||||||
Open dialog
|
|
||||||
</lion-button>
|
|
||||||
`;
|
|
||||||
```
|
|
||||||
|
|
||||||
### New (local example)
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { OverlayController } from '@lion/overlays';
|
|
||||||
|
|
||||||
const ctrl = new OverlayController({
|
|
||||||
...withModalDialogConfig(),
|
|
||||||
placementMode: 'local',
|
|
||||||
hidesOnEsc: true,
|
|
||||||
hidesOnOutsideClick: true,
|
|
||||||
contentNode: renderOffline(html`
|
|
||||||
<div>United Kingdom</div>
|
|
||||||
`),
|
|
||||||
invokerNode: renderOffline(html`
|
|
||||||
<button @click=${() => ctrl.toggle()}>UK</button>
|
|
||||||
`),
|
|
||||||
});
|
|
||||||
|
|
||||||
const template = html`
|
|
||||||
<div>In the ${ctrl.invoker}${ctrl.content} the weather is nice.</div>
|
|
||||||
`;
|
|
||||||
```
|
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
"stories",
|
"stories",
|
||||||
"test",
|
"test",
|
||||||
"test-helpers",
|
"test-helpers",
|
||||||
|
"test-suites",
|
||||||
"translations",
|
"translations",
|
||||||
"*.js"
|
"*.js"
|
||||||
],
|
],
|
||||||
|
|
@ -37,8 +38,6 @@
|
||||||
"popper.js": "^1.15.0"
|
"popper.js": "^1.15.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lion/button": "^0.3.43",
|
|
||||||
"@lion/icon": "^0.2.9",
|
|
||||||
"@open-wc/demoing-storybook": "^0.2.0",
|
"@open-wc/demoing-storybook": "^0.2.0",
|
||||||
"@open-wc/testing": "^2.3.4",
|
"@open-wc/testing": "^2.3.4",
|
||||||
"@open-wc/testing-helpers": "^1.0.0",
|
"@open-wc/testing-helpers": "^1.0.0",
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ export class OverlayController {
|
||||||
trapsKeyboardFocus: false,
|
trapsKeyboardFocus: false,
|
||||||
hidesOnEsc: false,
|
hidesOnEsc: false,
|
||||||
hidesOnOutsideClick: false,
|
hidesOnOutsideClick: false,
|
||||||
|
hidesOnHideEventInContentNode: true,
|
||||||
isTooltip: false,
|
isTooltip: false,
|
||||||
handlesUserInteraction: false,
|
handlesUserInteraction: false,
|
||||||
handlesAccessibility: false,
|
handlesAccessibility: false,
|
||||||
|
|
@ -336,6 +337,9 @@ export class OverlayController {
|
||||||
if (this.hidesOnOutsideClick) {
|
if (this.hidesOnOutsideClick) {
|
||||||
this._handleHidesOnOutsideClick({ phase });
|
this._handleHidesOnOutsideClick({ phase });
|
||||||
}
|
}
|
||||||
|
if (this.hidesOnHideEventInContentNode) {
|
||||||
|
this._handleHidesOnHideEventInContentNode({ phase });
|
||||||
|
}
|
||||||
if (this.handlesAccessibility) {
|
if (this.handlesAccessibility) {
|
||||||
this._handleAccessibility({ phase });
|
this._handleAccessibility({ phase });
|
||||||
}
|
}
|
||||||
|
|
@ -483,12 +487,28 @@ export class OverlayController {
|
||||||
if (phase === 'show') {
|
if (phase === 'show') {
|
||||||
this.__escKeyHandler = ev => ev.key === 'Escape' && this.hide();
|
this.__escKeyHandler = ev => ev.key === 'Escape' && this.hide();
|
||||||
this.contentNode.addEventListener('keyup', this.__escKeyHandler);
|
this.contentNode.addEventListener('keyup', this.__escKeyHandler);
|
||||||
|
if (this.invokerNode) {
|
||||||
this.invokerNode.addEventListener('keyup', this.__escKeyHandler);
|
this.invokerNode.addEventListener('keyup', this.__escKeyHandler);
|
||||||
|
}
|
||||||
} else if (phase === 'hide') {
|
} else if (phase === 'hide') {
|
||||||
this.contentNode.removeEventListener('keyup', this.__escKeyHandler);
|
this.contentNode.removeEventListener('keyup', this.__escKeyHandler);
|
||||||
|
if (this.invokerNode) {
|
||||||
this.invokerNode.removeEventListener('keyup', this.__escKeyHandler);
|
this.invokerNode.removeEventListener('keyup', this.__escKeyHandler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleHidesOnHideEventInContentNode({ phase }) {
|
||||||
|
if (phase === 'show') {
|
||||||
|
this.__hideEventInContentNodeHandler = ev => {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this.hide();
|
||||||
|
};
|
||||||
|
this.contentNode.addEventListener('hide', this.__hideEventInContentNodeHandler);
|
||||||
|
} else if (phase === 'hide') {
|
||||||
|
this.contentNode.removeEventListener('keyup', this.__hideEventInContentNodeHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_handleInheritsReferenceWidth() {
|
_handleInheritsReferenceWidth() {
|
||||||
if (!this._referenceNode) {
|
if (!this._referenceNode) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { render, dedupeMixin } from '@lion/core';
|
import { dedupeMixin } from '@lion/core';
|
||||||
import { OverlayController } from './OverlayController.js';
|
import { OverlayController } from './OverlayController.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -16,54 +16,26 @@ export const OverlayMixin = dedupeMixin(
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
reflect: true,
|
reflect: true,
|
||||||
},
|
},
|
||||||
config: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
closeEventName: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
this.opened = false;
|
||||||
this.config = {};
|
this.config = {};
|
||||||
this.closeEventName = 'overlay-close';
|
|
||||||
}
|
|
||||||
|
|
||||||
get opened() {
|
|
||||||
return this._overlayCtrl.isShown;
|
|
||||||
}
|
|
||||||
|
|
||||||
set opened(show) {
|
|
||||||
if (show) {
|
|
||||||
this.dispatchEvent(new Event('before-show'));
|
|
||||||
}
|
|
||||||
this._opened = show; // mainly captured for sync on connectedCallback
|
|
||||||
if (this._overlayCtrl) {
|
|
||||||
this.__syncOpened();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get config() {
|
get config() {
|
||||||
return this._config;
|
return this.__config;
|
||||||
}
|
}
|
||||||
|
|
||||||
set config(value) {
|
set config(value) {
|
||||||
if (this._overlayCtrl) {
|
if (this._overlayCtrl) {
|
||||||
this._overlayCtrl.updateConfig(value);
|
this._overlayCtrl.updateConfig(value);
|
||||||
}
|
}
|
||||||
this._config = value;
|
this.__config = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @overridable method `_overlayTemplate`
|
|
||||||
* Be aware that the overlay will be placed in a different shadow root.
|
|
||||||
* Therefore, style encapsulation should be provided by the contents of
|
|
||||||
* _overlayTemplate
|
|
||||||
* @return {TemplateResult}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @overridable method `_defineOverlay`
|
* @overridable method `_defineOverlay`
|
||||||
* @desc returns an instance of a (dynamic) overlay controller
|
* @desc returns an instance of a (dynamic) overlay controller
|
||||||
|
|
@ -88,7 +60,19 @@ export const OverlayMixin = dedupeMixin(
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
_defineOverlayConfig() {
|
_defineOverlayConfig() {
|
||||||
return {};
|
return {
|
||||||
|
placementMode: 'local',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
updated(changedProperties) {
|
||||||
|
super.updated(changedProperties);
|
||||||
|
|
||||||
|
if (changedProperties.has('opened')) {
|
||||||
|
if (this._overlayCtrl) {
|
||||||
|
this.__syncToOverlayController();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -106,44 +90,19 @@ export const OverlayMixin = dedupeMixin(
|
||||||
// eslint-disable-next-line class-methods-use-this
|
// eslint-disable-next-line class-methods-use-this
|
||||||
_teardownOpenCloseListeners() {}
|
_teardownOpenCloseListeners() {}
|
||||||
|
|
||||||
connectedCallback() {
|
firstUpdated(changedProperties) {
|
||||||
if (super.connectedCallback) {
|
super.firstUpdated(changedProperties);
|
||||||
super.connectedCallback();
|
// we setup in firstUpdated so we can use nodes from light and shadowDom
|
||||||
}
|
this._setupOverlayCtrl();
|
||||||
this._createOverlay();
|
|
||||||
|
|
||||||
// Default close event catcher on the contentNode which is useful if people want to close
|
|
||||||
// their overlay but the content is not in the global root node (nowhere near the overlay component)
|
|
||||||
this.__close = () => {
|
|
||||||
this.opened = false;
|
|
||||||
};
|
|
||||||
this._overlayCtrl.contentNode.addEventListener(this.closeEventName, this.__close);
|
|
||||||
|
|
||||||
this._setupOpenCloseListeners();
|
|
||||||
this.__syncOpened();
|
|
||||||
this.__syncPopper();
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated(c) {
|
|
||||||
super.firstUpdated(c);
|
|
||||||
this._createOutletForLocalOverlay();
|
|
||||||
}
|
|
||||||
|
|
||||||
updated(c) {
|
|
||||||
super.updated(c);
|
|
||||||
if (this.__managesOverlayViaTemplate) {
|
|
||||||
this._renderOverlayContent();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
if (super.disconnectedCallback) {
|
if (super.disconnectedCallback) {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
}
|
}
|
||||||
this.opened = false;
|
if (this._overlayCtrl) {
|
||||||
this._overlayCtrl.contentNode.removeEventListener(this.closeEventName, this.__close);
|
this._teardownOverlayCtrl();
|
||||||
this._teardownOpenCloseListeners();
|
}
|
||||||
this._overlayCtrl.teardown();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get _overlayInvokerNode() {
|
get _overlayInvokerNode() {
|
||||||
|
|
@ -171,69 +130,51 @@ export const OverlayMixin = dedupeMixin(
|
||||||
return contentNode || this._cachedOverlayContentNode;
|
return contentNode || this._cachedOverlayContentNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderOverlayContent() {
|
_setupOverlayCtrl() {
|
||||||
render(this._overlayTemplate(), this.__contentParent, {
|
this._overlayCtrl = this._defineOverlay({
|
||||||
scopeName: this.localName,
|
contentNode: this._overlayContentNode,
|
||||||
eventContext: this,
|
invokerNode: this._overlayInvokerNode,
|
||||||
});
|
});
|
||||||
|
this.__syncToOverlayController();
|
||||||
|
this.__setupSyncFromOverlayController();
|
||||||
|
|
||||||
|
this._setupOpenCloseListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
_createOverlay() {
|
_teardownOverlayCtrl() {
|
||||||
let contentNode;
|
this._teardownOpenCloseListeners();
|
||||||
if (this.__managesOverlayViaTemplate) {
|
this.__teardownSyncFromOverlayController();
|
||||||
this.__contentParent = document.createElement('div');
|
this._overlayCtrl.teardown();
|
||||||
this._renderOverlayContent();
|
|
||||||
contentNode = this.__contentParent.firstElementChild;
|
|
||||||
} else {
|
|
||||||
contentNode = this._overlayContentNode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Why no template support for invokerNode?
|
__setupSyncFromOverlayController() {
|
||||||
// -> Because this node will always be managed by the Subclasser and should
|
this.__onOverlayCtrlShow = () => {
|
||||||
// reside in the dom of the sub class. A reference to a rendered node suffices.
|
this.opened = true;
|
||||||
const invokerNode = this._overlayInvokerNode;
|
};
|
||||||
this._overlayCtrl = this._defineOverlay({ contentNode, invokerNode });
|
this.__onOverlayCtrlHide = () => {
|
||||||
|
this.opened = false;
|
||||||
|
};
|
||||||
|
this.__onBeforeShow = () => {
|
||||||
|
this.dispatchEvent(new Event('before-show'));
|
||||||
|
};
|
||||||
|
|
||||||
|
this._overlayCtrl.addEventListener('show', this.__onOverlayCtrlShow);
|
||||||
|
this._overlayCtrl.addEventListener('hide', this.__onOverlayCtrlHide);
|
||||||
|
this._overlayCtrl.addEventListener('before-show', this.__onBeforeShow);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: We add an overlay slot to the wrapper, but the content node already has a slot="content"
|
__teardownSyncFromOverlayController() {
|
||||||
// This is a big problem, because slots should be direct children of its host element.
|
this._overlayCtrl.removeEventListener('show', this.__onOverlayCtrlShow);
|
||||||
// Putting the shadow outlet slot in between breaks that. https://github.com/ing-bank/lion/issues/382
|
this._overlayCtrl.removeEventListener('hide', this.__onOverlayCtrlHide);
|
||||||
/**
|
this._overlayCtrl.removeEventListener('before-show', this.__onBeforeShow);
|
||||||
* @desc Should be called by Subclasser for local overlay support in shadow roots
|
|
||||||
* Create an outlet slot in shadow dom that our local overlay can pass through
|
|
||||||
*/
|
|
||||||
_createOutletForLocalOverlay() {
|
|
||||||
const outlet = document.createElement('slot');
|
|
||||||
outlet.name = '_overlay-shadow-outlet';
|
|
||||||
this.shadowRoot.appendChild(outlet);
|
|
||||||
this._overlayCtrl._contentNodeWrapper.slot = '_overlay-shadow-outlet';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
__syncToOverlayController() {
|
||||||
* @desc Two options for a Subclasser:
|
if (this.opened) {
|
||||||
* - 1: Define a template in `._overlayTemplate`. In this case the overlay content is
|
|
||||||
* predefined and thus belongs to the web component. Examples: datepicker.
|
|
||||||
* - 2: Define a getter `_overlayContentNode` that returns a node reference to a (content
|
|
||||||
* projected) node. Used when Application Developer is in charge of the content. Examples:
|
|
||||||
* popover, dialog, bottom sheet, dropdown, tooltip, select, combobox etc.
|
|
||||||
*/
|
|
||||||
get __managesOverlayViaTemplate() {
|
|
||||||
return Boolean(this._overlayTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
__syncOpened() {
|
|
||||||
if (this._opened) {
|
|
||||||
this._overlayCtrl.show();
|
this._overlayCtrl.show();
|
||||||
} else {
|
} else {
|
||||||
this._overlayCtrl.hide();
|
this._overlayCtrl.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__syncPopper() {
|
|
||||||
if (this._overlayCtrl) {
|
|
||||||
// TODO: Use updateConfig directly.. But maybe we can remove this entirely.
|
|
||||||
this._overlayCtrl.updatePopperConfig(this.config.popperConfig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
import { storiesOf, html, withKnobs } from '@open-wc/demoing-storybook';
|
import { storiesOf, html, withKnobs } from '@open-wc/demoing-storybook';
|
||||||
import { css, render, LitElement } from '@lion/core';
|
import { css, render, LitElement } from '@lion/core';
|
||||||
import '@lion/icon/lion-icon.js';
|
|
||||||
import '@lion/button/lion-button.js';
|
|
||||||
import {
|
import {
|
||||||
withBottomSheetConfig,
|
withBottomSheetConfig,
|
||||||
withDropdownConfig,
|
withDropdownConfig,
|
||||||
|
|
@ -70,7 +68,7 @@ const overlayDemoStyle = css`
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay {
|
.demo-overlay {
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
|
@ -80,7 +78,7 @@ const overlayDemoStyle = css`
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay lion-button {
|
.demo-overlay button {
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,11 +91,6 @@ const overlayDemoStyle = css`
|
||||||
customElements.define(
|
customElements.define(
|
||||||
'lion-demo-overlay',
|
'lion-demo-overlay',
|
||||||
class extends OverlayMixin(LitElement) {
|
class extends OverlayMixin(LitElement) {
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.closeEventName = 'demo-overlay-close';
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line class-methods-use-this
|
// eslint-disable-next-line class-methods-use-this
|
||||||
_defineOverlayConfig() {
|
_defineOverlayConfig() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -107,24 +100,20 @@ customElements.define(
|
||||||
|
|
||||||
_setupOpenCloseListeners() {
|
_setupOpenCloseListeners() {
|
||||||
this.__toggle = () => {
|
this.__toggle = () => {
|
||||||
console.log('toggle!');
|
|
||||||
this.opened = !this.opened;
|
this.opened = !this.opened;
|
||||||
};
|
};
|
||||||
|
this._overlayInvokerNode.addEventListener('click', this.__toggle);
|
||||||
console.log(this._overlayCtrl.invokerNode, this, this.__toggle);
|
|
||||||
this._overlayCtrl.invokerNode.addEventListener('click', this.__toggle);
|
|
||||||
this._overlayCtrl.invokerNode.addEventListener('click', () => console.log('ay'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_teardownOpenCloseListeners() {
|
_teardownOpenCloseListeners() {
|
||||||
console.log('teardown for', this);
|
this._overlayInvokerNode.removeEventListener('click', this.__toggle);
|
||||||
this._overlayCtrl.invokerNode.removeEventListener('click', this.__toggle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<slot name="invoker"></slot>
|
<slot name="invoker"></slot>
|
||||||
<slot name="content"></slot>
|
<slot name="content"></slot>
|
||||||
|
<slot name="_overlay-shadow-outlet"></slot>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -145,25 +134,25 @@ storiesOf('Overlay System | Overlay as a WC', module)
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
To close your overlay from some action performed inside the content slot, fire a
|
To close your overlay from some action performed inside the content slot, fire a
|
||||||
<code>close</code> event.
|
<code>hide</code> event.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
For the overlay to close, it will need to bubble to the content slot (use
|
For the overlay to close, it will need to bubble to the content slot (use
|
||||||
<code>bubbles: true</code>. Also <code>composed: true</code> if it needs to traverse shadow
|
<code>bubbles: true</code>. If absolutely needed <code>composed: true</code> can be used to
|
||||||
boundaries)
|
traverse shadow boundaries)
|
||||||
</p>
|
</p>
|
||||||
<p>The demo below demonstrates this</p>
|
<p>The demo below demonstrates this</p>
|
||||||
<div class="demo-box">
|
<div class="demo-box">
|
||||||
<lion-demo-overlay>
|
<lion-demo-overlay>
|
||||||
<lion-button slot="invoker">Overlay</lion-button>
|
<button slot="invoker">Overlay</button>
|
||||||
<div slot="content" class="overlay">
|
<div slot="content" class="demo-overlay">
|
||||||
Hello! You can close this notification here:
|
Hello! You can close this notification here:
|
||||||
<lion-button
|
<button
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click=${e =>
|
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
||||||
e.target.dispatchEvent(new Event('demo-overlay-close', { bubbles: true }))}
|
|
||||||
>⨯</lion-button
|
|
||||||
>
|
>
|
||||||
|
⨯
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</lion-demo-overlay>
|
</lion-demo-overlay>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -174,15 +163,15 @@ storiesOf('Overlay System | Overlay as a WC', module)
|
||||||
<lion-demo-overlay
|
<lion-demo-overlay
|
||||||
.config=${{ hasBackdrop: true, trapsKeyboardFocus: true, viewportConfig: { placement } }}
|
.config=${{ hasBackdrop: true, trapsKeyboardFocus: true, viewportConfig: { placement } }}
|
||||||
>
|
>
|
||||||
<lion-button slot="invoker">Overlay ${placement}</lion-button>
|
<button slot="invoker">Overlay ${placement}</button>
|
||||||
<div slot="content" class="overlay">
|
<div slot="content" class="demo-overlay">
|
||||||
Hello! You can close this notification here:
|
Hello! You can close this notification here:
|
||||||
<lion-button
|
<button
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click=${e =>
|
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
||||||
e.target.dispatchEvent(new Event('demo-overlay-close', { bubbles: true }))}
|
|
||||||
>⨯</lion-button
|
|
||||||
>
|
>
|
||||||
|
⨯
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</lion-demo-overlay>
|
</lion-demo-overlay>
|
||||||
`;
|
`;
|
||||||
|
|
@ -204,27 +193,25 @@ storiesOf('Overlay System | Overlay as a WC', module)
|
||||||
${overlayDemoStyle}
|
${overlayDemoStyle}
|
||||||
</style>
|
</style>
|
||||||
<lion-demo-overlay .config=${{ ...withModalDialogConfig() }}>
|
<lion-demo-overlay .config=${{ ...withModalDialogConfig() }}>
|
||||||
<lion-button slot="invoker">Overlay</lion-button>
|
<button slot="invoker">Overlay</button>
|
||||||
<div slot="content" class="overlay">
|
<div slot="content" class="demo-overlay">
|
||||||
<div>
|
<div>
|
||||||
Hello! This is a notification.
|
Hello! This is a notification.
|
||||||
<lion-button
|
<button @click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}>
|
||||||
@click=${e =>
|
Close
|
||||||
e.target.dispatchEvent(new Event('demo-overlay-close', { bubbles: true }))}
|
</button>
|
||||||
>Close</lion-button
|
|
||||||
>
|
|
||||||
<lion-demo-overlay
|
<lion-demo-overlay
|
||||||
.config=${{ ...withModalDialogConfig(), viewportConfig: { placement: 'top' } }}
|
.config=${{ ...withModalDialogConfig(), viewportConfig: { placement: 'top' } }}
|
||||||
>
|
>
|
||||||
<lion-button slot="invoker">Open child</lion-button>
|
<button slot="invoker">Open child</button>
|
||||||
<div slot="content" class="overlay">
|
<div slot="content" class="demo-overlay">
|
||||||
Hello! You can close this notification here:
|
Hello! You can close this notification here:
|
||||||
<lion-button
|
<button
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click=${e =>
|
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
||||||
e.target.dispatchEvent(new Event('demo-overlay-close', { bubbles: true }))}
|
|
||||||
>⨯</lion-button
|
|
||||||
>
|
>
|
||||||
|
⨯
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</lion-demo-overlay>
|
</lion-demo-overlay>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -242,15 +229,15 @@ storiesOf('Overlay System | Overlay as a WC', module)
|
||||||
<lion-demo-overlay
|
<lion-demo-overlay
|
||||||
.config=${{ placementMode: 'local', popperConfig: { placement: 'bottom-start' } }}
|
.config=${{ placementMode: 'local', popperConfig: { placement: 'bottom-start' } }}
|
||||||
>
|
>
|
||||||
<lion-button slot="invoker">Overlay</lion-button>
|
<button slot="invoker">Overlay</button>
|
||||||
<div slot="content" class="overlay">
|
<div slot="content" class="demo-overlay">
|
||||||
Hello! You can close this notification here:
|
Hello! You can close this notification here:
|
||||||
<lion-button
|
<button
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click=${e =>
|
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
||||||
e.target.dispatchEvent(new Event('demo-overlay-close', { bubbles: true }))}
|
|
||||||
>⨯</lion-button
|
|
||||||
>
|
>
|
||||||
|
⨯
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</lion-demo-overlay>
|
</lion-demo-overlay>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -307,15 +294,15 @@ storiesOf('Overlay System | Overlay as a WC', module)
|
||||||
.add('Switch overlays configuration', () => {
|
.add('Switch overlays configuration', () => {
|
||||||
const overlay = renderOffline(html`
|
const overlay = renderOffline(html`
|
||||||
<lion-demo-overlay .config=${{ ...withBottomSheetConfig() }}>
|
<lion-demo-overlay .config=${{ ...withBottomSheetConfig() }}>
|
||||||
<lion-button slot="invoker">Overlay</lion-button>
|
<button slot="invoker">Overlay</button>
|
||||||
<div slot="content" class="overlay">
|
<div slot="content" class="demo-overlay">
|
||||||
Hello! You can close this notification here:
|
Hello! You can close this notification here:
|
||||||
<lion-button
|
<button
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click=${e =>
|
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
||||||
e.target.dispatchEvent(new Event('demo-overlay-close', { bubbles: true }))}
|
|
||||||
>⨯</lion-button
|
|
||||||
>
|
>
|
||||||
|
⨯
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</lion-demo-overlay>
|
</lion-demo-overlay>
|
||||||
`);
|
`);
|
||||||
|
|
@ -376,15 +363,15 @@ storiesOf('Overlay System | Overlay as a WC', module)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<lion-button slot="invoker">Overlay</lion-button>
|
<button slot="invoker">Overlay</button>
|
||||||
<div slot="content" class="overlay">
|
<div slot="content" class="demo-overlay">
|
||||||
Hello! You can close this notification here:
|
Hello! You can close this notification here:
|
||||||
<lion-button
|
<button
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click=${e =>
|
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
||||||
e.target.dispatchEvent(new Event('demo-overlay-close', { bubbles: true }))}
|
|
||||||
>⨯</lion-button
|
|
||||||
>
|
>
|
||||||
|
⨯
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</lion-demo-overlay>
|
</lion-demo-overlay>
|
||||||
`,
|
`,
|
||||||
|
|
@ -411,7 +398,7 @@ storiesOf('Overlay System | Overlay as a WC', module)
|
||||||
}}
|
}}
|
||||||
>UK</span
|
>UK</span
|
||||||
>
|
>
|
||||||
<div slot="content" class="overlay">
|
<div slot="content" class="demo-overlay">
|
||||||
United Kingdom
|
United Kingdom
|
||||||
</div>
|
</div>
|
||||||
</lion-demo-overlay>
|
</lion-demo-overlay>
|
||||||
|
|
@ -475,13 +462,13 @@ storiesOf('Overlay System | Overlay as a WC', module)
|
||||||
: { popperConfig: { placement: text('local config', 'top-start') } }),
|
: { popperConfig: { placement: text('local config', 'top-start') } }),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<lion-button slot="invoker">Overlay</lion-button>
|
<button slot="invoker">Overlay</button>
|
||||||
<div slot="content" class="overlay">
|
<div slot="content" class="demo-overlay">
|
||||||
Hello! You can close this notification here:
|
Hello! You can close this notification here:
|
||||||
<lion-button
|
<button
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click=${e => e.target.dispatchEvent(new Event('demo-overlay-close', { bubbles: true }))}
|
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
||||||
>⨯</lion-button
|
>⨯</button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</lion-demo-overlay>
|
</lion-demo-overlay>
|
||||||
|
|
|
||||||
|
|
@ -233,7 +233,7 @@ storiesOf('Overlay System | Behavior Features', module)
|
||||||
${this.options[(this.options.indexOf(this.placement) + 1) % this.options.length]}
|
${this.options[(this.options.indexOf(this.placement) + 1) % this.options.length]}
|
||||||
position
|
position
|
||||||
</button>
|
</button>
|
||||||
<button @click="${() => this.dispatchEvent(new CustomEvent('close'))}">Close</button>
|
<button @click="${() => this.dispatchEvent(new Event('hide'))}">Close</button>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -265,9 +265,6 @@ storiesOf('Overlay System | Behavior Features', module)
|
||||||
element.addEventListener('toggle-placement', e => {
|
element.addEventListener('toggle-placement', e => {
|
||||||
overlayCtrl.updateConfig({ viewportConfig: { placement: e.detail } });
|
overlayCtrl.updateConfig({ viewportConfig: { placement: e.detail } });
|
||||||
});
|
});
|
||||||
element.addEventListener('close', () => {
|
|
||||||
overlayCtrl.hide();
|
|
||||||
});
|
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
${globalOverlayDemoStyle}
|
${globalOverlayDemoStyle}
|
||||||
|
|
|
||||||
57
packages/overlays/test-suites/OverlayMixin.suite.js
Normal file
57
packages/overlays/test-suites/OverlayMixin.suite.js
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { expect, fixture, html, aTimeout } from '@open-wc/testing';
|
||||||
|
|
||||||
|
export function runOverlayMixinSuite({ /* tagString, */ tag, suffix = '' }) {
|
||||||
|
describe(`OverlayMixin${suffix}`, () => {
|
||||||
|
let el;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
el = await fixture(html`
|
||||||
|
<${tag}>
|
||||||
|
<div slot="content">content of the overlay</div>
|
||||||
|
<button slot="invoker">invoker button</button>
|
||||||
|
</${tag}>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not be opened by default', async () => {
|
||||||
|
expect(el.opened).to.be.false;
|
||||||
|
expect(el._overlayCtrl.isShown).to.be.false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('syncs opened to overlayController', async () => {
|
||||||
|
el.opened = true;
|
||||||
|
expect(el.opened).to.be.true;
|
||||||
|
await aTimeout(); // overlayCtrl show/hide is async
|
||||||
|
expect(el._overlayCtrl.isShown).to.be.true;
|
||||||
|
|
||||||
|
el.opened = false;
|
||||||
|
expect(el.opened).to.be.false;
|
||||||
|
await aTimeout(0); // overlayCtrl show/hide is async
|
||||||
|
expect(el._overlayCtrl.isShown).to.be.false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('syncs overlayController to opened', async () => {
|
||||||
|
expect(el.opened).to.be.false;
|
||||||
|
await el._overlayCtrl.show();
|
||||||
|
expect(el.opened).to.be.true;
|
||||||
|
|
||||||
|
await el._overlayCtrl.hide();
|
||||||
|
expect(el.opened).to.be.false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should respond to initially and dynamically setting the config', async () => {
|
||||||
|
const itEl = await fixture(html`
|
||||||
|
<${tag} .config=${{ trapsKeyboardFocus: false, viewportConfig: { placement: 'top' } }}>
|
||||||
|
<div slot="content">content of the overlay</div>
|
||||||
|
<button slot="invoker">invoker button</button>
|
||||||
|
</${tag}>
|
||||||
|
`);
|
||||||
|
itEl.opened = true;
|
||||||
|
await itEl.updateComplete;
|
||||||
|
expect(itEl._overlayCtrl.trapsKeyboardFocus).to.be.false;
|
||||||
|
|
||||||
|
itEl.config = { viewportConfig: { placement: 'left' } };
|
||||||
|
expect(itEl._overlayCtrl.viewportConfig.placement).to.equal('left');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -314,6 +314,33 @@ describe('OverlayController', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('hidesOnHideEventInContentNode', () => {
|
||||||
|
it('hides content on hide event within the content ', async () => {
|
||||||
|
const ctrl = new OverlayController({
|
||||||
|
...withGlobalTestConfig(),
|
||||||
|
hidesOnHideEventInContentNode: true,
|
||||||
|
contentNode: fixtureSync(html`
|
||||||
|
<div>
|
||||||
|
my content
|
||||||
|
<button @click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}>
|
||||||
|
x
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`),
|
||||||
|
});
|
||||||
|
await ctrl.show();
|
||||||
|
|
||||||
|
const closeBtn = ctrl.contentNode.querySelector('button');
|
||||||
|
closeBtn.click();
|
||||||
|
|
||||||
|
expect(ctrl.isShown).to.be.false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does stop propagation of the "hide" event to not pollute the event stack and to prevent side effects', () => {
|
||||||
|
// TODO: how to test this?
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('hidesOnOutsideClick', () => {
|
describe('hidesOnOutsideClick', () => {
|
||||||
it('hides on outside click', async () => {
|
it('hides on outside click', async () => {
|
||||||
const contentNode = await fixture('<div>Content</div>');
|
const contentNode = await fixture('<div>Content</div>');
|
||||||
|
|
|
||||||
12
packages/overlays/test/OverlayMixin.test.js
Normal file
12
packages/overlays/test/OverlayMixin.test.js
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { defineCE, unsafeStatic } from '@open-wc/testing';
|
||||||
|
import { LitElement } from '@lion/core';
|
||||||
|
import { runOverlayMixinSuite } from '../test-suites/OverlayMixin.suite.js';
|
||||||
|
import { OverlayMixin } from '../src/OverlayMixin.js';
|
||||||
|
|
||||||
|
const tagString = defineCE(class extends OverlayMixin(LitElement) {});
|
||||||
|
const tag = unsafeStatic(tagString);
|
||||||
|
|
||||||
|
runOverlayMixinSuite({
|
||||||
|
tagString,
|
||||||
|
tag,
|
||||||
|
});
|
||||||
|
|
@ -182,12 +182,6 @@ export class LionSelectRich extends OverlayMixin(
|
||||||
if (super.connectedCallback) {
|
if (super.connectedCallback) {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.__setupOverlay();
|
|
||||||
this.__setupInvokerNode();
|
|
||||||
this.__setupListboxNode();
|
|
||||||
|
|
||||||
this._invokerNode.selectedElement = this.formElements[this.checkedIndex];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
|
|
@ -202,6 +196,12 @@ export class LionSelectRich extends OverlayMixin(
|
||||||
|
|
||||||
firstUpdated(c) {
|
firstUpdated(c) {
|
||||||
super.firstUpdated(c);
|
super.firstUpdated(c);
|
||||||
|
this.__setupOverlay();
|
||||||
|
this.__setupInvokerNode();
|
||||||
|
this.__setupListboxNode();
|
||||||
|
|
||||||
|
this._invokerNode.selectedElement = this.formElements[this.checkedIndex];
|
||||||
|
|
||||||
this.__toggleInvokerDisabled();
|
this.__toggleInvokerDisabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -243,6 +243,14 @@ export class LionSelectRich extends OverlayMixin(
|
||||||
return this.querySelector('[slot="input"]');
|
return this.querySelector('[slot="input"]');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
${this.labelTemplate()} ${this.helpTextTemplate()} ${this.inputGroupTemplate()}
|
||||||
|
${this.feedbackTemplate()}
|
||||||
|
<slot name="_overlay-shadow-outlet"></slot>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
updated(changedProps) {
|
updated(changedProps) {
|
||||||
super.updated(changedProps);
|
super.updated(changedProps);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -247,7 +247,7 @@ describe('lion-select-rich', () => {
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`);
|
`);
|
||||||
el._invokerNode.click();
|
el._invokerNode.click();
|
||||||
await el.updateComplete;
|
await aTimeout();
|
||||||
expect(el.opened).to.be.true;
|
expect(el.opened).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -258,7 +258,7 @@ describe('lion-select-rich', () => {
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`);
|
`);
|
||||||
el._invokerNode.click();
|
el._invokerNode.click();
|
||||||
await el.updateComplete;
|
await aTimeout();
|
||||||
expect(el.opened).to.be.true;
|
expect(el.opened).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,6 @@
|
||||||
"@lion/overlays": "^0.6.4"
|
"@lion/overlays": "^0.6.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lion/button": "^0.3.43",
|
|
||||||
"@lion/icon": "^0.2.9",
|
|
||||||
"@open-wc/demoing-storybook": "^0.2.0",
|
"@open-wc/demoing-storybook": "^0.2.0",
|
||||||
"@open-wc/testing": "^2.3.4"
|
"@open-wc/testing": "^2.3.4"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ export class LionTooltip extends OverlayMixin(LitElement) {
|
||||||
return html`
|
return html`
|
||||||
<slot name="invoker"></slot>
|
<slot name="invoker"></slot>
|
||||||
<slot name="content"></slot>
|
<slot name="content"></slot>
|
||||||
|
<slot name="_overlay-shadow-outlet"></slot>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
import { storiesOf, html, withKnobs, object, text } from '@open-wc/demoing-storybook';
|
import { storiesOf, html, withKnobs, object, text } from '@open-wc/demoing-storybook';
|
||||||
import { css } from '@lion/core';
|
import { css } from '@lion/core';
|
||||||
|
|
||||||
import '@lion/icon/lion-icon.js';
|
|
||||||
import '@lion/button/lion-button.js';
|
|
||||||
import '../lion-tooltip.js';
|
import '../lion-tooltip.js';
|
||||||
|
|
||||||
const tooltipDemoStyle = css`
|
const tooltipDemoStyle = css`
|
||||||
|
|
@ -53,7 +51,7 @@ storiesOf('Overlays Specific WC|Tooltip', module)
|
||||||
</style>
|
</style>
|
||||||
<div class="demo-box">
|
<div class="demo-box">
|
||||||
<lion-tooltip .config=${{ popperConfig: { placement: 'right' } }}>
|
<lion-tooltip .config=${{ popperConfig: { placement: 'right' } }}>
|
||||||
<lion-button slot="invoker">Tooltip</lion-button>
|
<button slot="invoker">Tooltip</button>
|
||||||
<div slot="content" class="demo-tooltip">Hello there!</div>
|
<div slot="content" class="demo-tooltip">Hello there!</div>
|
||||||
</lion-tooltip>
|
</lion-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -67,19 +65,19 @@ storiesOf('Overlays Specific WC|Tooltip', module)
|
||||||
</style>
|
</style>
|
||||||
<div class="demo-box_placements">
|
<div class="demo-box_placements">
|
||||||
<lion-tooltip .config=${{ popperConfig: { placement: 'top' } }}>
|
<lion-tooltip .config=${{ popperConfig: { placement: 'top' } }}>
|
||||||
<lion-button slot="invoker">Top</lion-button>
|
<button slot="invoker">Top</button>
|
||||||
<div slot="content" class="demo-tooltip">Its top placement</div>
|
<div slot="content" class="demo-tooltip">Its top placement</div>
|
||||||
</lion-tooltip>
|
</lion-tooltip>
|
||||||
<lion-tooltip .config=${{ popperConfig: { placement: 'right' } }}>
|
<lion-tooltip .config=${{ popperConfig: { placement: 'right' } }}>
|
||||||
<lion-button slot="invoker">Right</lion-button>
|
<button slot="invoker">Right</button>
|
||||||
<div slot="content" class="demo-tooltip">Its right placement</div>
|
<div slot="content" class="demo-tooltip">Its right placement</div>
|
||||||
</lion-tooltip>
|
</lion-tooltip>
|
||||||
<lion-tooltip .config=${{ popperConfig: { placement: 'bottom' } }}>
|
<lion-tooltip .config=${{ popperConfig: { placement: 'bottom' } }}>
|
||||||
<lion-button slot="invoker">Bottom</lion-button>
|
<button slot="invoker">Bottom</button>
|
||||||
<div slot="content" class="demo-tooltip">Its bottom placement</div>
|
<div slot="content" class="demo-tooltip">Its bottom placement</div>
|
||||||
</lion-tooltip>
|
</lion-tooltip>
|
||||||
<lion-tooltip .config=${{ popperConfig: { placement: 'left' } }}>
|
<lion-tooltip .config=${{ popperConfig: { placement: 'left' } }}>
|
||||||
<lion-button slot="invoker">Left</lion-button>
|
<button slot="invoker">Left</button>
|
||||||
<div slot="content" class="demo-tooltip">Its left placement</div>
|
<div slot="content" class="demo-tooltip">Its left placement</div>
|
||||||
</lion-tooltip>
|
</lion-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -119,7 +117,7 @@ storiesOf('Overlays Specific WC|Tooltip', module)
|
||||||
}),
|
}),
|
||||||
}}"
|
}}"
|
||||||
>
|
>
|
||||||
<lion-button slot="invoker">${text('Invoker text', 'Hover me!')}</lion-button>
|
<button slot="invoker">${text('Invoker text', 'Hover me!')}</button>
|
||||||
<div slot="content" class="demo-tooltip">${text('Content text', 'Hello, World!')}</div>
|
<div slot="content" class="demo-tooltip">${text('Content text', 'Hello, World!')}</div>
|
||||||
</lion-tooltip>
|
</lion-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,26 @@
|
||||||
import { expect, fixture, html } from '@open-wc/testing';
|
import { expect, fixture, html, unsafeStatic } from '@open-wc/testing';
|
||||||
|
import { runOverlayMixinSuite } from '@lion/overlays/test-suites/OverlayMixin.suite.js';
|
||||||
|
|
||||||
import '../lion-tooltip.js';
|
import '../lion-tooltip.js';
|
||||||
|
|
||||||
describe('lion-tooltip', () => {
|
describe('lion-tooltip', () => {
|
||||||
describe('Basic', () => {
|
describe('Integration tests', () => {
|
||||||
it('should not be shown by default', async () => {
|
const tagString = 'lion-tooltip';
|
||||||
const el = await fixture(html`
|
const tag = unsafeStatic(tagString);
|
||||||
<lion-tooltip>
|
|
||||||
<div slot="content">Hey there</div>
|
runOverlayMixinSuite({
|
||||||
<lion-button slot="invoker">Tooltip button</lion-button>
|
tagString,
|
||||||
</lion-tooltip>
|
tag,
|
||||||
`);
|
suffix: ' for lion-tooltip',
|
||||||
expect(el._overlayCtrl.isShown).to.equal(false);
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Basic', () => {
|
||||||
it('should show content on mouseenter and hide on mouseleave', async () => {
|
it('should show content on mouseenter and hide on mouseleave', async () => {
|
||||||
const el = await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-tooltip>
|
<lion-tooltip>
|
||||||
<div slot="content">Hey there</div>
|
<div slot="content">Hey there</div>
|
||||||
<lion-button slot="invoker">Tooltip button</lion-button>
|
<button slot="invoker">Tooltip button</button>
|
||||||
</lion-tooltip>
|
</lion-tooltip>
|
||||||
`);
|
`);
|
||||||
const eventMouseEnter = new Event('mouseenter');
|
const eventMouseEnter = new Event('mouseenter');
|
||||||
|
|
@ -35,7 +37,7 @@ describe('lion-tooltip', () => {
|
||||||
const el = await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-tooltip>
|
<lion-tooltip>
|
||||||
<div slot="content">Hey there</div>
|
<div slot="content">Hey there</div>
|
||||||
<lion-button slot="invoker">Tooltip button</lion-button>
|
<button slot="invoker">Tooltip button</button>
|
||||||
</lion-tooltip>
|
</lion-tooltip>
|
||||||
`);
|
`);
|
||||||
const eventMouseEnter = new Event('mouseenter');
|
const eventMouseEnter = new Event('mouseenter');
|
||||||
|
|
@ -52,7 +54,7 @@ describe('lion-tooltip', () => {
|
||||||
const el = await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-tooltip>
|
<lion-tooltip>
|
||||||
<div slot="content">Hey there</div>
|
<div slot="content">Hey there</div>
|
||||||
<lion-button slot="invoker">Tooltip button</lion-button>
|
<button slot="invoker">Tooltip button</button>
|
||||||
</lion-tooltip>
|
</lion-tooltip>
|
||||||
`);
|
`);
|
||||||
const invoker = Array.from(el.children).find(child => child.slot === 'invoker');
|
const invoker = Array.from(el.children).find(child => child.slot === 'invoker');
|
||||||
|
|
@ -70,7 +72,7 @@ describe('lion-tooltip', () => {
|
||||||
const el = await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-tooltip>
|
<lion-tooltip>
|
||||||
<div slot="content">Hey there</div>
|
<div slot="content">Hey there</div>
|
||||||
<lion-button slot="invoker">Tooltip button</lion-button>
|
<button slot="invoker">Tooltip button</button>
|
||||||
</lion-tooltip>
|
</lion-tooltip>
|
||||||
`);
|
`);
|
||||||
const invoker = Array.from(el.children).find(child => child.slot === 'invoker');
|
const invoker = Array.from(el.children).find(child => child.slot === 'invoker');
|
||||||
|
|
@ -90,7 +92,7 @@ describe('lion-tooltip', () => {
|
||||||
<div slot="content">
|
<div slot="content">
|
||||||
This is Tooltip using <strong id="click_overlay">overlay</strong>
|
This is Tooltip using <strong id="click_overlay">overlay</strong>
|
||||||
</div>
|
</div>
|
||||||
<lion-button slot="invoker">Tooltip button</lion-button>
|
<button slot="invoker">Tooltip button</button>
|
||||||
</lion-tooltip>
|
</lion-tooltip>
|
||||||
`);
|
`);
|
||||||
const invoker = Array.from(el.children).find(child => child.slot === 'invoker');
|
const invoker = Array.from(el.children).find(child => child.slot === 'invoker');
|
||||||
|
|
@ -106,7 +108,7 @@ describe('lion-tooltip', () => {
|
||||||
const el = await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-tooltip>
|
<lion-tooltip>
|
||||||
<div slot="content">Hey there</div>
|
<div slot="content">Hey there</div>
|
||||||
<lion-button slot="invoker">Tooltip button</lion-button>
|
<button slot="invoker">Tooltip button</button>
|
||||||
</lion-tooltip>
|
</lion-tooltip>
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2088,7 +2088,7 @@
|
||||||
wallaby-webpack "^3.0.0"
|
wallaby-webpack "^3.0.0"
|
||||||
webpack "^4.28.0"
|
webpack "^4.28.0"
|
||||||
|
|
||||||
"@open-wc/testing@^2.3.4", "@open-wc/testing@^2.3.9":
|
"@open-wc/testing@^2.3.4":
|
||||||
version "2.3.9"
|
version "2.3.9"
|
||||||
resolved "https://registry.yarnpkg.com/@open-wc/testing/-/testing-2.3.9.tgz#048bb3122d989cf0df96611513aaec7738964e3d"
|
resolved "https://registry.yarnpkg.com/@open-wc/testing/-/testing-2.3.9.tgz#048bb3122d989cf0df96611513aaec7738964e3d"
|
||||||
integrity sha512-5pKtHNP/73y9VWAwXOdxf4uzKVAtCowSdy4B6It4iETq8RshkAtKJbJBj+iQSU81pG6jOgSNPlGYeU01/CXaxw==
|
integrity sha512-5pKtHNP/73y9VWAwXOdxf4uzKVAtCowSdy4B6It4iETq8RshkAtKJbJBj+iQSU81pG6jOgSNPlGYeU01/CXaxw==
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue