diff --git a/README.md b/README.md index 0fe06bbe1..378d78da0 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ The accessibility column indicates whether the functionality is accessible in it | [icon](./packages/icon) | [![icon](https://img.shields.io/npm/v/@lion/icon.svg)](https://www.npmjs.com/package/@lion/icon) | Display our svg icons | [#173][i173], [#172][i172] | | [steps](./packages/steps) | [![steps](https://img.shields.io/npm/v/@lion/steps.svg)](https://www.npmjs.com/package/@lion/steps) | Multi Step System | n/a | | [tabs](./packages/tabs) | [![tBS](https://img.shields.io/npm/v/@lion/tabs.svg)](https://www.npmjs.com/package/@lion/tabs) | Move between a small number of equally important views | n/a | -| **-- Forms --** | | | +| **-- Forms --** | | | | | [form](./packages/form) | [![form](https://img.shields.io/npm/v/@lion/form.svg)](https://www.npmjs.com/package/@lion/form) | Wrapper for multiple form elements | ✔️ | | [field](./packages/field) | [![field](https://img.shields.io/npm/v/@lion/field.svg)](https://www.npmjs.com/package/@lion/field) | Base Class for all inputs | [#190][i190] | | [fieldset](./packages/fieldset) | [![fieldset](https://img.shields.io/npm/v/@lion/fieldset.svg)](https://www.npmjs.com/package/@lion/fieldset) | Group for form inputs | ✔️ | @@ -48,7 +48,7 @@ The accessibility column indicates whether the functionality is accessible in it | [checkbox](./packages/checkbox) | [![checkbox](https://img.shields.io/npm/v/@lion/checkbox.svg)](https://www.npmjs.com/package/@lion/checkbox) | Checkbox form element | ✔️ | | [checkbox-group](./packages/checkbox-group) | [![checkbox-group](https://img.shields.io/npm/v/@lion/checkbox-group.svg)](https://www.npmjs.com/package/@lion/checkbox-group) | Group of checkboxes | ✔️ | | [input](./packages/input) | [![input](https://img.shields.io/npm/v/@lion/input.svg)](https://www.npmjs.com/package/@lion/input) | Input element for strings | ✔️ | -| [input-amount](./packages/input-amount) | [![input-amount](https://img.shields.io/npm/v/@lion/input-amount.svg)](https://www.npmjs.com/package/@lion/input-amount) | Input element for amounts | [#166][i166] | ✔️ | +| [input-amount](./packages/input-amount) | [![input-amount](https://img.shields.io/npm/v/@lion/input-amount.svg)](https://www.npmjs.com/package/@lion/input-amount) | Input element for amounts | [#166][i166] | | [input-date](./packages/input-date) | [![input-date](https://img.shields.io/npm/v/@lion/input-date.svg)](https://www.npmjs.com/package/@lion/input-date) | Input element for dates | ✔️ | | [input-datepicker](./packages/input-datepicker) | [![input-datepicker](https://img.shields.io/npm/v/@lion/input-datepicker.svg)](https://www.npmjs.com/package/@lion/input-datepicker) | Input element for dates with a datepicker | ✔️ | | [input-email](./packages/input-email) | [![input-email](https://img.shields.io/npm/v/@lion/input-email.svg)](https://www.npmjs.com/package/@lion/input-email) | Input element for e-mails | [#169][i169] | @@ -58,9 +58,9 @@ The accessibility column indicates whether the functionality is accessible in it | [select](./packages/select) | [![select](https://img.shields.io/npm/v/@lion/select.svg)](https://www.npmjs.com/package/@lion/select) | Simple native dropdown element | ✔️ | | [textarea](./packages/textarea) | [![textarea](https://img.shields.io/npm/v/@lion/textarea.svg)](https://www.npmjs.com/package/@lion/textarea) | Multiline text input | [#165][i165] | | **-- Overlays --** | | | | -| [overlays](./packages/overlays) | [![overlays](https://img.shields.io/npm/v/@lion/overlays.svg)](https://www.npmjs.com/package/@lion/overlays) | Overlays System using lit-html for rendering | ✔️ | -| [popup](./packages/popup) | [![popup](https://img.shields.io/npm/v/@lion/popup.svg)](https://www.npmjs.com/package/@lion/popup) | Popup element | [#175][i175], [#174][i174] | -| [tooltip](./packages/tooltip) | [![tooltip](https://img.shields.io/npm/v/@lion/tooltip.svg)](https://www.npmjs.com/package/@lion/tooltip) | Popup element | [#178][i178], [#177][i177], [#176][i176], [#175][i175], [#174][i174] | +| [overlays](./packages/overlays) | [![overlays](https://img.shields.io/npm/v/@lion/overlays.svg)](https://www.npmjs.com/package/@lion/overlays) | Overlay System | ✔️ | +| [dialog](./packages/dialog) | [![dialog](https://img.shields.io/npm/v/@lion/dialog.svg)](https://www.npmjs.com/package/@lion/dialog) | Dialog element | ✔️ | +| [tooltip](./packages/tooltip) | [![tooltip](https://img.shields.io/npm/v/@lion/tooltip.svg)](https://www.npmjs.com/package/@lion/tooltip) | Tooltip element | [#178][i178], [#177][i177], [#176][i176], [#175][i175], [#174][i174] | ## How to use diff --git a/packages/calendar/test/lion-calendar.test.js b/packages/calendar/test/lion-calendar.test.js index 8ea9f16a7..dfd711107 100644 --- a/packages/calendar/test/lion-calendar.test.js +++ b/packages/calendar/test/lion-calendar.test.js @@ -108,10 +108,16 @@ describe('', () => { `), ); expect( - elObj.checkForAllDayObjs(o => o.buttonEl.getAttribute('tabindex') === '0', n => n === 5), + elObj.checkForAllDayObjs( + o => o.buttonEl.getAttribute('tabindex') === '0', + n => n === 5, + ), ).to.be.true; expect( - elObj.checkForAllDayObjs(o => o.buttonEl.getAttribute('tabindex') === '-1', n => n !== 5), + elObj.checkForAllDayObjs( + o => o.buttonEl.getAttribute('tabindex') === '-1', + n => n !== 5, + ), ).to.be.true; }); diff --git a/packages/dialog/README.md b/packages/dialog/README.md index 384ce2efc..aa2abb950 100644 --- a/packages/dialog/README.md +++ b/packages/dialog/README.md @@ -34,7 +34,7 @@ html` }}> This is a dialog
@@ -98,7 +98,7 @@ storiesOf('Overlays Specific WC | Dialog', module) Hello! You can close this notification here: e.target.dispatchEvent(new Event('close', { bubbles: true }))} + @click=${e => e.target.dispatchEvent(new Event('dialog-close', { bubbles: true }))} >⨯ @@ -123,7 +123,7 @@ storiesOf('Overlays Specific WC | Dialog', module) Hello! You can close this notification here: e.target.dispatchEvent(new Event('close', { bubbles: true }))} + @click=${e => e.target.dispatchEvent(new Event('dialog-close', { bubbles: true }))} >⨯ diff --git a/packages/dialog/test/lion-dialog.test.js b/packages/dialog/test/lion-dialog.test.js index c3717f79a..155fef199 100644 --- a/packages/dialog/test/lion-dialog.test.js +++ b/packages/dialog/test/lion-dialog.test.js @@ -35,7 +35,9 @@ describe('lion-dialog', () => {
Hey there -
diff --git a/packages/fieldset/test/lion-fieldset.test.js b/packages/fieldset/test/lion-fieldset.test.js index fa71b2b0d..1c6b82668 100644 --- a/packages/fieldset/test/lion-fieldset.test.js +++ b/packages/fieldset/test/lion-fieldset.test.js @@ -177,16 +177,28 @@ describe('', () => { expect(el.modelValue).to.deep.equal({ lastName: 'Bar', newfieldset: { - 'hobbies[]': [{ checked: true, value: 'chess' }, { checked: false, value: 'football' }], - 'gender[]': [{ checked: false, value: 'male' }, { checked: false, value: 'female' }], + 'hobbies[]': [ + { checked: true, value: 'chess' }, + { checked: false, value: 'football' }, + ], + 'gender[]': [ + { checked: false, value: 'male' }, + { checked: false, value: 'female' }, + ], color: { checked: false, value: 'blue' }, }, }); el.modelValue = { lastName: 2, newfieldset: { - 'hobbies[]': [{ checked: true, value: 'chess' }, { checked: false, value: 'baseball' }], - 'gender[]': [{ checked: false, value: 'male' }, { checked: false, value: 'female' }], + 'hobbies[]': [ + { checked: true, value: 'chess' }, + { checked: false, value: 'baseball' }, + ], + 'gender[]': [ + { checked: false, value: 'male' }, + { checked: false, value: 'female' }, + ], color: { checked: false, value: 'blue' }, }, }; @@ -546,7 +558,10 @@ describe('', () => { expect(fieldset.formElements['hobbies[]'][0].serializedValue).to.equal('Bar-serialized'); expect(fieldset.serializeGroup()).to.deep.equal({ 'hobbies[]': ['Bar-serialized', { checked: false, value: 'rugby' }], - 'gender[]': [{ checked: false, value: 'male' }, { checked: false, value: 'female' }], + 'gender[]': [ + { checked: false, value: 'male' }, + { checked: false, value: 'female' }, + ], color: { checked: false, value: 'blue' }, }); }); @@ -562,15 +577,27 @@ describe('', () => { fieldset.formElements.color.modelValue = { checked: false, value: 'blue' }; expect(fieldset.serializeGroup()).to.deep.equal({ - 'hobbies[]': [{ checked: true, value: 'football' }, { checked: false, value: 'rugby' }], - 'gender[]': [{ checked: true, value: 'male' }, { checked: false, value: 'female' }], + 'hobbies[]': [ + { checked: true, value: 'football' }, + { checked: false, value: 'rugby' }, + ], + 'gender[]': [ + { checked: true, value: 'male' }, + { checked: false, value: 'female' }, + ], color: { checked: false, value: 'blue' }, }); fieldset.formElements.color.disabled = true; expect(fieldset.serializeGroup()).to.deep.equal({ - 'hobbies[]': [{ checked: true, value: 'football' }, { checked: false, value: 'rugby' }], - 'gender[]': [{ checked: true, value: 'male' }, { checked: false, value: 'female' }], + 'hobbies[]': [ + { checked: true, value: 'football' }, + { checked: false, value: 'rugby' }, + ], + 'gender[]': [ + { checked: true, value: 'male' }, + { checked: false, value: 'female' }, + ], }); }); @@ -594,8 +621,14 @@ describe('', () => { expect(fieldset.serializeGroup()).to.deep.equal({ comment: 'Foo', newfieldset: { - 'hobbies[]': [{ checked: false, value: 'chess' }, { checked: false, value: 'rugby' }], - 'gender[]': [{ checked: false, value: 'male' }, { checked: false, value: 'female' }], + 'hobbies[]': [ + { checked: false, value: 'chess' }, + { checked: false, value: 'rugby' }, + ], + 'gender[]': [ + { checked: false, value: 'male' }, + { checked: false, value: 'female' }, + ], color: { checked: false, value: 'blue' }, }, }); @@ -622,8 +655,14 @@ describe('', () => { expect(fieldset.serializeGroup()).to.deep.equal({ comment: 'Foo', newfieldset: { - 'hobbies[]': [{ checked: false, value: 'chess' }, { checked: false, value: 'rugby' }], - 'gender[]': [{ checked: false, value: 'male' }, { checked: false, value: 'female' }], + 'hobbies[]': [ + { checked: false, value: 'chess' }, + { checked: false, value: 'rugby' }, + ], + 'gender[]': [ + { checked: false, value: 'male' }, + { checked: false, value: 'female' }, + ], }, }); @@ -631,8 +670,14 @@ describe('', () => { expect(fieldset.serializeGroup()).to.deep.equal({ comment: 'Foo', newfieldset: { - 'hobbies[]': [{ checked: false, value: 'chess' }, { checked: false, value: 'rugby' }], - 'gender[]': [{ checked: false, value: 'male' }, { checked: false, value: 'female' }], + 'hobbies[]': [ + { checked: false, value: 'chess' }, + { checked: false, value: 'rugby' }, + ], + 'gender[]': [ + { checked: false, value: 'male' }, + { checked: false, value: 'female' }, + ], color: { checked: false, value: 'blue' }, }, }); @@ -647,8 +692,14 @@ describe('', () => { fieldset.formElements['gender[]'][1].modelValue = { checked: false, value: 'female' }; fieldset.formElements.color.modelValue = { checked: false, value: 'blue' }; expect(fieldset.serializeGroup()).to.deep.equal({ - 'hobbies[]': [{ checked: false, value: 'chess' }, { checked: false, value: 'rugby' }], - 'gender[]': [{ checked: false, value: 'male' }, { checked: false, value: 'female' }], + 'hobbies[]': [ + { checked: false, value: 'chess' }, + { checked: false, value: 'rugby' }, + ], + 'gender[]': [ + { checked: false, value: 'male' }, + { checked: false, value: 'female' }, + ], color: { checked: false, value: 'blue' }, }); }); diff --git a/packages/input-datepicker/src/LionInputDatepicker.js b/packages/input-datepicker/src/LionInputDatepicker.js index 9b5c04d2e..2d34c8475 100644 --- a/packages/input-datepicker/src/LionInputDatepicker.js +++ b/packages/input-datepicker/src/LionInputDatepicker.js @@ -1,6 +1,6 @@ import { html, ifDefined, render } from '@lion/core'; import { LionInputDate } from '@lion/input-date'; -import { OverlayController, withModalDialogConfig, OverlayMixin } from '@lion/overlays'; +import { withModalDialogConfig, OverlayMixin } from '@lion/overlays'; import '@lion/calendar/lion-calendar.js'; import './lion-calendar-overlay-frame.js'; @@ -255,17 +255,14 @@ export class LionInputDatepicker extends OverlayMixin(LionInputDate) { /** * @override Configures OverlayMixin - * @desc returns an instance of a (dynamic) overlay controller - * @returns {OverlayController} + * @desc overrides default configuration options for this component + * @returns {Object} */ // eslint-disable-next-line class-methods-use-this - _defineOverlay({ contentNode, invokerNode }) { - const ctrl = new OverlayController({ + _defineOverlayConfig() { + return { ...withModalDialogConfig(), - contentNode, - invokerNode, - }); - return ctrl; + }; } async __openCalendarOverlay() { diff --git a/packages/overlays/docs/migration.md b/packages/overlays/docs/migration.md index f219c78fc..3e301a123 100644 --- a/packages/overlays/docs/migration.md +++ b/packages/overlays/docs/migration.md @@ -53,7 +53,7 @@ const template = html`
Hello, World!
-
diff --git a/packages/overlays/src/OverlayController.js b/packages/overlays/src/OverlayController.js index 2865fcbe5..299975a4e 100644 --- a/packages/overlays/src/OverlayController.js +++ b/packages/overlays/src/OverlayController.js @@ -152,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.config.popperConfig || {}); + this.__mergePopperConfigs(cfgToAdd.popperConfig || {}); } this._handleFeatures({ phase: 'init' }); } diff --git a/packages/overlays/src/OverlayMixin.js b/packages/overlays/src/OverlayMixin.js index 749a2184f..552775074 100644 --- a/packages/overlays/src/OverlayMixin.js +++ b/packages/overlays/src/OverlayMixin.js @@ -1,4 +1,5 @@ import { render, dedupeMixin } from '@lion/core'; +import { OverlayController } from './OverlayController.js'; /** * @type {Function()} @@ -18,12 +19,16 @@ export const OverlayMixin = dedupeMixin( config: { type: Object, }, + closeEventName: { + type: String, + }, }; } constructor() { super(); this.config = {}; + this.closeEventName = 'overlay-close'; } get opened() { @@ -59,10 +64,29 @@ export const OverlayMixin = dedupeMixin( /** * @overridable method `_defineOverlay` * @desc returns an instance of a (dynamic) overlay controller + * In case overriding _defineOverlayConfig is not enough * @returns {OverlayController} */ // eslint-disable-next-line - _defineOverlay({ contentNode, invokerNode }) {} + _defineOverlay({ contentNode, invokerNode }) { + return new OverlayController({ + contentNode, + invokerNode, + ...this._defineOverlayConfig(), + ...this.config, + }); + } + + /** + * @overridable method `_defineOverlay` + * @desc returns an object with default configuration options for your overlay component. + * This is generally speaking easier to override than _defineOverlay method entirely. + * @returns {OverlayController} + */ + // eslint-disable-next-line + _defineOverlayConfig() { + return {}; + } /** * @overridable @@ -84,6 +108,14 @@ export const OverlayMixin = dedupeMixin( super.connectedCallback(); } 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(); @@ -91,9 +123,7 @@ export const OverlayMixin = dedupeMixin( firstUpdated(c) { super.firstUpdated(c); - if (this._overlayCtrl.config.placementMode === 'local') { - this._createOutletForLocalOverlay(); - } + this._createOutletForLocalOverlay(); } updated(c) { @@ -107,6 +137,7 @@ export const OverlayMixin = dedupeMixin( if (super.disconnectedCallback) { super.disconnectedCallback(); } + this._overlayCtrl.contentNode.removeEventListener(this.closeEventName, this.__close); this._teardownOpenCloseListeners(); } @@ -114,7 +145,8 @@ export const OverlayMixin = dedupeMixin( 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') + // 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]'); @@ -184,7 +216,7 @@ export const OverlayMixin = dedupeMixin( __syncPopper() { if (this._overlayCtrl) { - // TODO: Use updateConfig directly.. but first check if this sync is even still needed! Maybe we can remove it. + // TODO: Use updateConfig directly.. But maybe we can remove this entirely. this._overlayCtrl.updatePopperConfig(this.config.popperConfig); } } diff --git a/packages/overlays/stories/index.stories.js b/packages/overlays/stories/index.stories.js index 32ffdacf7..e33c82de1 100644 --- a/packages/overlays/stories/index.stories.js +++ b/packages/overlays/stories/index.stories.js @@ -7,7 +7,6 @@ import { withDropdownConfig, withModalDialogConfig, OverlayMixin, - OverlayController, } from '../index.js'; function renderOffline(litHtmlTemplate) { @@ -96,14 +95,16 @@ const overlayDemoStyle = css` customElements.define( 'lion-demo-overlay', class extends OverlayMixin(LitElement) { + constructor() { + super(); + this.closeEventName = 'demo-overlay-close'; + } + // eslint-disable-next-line class-methods-use-this - _defineOverlay({ contentNode, invokerNode }) { - return new OverlayController({ + _defineOverlayConfig() { + return { placementMode: 'global', // have to set a default - contentNode, - invokerNode, - ...this.config, - }); + }; } _setupOpenCloseListeners() { @@ -114,12 +115,10 @@ customElements.define( 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() { @@ -161,7 +160,8 @@ storiesOf('Overlay System | Overlay as a WC', module) Hello! You can close this notification here: e.target.dispatchEvent(new Event('close', { bubbles: true }))} + @click=${e => + e.target.dispatchEvent(new Event('demo-overlay-close', { bubbles: true }))} >⨯ @@ -179,7 +179,8 @@ storiesOf('Overlay System | Overlay as a WC', module) Hello! You can close this notification here: e.target.dispatchEvent(new Event('close', { bubbles: true }))} + @click=${e => + e.target.dispatchEvent(new Event('demo-overlay-close', { bubbles: true }))} >⨯ @@ -211,7 +212,8 @@ storiesOf('Overlay System | Overlay as a WC', module) Hello! You can close this notification here: e.target.dispatchEvent(new Event('close', { bubbles: true }))} + @click=${e => + e.target.dispatchEvent(new Event('demo-overlay-close', { bubbles: true }))} >⨯ @@ -275,7 +277,8 @@ storiesOf('Overlay System | Overlay as a WC', module) Hello! You can close this notification here: e.target.dispatchEvent(new Event('close', { bubbles: true }))} + @click=${e => + e.target.dispatchEvent(new Event('demo-overlay-close', { bubbles: true }))} >⨯ @@ -410,7 +413,7 @@ storiesOf('Overlay System | Overlay as a WC', module) Hello! You can close this notification here: e.target.dispatchEvent(new Event('close', { bubbles: true }))} + @click=${e => e.target.dispatchEvent(new Event('demo-overlay-close', { bubbles: true }))} >⨯ diff --git a/packages/select-rich/src/LionSelectRich.js b/packages/select-rich/src/LionSelectRich.js index 1067b271e..2f81abfb6 100644 --- a/packages/select-rich/src/LionSelectRich.js +++ b/packages/select-rich/src/LionSelectRich.js @@ -1,5 +1,5 @@ import { html, css, LitElement, SlotMixin } from '@lion/core'; -import { OverlayController, withDropdownConfig, OverlayMixin } from '@lion/overlays'; +import { withDropdownConfig, OverlayMixin } from '@lion/overlays'; import { FormControlMixin, InteractionStateMixin, FormRegistrarMixin } from '@lion/field'; import { ValidateMixin } from '@lion/validate'; import './differentKeyNamesShimIE.js'; @@ -601,12 +601,10 @@ export class LionSelectRich extends OverlayMixin( } // eslint-disable-next-line class-methods-use-this - _defineOverlay({ invokerNode, contentNode } = {}) { - return new OverlayController({ + _defineOverlayConfig() { + return { ...withDropdownConfig(), - contentNode, - invokerNode, - }); + }; } __setupOverlay() { diff --git a/packages/tooltip/README.md b/packages/tooltip/README.md index e7a4b8914..c42b725f7 100644 --- a/packages/tooltip/README.md +++ b/packages/tooltip/README.md @@ -17,7 +17,7 @@ e invoker element is focused. ### Installation ```sh -npm i --save @lion/popup +npm i --save @lion/tooltip ``` ```js @@ -28,7 +28,7 @@ import '@lion/tooltip/lion-tooltip.js'; ```html -
This is a popup
+
This is a tooltip
Popup on link diff --git a/packages/tooltip/package.json b/packages/tooltip/package.json index 81d716e1b..94f03ee80 100644 --- a/packages/tooltip/package.json +++ b/packages/tooltip/package.json @@ -33,8 +33,7 @@ ], "dependencies": { "@lion/core": "^0.3.0", - "@lion/overlays": "^0.6.4", - "@lion/popup": "^0.3.20" + "@lion/overlays": "^0.6.4" }, "devDependencies": { "@lion/button": "^0.3.43", diff --git a/packages/tooltip/src/LionTooltip.js b/packages/tooltip/src/LionTooltip.js index 6eeb8f36b..1976ea6be 100644 --- a/packages/tooltip/src/LionTooltip.js +++ b/packages/tooltip/src/LionTooltip.js @@ -1,21 +1,20 @@ -import { OverlayMixin, OverlayController } from '@lion/overlays'; +import { OverlayMixin } from '@lion/overlays'; import { LitElement, html } from '@lion/core'; export class LionTooltip extends OverlayMixin(LitElement) { constructor() { super(); + this.closeEventName = 'tooltip-close'; this.mouseActive = false; this.keyActive = false; } - _defineOverlay({ contentNode, invokerNode }) { - return new OverlayController({ + // eslint-disable-next-line class-methods-use-this + _defineOverlayConfig() { + return { placementMode: 'local', // have to set a default elementToFocusAfterHide: null, - contentNode, - invokerNode, - ...this.config, - }); + }; } _setupOpenCloseListeners() {