Merge pull request #289 from ing-bank/feat/richSelectAllowDynamicOverlays

Feat/rich select allow dynamic overlays
This commit is contained in:
Joren Broekema 2019-09-26 07:07:45 -07:00 committed by GitHub
commit b3741aa2fd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 128 additions and 7 deletions

View file

@ -34,6 +34,8 @@ export class DynamicOverlayController {
if (!this.content) {
this.content = document.createElement('div');
}
this.__fakeExtendsEventTarget();
this.__delegateEvent = this.__delegateEvent.bind(this);
}
add(ctrlToAdd) {
@ -71,9 +73,13 @@ export class DynamicOverlayController {
if (this.isShown === true) {
throw new Error('You can not switch overlays while being shown');
}
const prevActive = this.active;
this.active.switchOut();
ctrlToSwitchTo.switchIn();
this.__active = ctrlToSwitchTo;
this._delegateEvents(this.__active, prevActive);
}
async show() {
@ -99,4 +105,24 @@ export class DynamicOverlayController {
get 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);
});
}
}

View file

@ -133,4 +133,34 @@ describe('DynamicOverlayController', () => {
expect(globalOutSpy).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);
});
});
});
});

View file

@ -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({
contentNode: this._listboxNode,
invokerNode: this._invokerNode,
contentNode,
invokerNode,
hidesOnEsc: false,
hidesOnOutsideClick: 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.opened = true;

View file

@ -1,9 +1,16 @@
import { expect, fixture, html, aTimeout } from '@open-wc/testing';
import './keyboardEventShimIE.js';
import { expect, fixture, html, aTimeout, defineCE, unsafeStatic } from '@open-wc/testing';
import '@lion/option/lion-option.js';
import {
overlays,
LocalOverlayController,
GlobalOverlayController,
DynamicOverlayController,
} from '@lion/overlays';
import './keyboardEventShimIE.js';
import '../lion-options.js';
import '../lion-select-rich.js';
import { LionSelectRich } from '../index.js';
describe('lion-select-rich', () => {
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);
});
});
});