diff --git a/packages/dialog/src/LionDialog.js b/packages/dialog/src/LionDialog.js index 390a20459..a35d920a4 100644 --- a/packages/dialog/src/LionDialog.js +++ b/packages/dialog/src/LionDialog.js @@ -1,6 +1,7 @@ -import { LionOverlay, OverlayController, withModalDialogConfig } from '@lion/overlays'; +import { OverlayController, withModalDialogConfig, OverlayMixin } from '@lion/overlays'; +import { LitElement, html } from '@lion/core'; -export class LionDialog extends LionOverlay { +export class LionDialog extends OverlayMixin(LitElement) { // eslint-disable-next-line class-methods-use-this _defineOverlay({ contentNode, invokerNode }) { return new OverlayController({ @@ -11,4 +12,27 @@ export class LionDialog extends LionOverlay { ...this.config, // lit-property set by user for overrides }); } + + _setupOpenCloseListeners() { + this.__close = () => { + this.opened = false; + }; + this.__toggle = () => { + this.opened = !this.opened; + }; + this._overlayCtrl.invokerNode.addEventListener('click', this.__toggle); + this._overlayCtrl.contentNode.addEventListener('close', this.__close); + } + + _teardownOpenCloseListeners() { + this._overlayCtrl.invokerNode.removeEventListener('click', this.__toggle); + this._overlayCtrl.contentNode.removeEventListener('close', this.__close); + } + + render() { + return html` + + + `; + } } diff --git a/packages/input-datepicker/src/LionInputDatepicker.js b/packages/input-datepicker/src/LionInputDatepicker.js index aa62fea9b..9b5c04d2e 100644 --- a/packages/input-datepicker/src/LionInputDatepicker.js +++ b/packages/input-datepicker/src/LionInputDatepicker.js @@ -264,8 +264,6 @@ export class LionInputDatepicker extends OverlayMixin(LionInputDate) { ...withModalDialogConfig(), contentNode, invokerNode, - elementToFocusAfterHide: invokerNode, - hidesOnOutsideClick: true, }); return ctrl; } diff --git a/packages/overlays/index.js b/packages/overlays/index.js index 04d690a13..7719aab23 100644 --- a/packages/overlays/index.js +++ b/packages/overlays/index.js @@ -7,5 +7,3 @@ export { OverlayMixin } from './src/OverlayMixin.js'; export { withBottomSheetConfig } from './src/configurations/withBottomSheetConfig.js'; export { withModalDialogConfig } from './src/configurations/withModalDialogConfig.js'; export { withDropdownConfig } from './src/configurations/withDropdownConfig.js'; - -export { LionOverlay } from './src/LionOverlay.js'; diff --git a/packages/overlays/lion-overlay.js b/packages/overlays/lion-overlay.js deleted file mode 100644 index 45327afaa..000000000 --- a/packages/overlays/lion-overlay.js +++ /dev/null @@ -1,3 +0,0 @@ -import { LionOverlay } from './src/LionOverlay.js'; - -customElements.define('lion-overlay', LionOverlay); diff --git a/packages/overlays/src/LionOverlay.js b/packages/overlays/src/LionOverlay.js deleted file mode 100644 index 7eb3cb294..000000000 --- a/packages/overlays/src/LionOverlay.js +++ /dev/null @@ -1,95 +0,0 @@ -import { LitElement, html } from '@lion/core'; -import { OverlayMixin } from './OverlayMixin.js'; -import { OverlayController } from './OverlayController.js'; - -export class LionOverlay extends OverlayMixin(LitElement) { - static get properties() { - return { - config: { - type: Object, - }, - }; - } - - constructor() { - super(); - this.config = {}; - } - - get config() { - return this._config; - } - - set config(value) { - if (this._overlayCtrl) { - this._overlayCtrl.updateConfig(value); - } - this._config = value; - } - - render() { - return html` - - - `; - } - - // FIXME: This should be refactored to Array.from(this.children).find(child => child.slot === 'content') - // When this issue is fixed https://github.com/ing-bank/lion/issues/382 - /** - * @override - * Overrides OverlayMixin - * Important to use this override, so that later, contentTemplates can also be accepted - */ - get _overlayContentNode() { - const contentNode = this.querySelector('[slot=content]'); - if (contentNode) { - this._cachedOverlayContentNode = contentNode; - } - return contentNode || this._cachedOverlayContentNode; - } - - /** - * @override - * Overrides OverlayMixin - */ - get _overlayInvokerNode() { - return Array.from(this.children).find(child => child.slot === 'invoker'); - } - - // eslint-disable-next-line class-methods-use-this - _defineOverlay({ contentNode, invokerNode }) { - return new OverlayController({ - placementMode: 'global', // have to set a default - contentNode, - invokerNode, - ...this.config, - }); - } - - _setupShowHideListeners() { - this.__close = () => { - this.opened = false; - }; - this.__toggle = () => { - this.opened = !this.opened; - }; - this._overlayCtrl.invokerNode.addEventListener('click', this.__toggle); - this._overlayCtrl.contentNode.addEventListener('close', this.__close); - } - - _teardownShowHideListeners() { - this._overlayCtrl.invokerNode.removeEventListener('click', this.__toggle); - this._overlayCtrl.contentNode.removeEventListener('close', this.__close); - } - - connectedCallback() { - super.connectedCallback(); - this._setupShowHideListeners(); - } - - disconnectedCallback() { - super.disconnectedCallback(); - this._teardownShowHideListeners(); - } -} diff --git a/packages/overlays/src/OverlayController.js b/packages/overlays/src/OverlayController.js index 2d88e154d..2865fcbe5 100644 --- a/packages/overlays/src/OverlayController.js +++ b/packages/overlays/src/OverlayController.js @@ -103,18 +103,6 @@ export class OverlayController { * @param {OverlayConfig} cfgToAdd */ updateConfig(cfgToAdd) { - // only updating the viewportConfig - if (Object.keys(cfgToAdd).length === 1 && Object.keys(cfgToAdd)[0] === 'viewportConfig') { - this.updateViewportConfig(cfgToAdd.viewportConfig); - return; - } - - // only updating the popperConfig - if (Object.keys(cfgToAdd).length === 1 && Object.keys(cfgToAdd)[0] === 'popperConfig') { - this.updatePopperConfig(cfgToAdd.popperConfig); - return; - } - // Teardown all previous configs this._handleFeatures({ phase: 'teardown' }); @@ -164,7 +152,7 @@ export class OverlayController { // TODO: Instead, prefetch it or use a preloader-manager to load it during idle time this.constructor.popperModule = preloadPopper(); } - this.__mergePopperConfigs(this.popperConfig || {}); + this.__mergePopperConfigs(this.config.popperConfig || {}); } this._handleFeatures({ phase: 'init' }); } @@ -312,7 +300,6 @@ export class OverlayController { // Otherwise we assume the 'outside world' has, purposefully, taken over // if (this._contentNodeWrapper.activeElement) { if (this.elementToFocusAfterHide) { - console.log(this.elementToFocusAfterHide); this.elementToFocusAfterHide.focus(); } // } @@ -556,8 +543,7 @@ export class OverlayController { } } - // Popper does not export a nice method to update an existing instance with a new config. Therefore we recreate the instance. - // TODO: Send a merge request to Popper to abstract their logic in the constructor to an exposed method which takes in the user config. + // TODO: Remove when no longer required by OverlayMixin (after updateConfig works properly while opened) async updatePopperConfig(config = {}) { this.__mergePopperConfigs(config); if (this.isShown) { @@ -566,12 +552,6 @@ export class OverlayController { } } - updateViewportConfig(newConfig) { - this._handlePosition({ phase: 'hide' }); - this.viewportConfig = newConfig; - this._handlePosition({ phase: 'show' }); - } - teardown() { this._handleFeatures({ phase: 'teardown' }); } @@ -607,14 +587,19 @@ export class OverlayController { }, }; - // Deep merging default config, previously configured user config, new user config - this.popperConfig = { + /** + * Deep merging: + * - default config + * - previously configured user config + * - new user added config + */ + this.config.popperConfig = { ...defaultConfig, - ...(this.popperConfig || {}), + ...(this.config.popperConfig || {}), ...(config || {}), modifiers: { ...defaultConfig.modifiers, - ...((this.popperConfig && this.popperConfig.modifiers) || {}), + ...((this.config.popperConfig && this.config.popperConfig.modifiers) || {}), ...((config && config.modifiers) || {}), }, }; @@ -627,7 +612,7 @@ export class OverlayController { } const { default: Popper } = await this.constructor.popperModule; this._popper = new Popper(this._referenceNode, this._contentNodeWrapper, { - ...this.popperConfig, + ...this.config.popperConfig, }); } diff --git a/packages/overlays/src/OverlayMixin.js b/packages/overlays/src/OverlayMixin.js index 25af76523..749a2184f 100644 --- a/packages/overlays/src/OverlayMixin.js +++ b/packages/overlays/src/OverlayMixin.js @@ -15,10 +15,17 @@ export const OverlayMixin = dedupeMixin( type: Boolean, reflect: true, }, - popperConfig: Object, + config: { + type: Object, + }, }; } + constructor() { + super(); + this.config = {}; + } + get opened() { return this._overlayCtrl.isShown; } @@ -30,44 +37,63 @@ export const OverlayMixin = dedupeMixin( } } - __syncOpened() { - if (this._opened) { - this._overlayCtrl.show(); - } else { - this._overlayCtrl.hide(); - } + get config() { + return this._config; } - get popperConfig() { - return this._popperConfig; - } - - set popperConfig(config) { - this._popperConfig = { - ...this._popperConfig, - ...config, - }; - this.__syncPopper(); - } - - __syncPopper() { + set config(value) { if (this._overlayCtrl) { - this._overlayCtrl.updatePopperConfig(this._popperConfig); + this._overlayCtrl.updateConfig(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` + * @desc returns an instance of a (dynamic) overlay controller + * @returns {OverlayController} + */ + // eslint-disable-next-line + _defineOverlay({ contentNode, invokerNode }) {} + + /** + * @overridable + * @desc use this method to setup your open and close event listeners + * For example, set a click event listener on _overlayInvokerNode to set opened to true + */ + // eslint-disable-next-line class-methods-use-this + _setupOpenCloseListeners() {} + + /** + * @overridable + * @desc use this method to tear down your event listeners + */ + // eslint-disable-next-line class-methods-use-this + _teardownOpenCloseListeners() {} + connectedCallback() { if (super.connectedCallback) { super.connectedCallback(); } this._createOverlay(); + this._setupOpenCloseListeners(); this.__syncOpened(); this.__syncPopper(); } firstUpdated(c) { super.firstUpdated(c); - this._createOutletForLocalOverlay(); + if (this._overlayCtrl.config.placementMode === 'local') { + this._createOutletForLocalOverlay(); + } } updated(c) { @@ -77,6 +103,27 @@ export const OverlayMixin = dedupeMixin( } } + disconnectedCallback() { + if (super.disconnectedCallback) { + super.disconnectedCallback(); + } + this._teardownOpenCloseListeners(); + } + + get _overlayInvokerNode() { + return Array.from(this.children).find(child => child.slot === 'invoker'); + } + + // FIXME: This should be refactored to Array.from(this.children).find(child => child.slot === 'content') + // When this issue is fixed https://github.com/ing-bank/lion/issues/382 + get _overlayContentNode() { + const contentNode = this.querySelector('[slot=content]'); + if (contentNode) { + this._cachedOverlayContentNode = contentNode; + } + return contentNode || this._cachedOverlayContentNode; + } + _renderOverlayContent() { render(this._overlayTemplate(), this.__contentParent, { scopeName: this.localName, @@ -84,18 +131,6 @@ export const OverlayMixin = dedupeMixin( }); } - /** - * @desc Two options for a Subclasser: - * - 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); - } - _createOverlay() { let contentNode; if (this.__managesOverlayViaTemplate) { @@ -128,19 +163,30 @@ export const OverlayMixin = dedupeMixin( } /** - * @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} + * @desc Two options for a Subclasser: + * - 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); + } - /** - * @overridable method `_defineOverlay` - * @desc returns an instance of a (dynamic) overlay controller - * @returns {OverlayController} - */ - // eslint-disable-next-line - _defineOverlay({ contentNode, invokerNode }) {} + __syncOpened() { + if (this._opened) { + this._overlayCtrl.show(); + } else { + this._overlayCtrl.hide(); + } + } + + __syncPopper() { + if (this._overlayCtrl) { + // TODO: Use updateConfig directly.. but first check if this sync is even still needed! Maybe we can remove it. + this._overlayCtrl.updatePopperConfig(this.config.popperConfig); + } + } }, ); diff --git a/packages/overlays/stories/index.stories.js b/packages/overlays/stories/index.stories.js index 9404080b6..32ffdacf7 100644 --- a/packages/overlays/stories/index.stories.js +++ b/packages/overlays/stories/index.stories.js @@ -1,9 +1,14 @@ import { storiesOf, html, withKnobs } from '@open-wc/demoing-storybook'; -import { css, render } from '@lion/core'; +import { css, render, LitElement } from '@lion/core'; import '@lion/icon/lion-icon.js'; import '@lion/button/lion-button.js'; -import { withBottomSheetConfig, withDropdownConfig, withModalDialogConfig } from '../index.js'; -import '../lion-overlay.js'; +import { + withBottomSheetConfig, + withDropdownConfig, + withModalDialogConfig, + OverlayMixin, + OverlayController, +} from '../index.js'; function renderOffline(litHtmlTemplate) { const offlineRenderContainer = document.createElement('div'); @@ -51,7 +56,7 @@ const overlayDemoStyle = css` margin-top: 68px; } - lion-overlay { + lion-demo-overlay { padding: 10px; } @@ -88,7 +93,45 @@ const overlayDemoStyle = css` } `; -storiesOf('Overlay System | Overlay Component', module) +customElements.define( + 'lion-demo-overlay', + class extends OverlayMixin(LitElement) { + // eslint-disable-next-line class-methods-use-this + _defineOverlay({ contentNode, invokerNode }) { + return new OverlayController({ + placementMode: 'global', // have to set a default + contentNode, + invokerNode, + ...this.config, + }); + } + + _setupOpenCloseListeners() { + this.__close = () => { + this.opened = false; + }; + this.__toggle = () => { + this.opened = !this.opened; + }; + this._overlayCtrl.invokerNode.addEventListener('click', this.__toggle); + this._overlayCtrl.contentNode.addEventListener('close', this.__close); + } + + _teardownOpenCloseListeners() { + this._overlayCtrl.invokerNode.removeEventListener('click', this.__toggle); + this._overlayCtrl.contentNode.removeEventListener('close', this.__close); + } + + render() { + return html` + + + `; + } + }, +); + +storiesOf('Overlay System | Overlay as a WC', module) .addDecorator(withKnobs) .add( 'Default', @@ -97,8 +140,9 @@ storiesOf('Overlay System | Overlay Component', module) ${overlayDemoStyle}

- Important note: Your slot="content" gets moved to global overlay container. - After initialization it is no longer a child of lion-overlay + Important note: For placementMode: 'global', your + slot="content" gets moved to global overlay container. After initialization it + is no longer a child of lion-demo-overlay

To close your overlay from some action performed inside the content slot, fire a @@ -111,7 +155,7 @@ storiesOf('Overlay System | Overlay Component', module)

The demo below demonstrates this

- + Overlay
Hello! You can close this notification here: @@ -121,13 +165,13 @@ storiesOf('Overlay System | Overlay Component', module) >⨯
-
+
`, ) .add('Global placement configuration', () => { const overlay = placement => html` - Overlay ${placement} @@ -139,7 +183,7 @@ storiesOf('Overlay System | Overlay Component', module) >⨯ - + `; return html` @@ -159,7 +203,7 @@ storiesOf('Overlay System | Overlay Component', module) ${overlayDemoStyle}
- Overlay @@ -171,7 +215,7 @@ storiesOf('Overlay System | Overlay Component', module) >⨯
- + `, ) @@ -186,13 +230,13 @@ storiesOf('Overlay System | Overlay Component', module) Popper.js Docs
- UK - +
`, ) .add('Switch overlays configuration', () => { const overlay = renderOffline(html` - + Overlay
Hello! You can close this notification here: @@ -235,7 +279,7 @@ storiesOf('Overlay System | Overlay Component', module) >⨯
-
+ `); return html` @@ -278,7 +322,7 @@ storiesOf('Overlay System | Overlay Component', module) }) .add('On hover', () => { const popup = renderOffline(html` - United Kingdom - + `); return html` @@ -315,7 +359,7 @@ storiesOf('Overlay System | Overlay Component', module) }) .add('On an input', () => { const popup = renderOffline(html` - - + `); return html` @@ -353,7 +397,7 @@ storiesOf('Overlay System | Overlay Component', module) /* .add('Toggle placement with knobs', () => { const overlay = (placementMode = 'global') => html` - ⨯ - + `; return html` diff --git a/packages/overlays/test/OverlayController.test.js b/packages/overlays/test/OverlayController.test.js index 3cdd02c3f..ec4bb8526 100644 --- a/packages/overlays/test/OverlayController.test.js +++ b/packages/overlays/test/OverlayController.test.js @@ -881,7 +881,8 @@ describe('OverlayController', () => { expect(ctrl.contentNode).to.equal(contentNode); }); - it('allows for updating viewport config placement only, while keeping the content shown', async () => { + // TODO: Currently not working, enable again when we fix updateConfig + it.skip('allows for updating viewport config placement only, while keeping the content shown', async () => { const contentNode = fixtureSync(html`
my content
`); diff --git a/packages/overlays/test/lion-overlay.test.js b/packages/overlays/test/lion-overlay.test.js deleted file mode 100644 index 2d42c7663..000000000 --- a/packages/overlays/test/lion-overlay.test.js +++ /dev/null @@ -1,79 +0,0 @@ -import { expect, fixture, html } from '@open-wc/testing'; - -import '../lion-overlay.js'; - -describe('lion-overlay', () => { - describe('Basic', () => { - it('should not be shown by default', async () => { - const el = await fixture(html` - -
Hey there
- Invoker button -
- `); - expect(el._overlayCtrl.isShown).to.be.false; - }); - - it('should show content on invoker click', async () => { - const el = await fixture(html` - -
- Hey there -
- Invoker button -
- `); - const invoker = el.querySelector('[slot="invoker"]'); - invoker.click(); - await el.updateComplete; - - expect(el._overlayCtrl.isShown).to.be.true; - }); - - it('should hide content on close event', async () => { - const el = await fixture(html` - -
- Hey there - -
- Invoker button -
- `); - const invoker = el.querySelector('[slot="invoker"]'); - invoker.click(); - await el.updateComplete; - - expect(el._overlayCtrl.isShown).to.be.true; - - const closeBtn = el._overlayCtrl.contentNode.querySelector('button'); - closeBtn.click(); - await el.updateComplete; - - expect(el._overlayCtrl.isShown).to.be.false; - }); - - it('should respond to initially and dynamically setting the config', async () => { - const el = await fixture(html` - -
Hey there
- Invoker button -
- `); - 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', - ), - ); - }); - }); -}); diff --git a/packages/popup/README.md b/packages/popup/README.md deleted file mode 100644 index 873dadf49..000000000 --- a/packages/popup/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# Popup - -[//]: # 'AUTO INSERT HEADER PREPUBLISH' - -`lion-popup` is a component used for basic popups on click. -Its purpose is to show content appearing when the user clicks an invoker element with the cursor or with the keyboard. - -## Features - -- Show content when clicking the invoker -- Use the position property to position the content popup relative to the invoker - -## How to use - -### Installation - -```sh -npm i --save @lion/popup -``` - -```js -import '@lion/popup/lion-popup.js'; -``` - -### Example - -```html - -
This is a popup
- - Popup on link - - -``` diff --git a/packages/popup/index.js b/packages/popup/index.js deleted file mode 100644 index 6b671f807..000000000 --- a/packages/popup/index.js +++ /dev/null @@ -1 +0,0 @@ -export { LionPopup } from './src/LionPopup.js'; diff --git a/packages/popup/lion-popup.js b/packages/popup/lion-popup.js deleted file mode 100644 index e5dbebcdf..000000000 --- a/packages/popup/lion-popup.js +++ /dev/null @@ -1,3 +0,0 @@ -import { LionPopup } from './src/LionPopup.js'; - -customElements.define('lion-popup', LionPopup); diff --git a/packages/popup/src/LionPopup.js b/packages/popup/src/LionPopup.js deleted file mode 100644 index 0364763bf..000000000 --- a/packages/popup/src/LionPopup.js +++ /dev/null @@ -1,16 +0,0 @@ -import { OverlayController, LionOverlay } from '@lion/overlays'; - -export class LionPopup extends LionOverlay { - // eslint-disable-next-line class-methods-use-this - _defineOverlay() { - return new OverlayController({ - placementMode: 'local', - hidesOnOutsideClick: true, - hidesOnEsc: true, - contentNode: this._overlayContentNode, - invokerNode: this._overlayInvokerNode, - handlesAccessibility: true, - ...this.config, - }); - } -} diff --git a/packages/popup/stories/index.stories.js b/packages/popup/stories/index.stories.js deleted file mode 100644 index d2b789f55..000000000 --- a/packages/popup/stories/index.stories.js +++ /dev/null @@ -1,131 +0,0 @@ -import { storiesOf, html, withKnobs, object, text } from '@open-wc/demoing-storybook'; -import { css } from '@lion/core'; - -import '@lion/icon/lion-icon.js'; -import '@lion/button/lion-button.js'; -import '../lion-popup.js'; - -const popupDemoStyle = css` - .demo-box { - width: 200px; - background-color: white; - border-radius: 2px; - border: 1px solid grey; - margin: 250px 0 0 250px; - padding: 8px; - } - - .demo-box_placements { - display: flex; - flex-direction: column; - width: 173px; - margin: 0 auto; - margin-top: 68px; - } - - lion-popup { - padding: 10px; - } - - .demo-box__column { - display: flex; - flex-direction: column; - } - - .popup { - display: block; - position: absolute; - font-size: 16px; - color: white; - background-color: black; - border-radius: 4px; - padding: 8px; - } - - @media (max-width: 480px) { - .popup { - display: none; - } - } -`; - -storiesOf('Overlays Specific WC|Popup', module) - .addDecorator(withKnobs) - .add( - 'Button popup', - () => html` - -
- - Popup - - -
- `, - ) - .add( - 'placements', - () => html` - -
- - Top - - - - Right - - - - Bottom - - - - Left - - -
- `, - ) - .add( - 'Override popper configuration', - () => html` - -

Use the Storybook Knobs to dynamically change the popper configuration!

-
- - ${text('Invoker text', 'Click me!')} - - -
- `, - ); diff --git a/packages/popup/test/lion-popup.test.js b/packages/popup/test/lion-popup.test.js deleted file mode 100644 index 00db53e14..000000000 --- a/packages/popup/test/lion-popup.test.js +++ /dev/null @@ -1,63 +0,0 @@ -import { expect, fixture, html } from '@open-wc/testing'; - -import '../lion-popup.js'; - -describe('lion-popup', () => { - describe('Basic', () => { - it('should not be shown by default', async () => { - const el = await fixture(html` - - - Popup button - - `); - expect(el._overlayCtrl.isShown).to.be.false; - }); - - it('should toggle to show content on click', async () => { - const el = await fixture(html` - - - Popup button - - `); - const invoker = Array.from(el.children).find(child => child.slot === 'invoker'); - invoker.click(); - await el.updateComplete; - - expect(el._overlayCtrl.isShown).to.be.true; - invoker.click(); - await el.updateComplete; - expect(el._overlayCtrl.isShown).to.be.false; - }); - - it('should support popup containing html when specified in popup content body', async () => { - const el = await fixture(html` - -
This is Popup using overlay
- Popup button -
- `); - const invoker = Array.from(el.children).find(child => child.slot === 'invoker'); - const event = new Event('click'); - invoker.dispatchEvent(event); - await el.updateComplete; - expect(el.querySelector('strong')).to.not.be.undefined; - }); - - it('should respond to dynamically changing the popperConfig', async () => { - const el = await fixture(html` - - - Popup button - - `); - await el._overlayCtrl.show(); - expect(el._overlayCtrl._popper.options.placement).to.equal('top'); - - el.popperConfig = { placement: 'left' }; - await el._overlayCtrl.show(); - expect(el._overlayCtrl._popper.options.placement).to.equal('left'); - }); - }); -}); diff --git a/packages/tooltip/src/LionTooltip.js b/packages/tooltip/src/LionTooltip.js index d22bc1a19..6eeb8f36b 100644 --- a/packages/tooltip/src/LionTooltip.js +++ b/packages/tooltip/src/LionTooltip.js @@ -1,19 +1,24 @@ -import { LionPopup } from '@lion/popup'; +import { OverlayMixin, OverlayController } from '@lion/overlays'; +import { LitElement, html } from '@lion/core'; -export class LionTooltip extends LionPopup { +export class LionTooltip extends OverlayMixin(LitElement) { constructor() { super(); this.mouseActive = false; this.keyActive = false; - - // Trigger config setter to ensure it updates in OverlayController - this.config = { - ...this.config, - elementToFocusAfterHide: null, - }; } - _setupShowHideListeners() { + _defineOverlay({ contentNode, invokerNode }) { + return new OverlayController({ + placementMode: 'local', // have to set a default + elementToFocusAfterHide: null, + contentNode, + invokerNode, + ...this.config, + }); + } + + _setupOpenCloseListeners() { this.__resetActive = () => { this.mouseActive = false; this.keyActive = false; @@ -52,7 +57,7 @@ export class LionTooltip extends LionPopup { this._overlayInvokerNode.addEventListener('focusout', this.__hideKey); } - _teardownShowHideListeners() { + _teardownOpenCloseListeners() { this._overlayCtrl.removeEventListener('hide', this.__resetActive); this.removeEventListener('mouseenter', this.__showMouse); this.removeEventListener('mouseleave', this._hideMouse); @@ -64,4 +69,11 @@ export class LionTooltip extends LionPopup { super.connectedCallback(); this._overlayContentNode.setAttribute('role', 'tooltip'); } + + render() { + return html` + + + `; + } } diff --git a/packages/tooltip/stories/index.stories.js b/packages/tooltip/stories/index.stories.js index a2d04e7ce..728809d64 100644 --- a/packages/tooltip/stories/index.stories.js +++ b/packages/tooltip/stories/index.stories.js @@ -58,7 +58,7 @@ storiesOf('Overlays Specific WC|Tooltip', module) ${tooltipDemoStyle}
- + Tooltip
Hello there!
@@ -72,19 +72,19 @@ storiesOf('Overlays Specific WC|Tooltip', module) ${tooltipDemoStyle}
- + Top
Its top placement
- + Right
Its right placement
- + Bottom
Its bottom placement
- + Left
Its left placement
@@ -100,28 +100,30 @@ storiesOf('Overlays Specific WC|Tooltip', module)

Use the Storybook Knobs to dynamically change the popper configuration!

${text('Invoker text', 'Hover me!')}
${text('Content text', 'Hello, World!')}
diff --git a/stories/index.stories.js b/stories/index.stories.js index 2249c4ad9..632b5a3f1 100755 --- a/stories/index.stories.js +++ b/stories/index.stories.js @@ -29,7 +29,6 @@ import '../packages/calendar/stories/index.stories.js'; import '../packages/overlays/stories/index.stories.js'; import '../packages/overlays/stories/overlay-features.stories.js'; import '../packages/dialog/stories/index.stories.js'; -import '../packages/popup/stories/index.stories.js'; import '../packages/tooltip/stories/index.stories.js'; import '../packages/select-rich/stories/index.stories.js';