Merge pull request #289 from ing-bank/feat/richSelectAllowDynamicOverlays
Feat/rich select allow dynamic overlays
This commit is contained in:
commit
b3741aa2fd
4 changed files with 128 additions and 7 deletions
|
|
@ -34,6 +34,8 @@ export class DynamicOverlayController {
|
||||||
if (!this.content) {
|
if (!this.content) {
|
||||||
this.content = document.createElement('div');
|
this.content = document.createElement('div');
|
||||||
}
|
}
|
||||||
|
this.__fakeExtendsEventTarget();
|
||||||
|
this.__delegateEvent = this.__delegateEvent.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
add(ctrlToAdd) {
|
add(ctrlToAdd) {
|
||||||
|
|
@ -71,9 +73,13 @@ export class DynamicOverlayController {
|
||||||
if (this.isShown === true) {
|
if (this.isShown === true) {
|
||||||
throw new Error('You can not switch overlays while being shown');
|
throw new Error('You can not switch overlays while being shown');
|
||||||
}
|
}
|
||||||
|
const prevActive = this.active;
|
||||||
|
|
||||||
this.active.switchOut();
|
this.active.switchOut();
|
||||||
ctrlToSwitchTo.switchIn();
|
ctrlToSwitchTo.switchIn();
|
||||||
this.__active = ctrlToSwitchTo;
|
this.__active = ctrlToSwitchTo;
|
||||||
|
|
||||||
|
this._delegateEvents(this.__active, prevActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
async show() {
|
async show() {
|
||||||
|
|
@ -99,4 +105,24 @@ export class DynamicOverlayController {
|
||||||
get invokerNode() {
|
get invokerNode() {
|
||||||
return this.active.invokerNode;
|
return this.active.invokerNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_delegateEvents(active, prevActive) {
|
||||||
|
['show', 'hide'].forEach(event => {
|
||||||
|
active.addEventListener(event, this.__delegateEvent);
|
||||||
|
prevActive.removeEventListener(event, this.__delegateEvent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
__delegateEvent(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this.dispatchEvent(new Event(ev.type));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this method has to be removed when EventTarget polyfill is available on IE11
|
||||||
|
__fakeExtendsEventTarget() {
|
||||||
|
const delegate = document.createDocumentFragment();
|
||||||
|
['addEventListener', 'dispatchEvent', 'removeEventListener'].forEach(funcName => {
|
||||||
|
this[funcName] = (...args) => delegate[funcName](...args);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -133,4 +133,34 @@ describe('DynamicOverlayController', () => {
|
||||||
expect(globalOutSpy).to.have.callCount(1);
|
expect(globalOutSpy).to.have.callCount(1);
|
||||||
expect(localInSpy).to.have.callCount(1);
|
expect(localInSpy).to.have.callCount(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('API abstraction for active overlay controller', () => {
|
||||||
|
describe('Events', () => {
|
||||||
|
it('delegates "show/hide" event', async () => {
|
||||||
|
const ctrl = new DynamicOverlayController();
|
||||||
|
const global = new FakeGlobalCtrl(defaultOptions);
|
||||||
|
const local = new FakeLocalCtrl(defaultOptions);
|
||||||
|
ctrl.add(global);
|
||||||
|
ctrl.add(local);
|
||||||
|
ctrl.switchTo(local);
|
||||||
|
|
||||||
|
const showSpy = sinon.spy();
|
||||||
|
const hideSpy = sinon.spy();
|
||||||
|
|
||||||
|
ctrl.addEventListener('show', showSpy);
|
||||||
|
ctrl.addEventListener('hide', hideSpy);
|
||||||
|
|
||||||
|
await ctrl.show();
|
||||||
|
expect(showSpy.callCount).to.equal(1);
|
||||||
|
await ctrl.hide();
|
||||||
|
expect(hideSpy.callCount).to.equal(1);
|
||||||
|
|
||||||
|
ctrl.switchTo(global);
|
||||||
|
await ctrl.show();
|
||||||
|
expect(showSpy.callCount).to.equal(2);
|
||||||
|
await ctrl.hide();
|
||||||
|
expect(hideSpy.callCount).to.equal(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -548,11 +548,15 @@ export class LionSelectRich extends FormRegistrarMixin(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__setupOverlay() {
|
/**
|
||||||
this.__overlay = overlays.add(
|
* @overridable Subclassers can override the default
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line class-methods-use-this
|
||||||
|
_defineOverlay({ invokerNode, contentNode } = {}) {
|
||||||
|
return overlays.add(
|
||||||
new LocalOverlayController({
|
new LocalOverlayController({
|
||||||
contentNode: this._listboxNode,
|
contentNode,
|
||||||
invokerNode: this._invokerNode,
|
invokerNode,
|
||||||
hidesOnEsc: false,
|
hidesOnEsc: false,
|
||||||
hidesOnOutsideClick: true,
|
hidesOnOutsideClick: true,
|
||||||
inheritsReferenceObjectWidth: true,
|
inheritsReferenceObjectWidth: true,
|
||||||
|
|
@ -566,6 +570,13 @@ export class LionSelectRich extends FormRegistrarMixin(
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
__setupOverlay() {
|
||||||
|
this.__overlay = this._defineOverlay({
|
||||||
|
invokerNode: this._invokerNode,
|
||||||
|
contentNode: this._listboxNode,
|
||||||
|
});
|
||||||
|
|
||||||
this.__overlayOnShow = () => {
|
this.__overlayOnShow = () => {
|
||||||
this.opened = true;
|
this.opened = true;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,16 @@
|
||||||
import { expect, fixture, html, aTimeout } from '@open-wc/testing';
|
import { expect, fixture, html, aTimeout, defineCE, unsafeStatic } from '@open-wc/testing';
|
||||||
import './keyboardEventShimIE.js';
|
|
||||||
|
|
||||||
import '@lion/option/lion-option.js';
|
import '@lion/option/lion-option.js';
|
||||||
|
import {
|
||||||
|
overlays,
|
||||||
|
LocalOverlayController,
|
||||||
|
GlobalOverlayController,
|
||||||
|
DynamicOverlayController,
|
||||||
|
} from '@lion/overlays';
|
||||||
|
|
||||||
|
import './keyboardEventShimIE.js';
|
||||||
import '../lion-options.js';
|
import '../lion-options.js';
|
||||||
import '../lion-select-rich.js';
|
import '../lion-select-rich.js';
|
||||||
|
import { LionSelectRich } from '../index.js';
|
||||||
|
|
||||||
describe('lion-select-rich', () => {
|
describe('lion-select-rich', () => {
|
||||||
it('does not have a tabindex', async () => {
|
it('does not have a tabindex', async () => {
|
||||||
|
|
@ -328,4 +335,51 @@ describe('lion-select-rich', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Subclassers', () => {
|
||||||
|
it('allows to override the type of overlays', async () => {
|
||||||
|
const mySelectTagString = defineCE(
|
||||||
|
class MySelect extends LionSelectRich {
|
||||||
|
_defineOverlay({ invokerNode, contentNode }) {
|
||||||
|
// add a DynamicOverlayController
|
||||||
|
const dynamicCtrl = new DynamicOverlayController();
|
||||||
|
|
||||||
|
const localCtrl = overlays.add(
|
||||||
|
new LocalOverlayController({
|
||||||
|
contentNode,
|
||||||
|
invokerNode,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
dynamicCtrl.add(localCtrl);
|
||||||
|
|
||||||
|
const globalCtrl = overlays.add(
|
||||||
|
new GlobalOverlayController({
|
||||||
|
contentNode,
|
||||||
|
invokerNode,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
dynamicCtrl.add(globalCtrl);
|
||||||
|
|
||||||
|
return dynamicCtrl;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const mySelectTag = unsafeStatic(mySelectTagString);
|
||||||
|
|
||||||
|
const el = await fixture(html`
|
||||||
|
<${mySelectTag} label="Favorite color" name="color">
|
||||||
|
<lion-options slot="input">
|
||||||
|
${Array(2).map(
|
||||||
|
(_, i) => html`
|
||||||
|
<lion-option .modelValue="${{ value: i, checked: false }}">value ${i}</lion-option>
|
||||||
|
`,
|
||||||
|
)}
|
||||||
|
</lion-options>
|
||||||
|
</${mySelectTag}>
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(el.__overlay).to.be.instanceOf(DynamicOverlayController);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue