feat(popup): change API to popper based
This commit is contained in:
parent
22357ea81f
commit
1e6d60dfdb
3 changed files with 96 additions and 25 deletions
|
|
@ -4,29 +4,44 @@ import { overlays, LocalOverlayController } from '@lion/overlays';
|
||||||
export class LionPopup extends UpdatingElement {
|
export class LionPopup extends UpdatingElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
position: {
|
placementConfig: {
|
||||||
type: String,
|
type: Object,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get placementConfig() {
|
||||||
|
return this._placementConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
set placementConfig(config) {
|
||||||
|
this._placementConfig = {
|
||||||
|
...this._placementConfig,
|
||||||
|
...config,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this._controller && this._controller._popper) {
|
||||||
|
this._controller.updatePlacementConfig(this._placementConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
this.contenNode = this.querySelector('[slot="content"]');
|
this.contentNode = this.querySelector('[slot="content"]');
|
||||||
this.invokerNode = this.querySelector('[slot="invoker"]');
|
this.invokerNode = this.querySelector('[slot="invoker"]');
|
||||||
|
|
||||||
this._popup = overlays.add(
|
this._controller = overlays.add(
|
||||||
new LocalOverlayController({
|
new LocalOverlayController({
|
||||||
hidesOnEsc: true,
|
hidesOnEsc: true,
|
||||||
hidesOnOutsideClick: true,
|
hidesOnOutsideClick: true,
|
||||||
placement: this.position,
|
placementConfig: this.placementConfig,
|
||||||
contentNode: this.contenNode,
|
contentNode: this.contentNode,
|
||||||
invokerNode: this.invokerNode,
|
invokerNode: this.invokerNode,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
this._show = () => this._popup.show();
|
this._show = () => this._controller.show();
|
||||||
this._hide = () => this._popup.hide();
|
this._hide = () => this._controller.hide();
|
||||||
this._toggle = () => this._popup.toggle();
|
this._toggle = () => this._controller.toggle();
|
||||||
|
|
||||||
this.invokerNode.addEventListener('click', this._toggle);
|
this.invokerNode.addEventListener('click', this._toggle);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { storiesOf, html } 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/icon/lion-icon.js';
|
||||||
|
|
@ -11,11 +11,11 @@ const popupDemoStyle = css`
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
border: 1px solid grey;
|
border: 1px solid grey;
|
||||||
margin: 250px;
|
margin: 250px 0 0 250px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.demo-box_positions {
|
.demo-box_placements {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 173px;
|
width: 173px;
|
||||||
|
|
@ -40,6 +40,8 @@ const popupDemoStyle = css`
|
||||||
background-color: black;
|
background-color: black;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
/* To display on top of elements with no z-index that are appear later in the DOM */
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
|
|
@ -50,6 +52,7 @@ const popupDemoStyle = css`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
storiesOf('Local Overlay System|Popup', module)
|
storiesOf('Local Overlay System|Popup', module)
|
||||||
|
.addDecorator(withKnobs)
|
||||||
.add(
|
.add(
|
||||||
'Button popup',
|
'Button popup',
|
||||||
() => html`
|
() => html`
|
||||||
|
|
@ -57,36 +60,73 @@ storiesOf('Local Overlay System|Popup', module)
|
||||||
${popupDemoStyle}
|
${popupDemoStyle}
|
||||||
</style>
|
</style>
|
||||||
<div class="demo-box">
|
<div class="demo-box">
|
||||||
<lion-popup position="right">
|
<lion-popup .placementConfig="${{ placement: 'top' }}">
|
||||||
<div slot="content" class="popup">hey there</div>
|
<div slot="content" class="popup">Hello there!</div>
|
||||||
<lion-button slot="invoker">Popup</lion-button>
|
<lion-button slot="invoker">Popup</lion-button>
|
||||||
</lion-popup>
|
</lion-popup>
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
)
|
)
|
||||||
.add(
|
.add(
|
||||||
'positions',
|
'placements',
|
||||||
() => html`
|
() => html`
|
||||||
<style>
|
<style>
|
||||||
${popupDemoStyle}
|
${popupDemoStyle}
|
||||||
</style>
|
</style>
|
||||||
<div class="demo-box_positions">
|
<div class="demo-box_placements">
|
||||||
<lion-popup position="top">
|
<lion-popup .placementConfig="${{ placement: 'top' }}">
|
||||||
<div slot="content" class="popup">Its top position</div>
|
<div slot="content" class="popup">Its top placement</div>
|
||||||
<lion-button slot="invoker">Top</lion-button>
|
<lion-button slot="invoker">Top</lion-button>
|
||||||
</lion-popup>
|
</lion-popup>
|
||||||
<lion-popup position="right">
|
<lion-popup .placementConfig="${{ placement: 'right' }}">
|
||||||
<div slot="content" class="popup">Its right position</div>
|
<div slot="content" class="popup">Its right placement</div>
|
||||||
<lion-button slot="invoker">Right</lion-button>
|
<lion-button slot="invoker">Right</lion-button>
|
||||||
</lion-popup>
|
</lion-popup>
|
||||||
<lion-popup position="bottom">
|
<lion-popup .placementConfig="${{ placement: 'bottom' }}">
|
||||||
<div slot="content" class="popup">Its bottom position</div>
|
<div slot="content" class="popup">Its bottom placement</div>
|
||||||
<lion-button slot="invoker">Bottom</lion-button>
|
<lion-button slot="invoker">Bottom</lion-button>
|
||||||
</lion-popup>
|
</lion-popup>
|
||||||
<lion-popup position="left">
|
<lion-popup .placementConfig="${{ placement: 'left' }}">
|
||||||
<div slot="content" class="popup">Its left position</div>
|
<div slot="content" class="popup">Its left placement</div>
|
||||||
<lion-button slot="invoker">Left</lion-button>
|
<lion-button slot="invoker">Left</lion-button>
|
||||||
</lion-popup>
|
</lion-popup>
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
|
)
|
||||||
|
.add(
|
||||||
|
'Override placement configuration',
|
||||||
|
() => html`
|
||||||
|
<style>
|
||||||
|
${popupDemoStyle}
|
||||||
|
</style>
|
||||||
|
<p>Use the Storybook Knobs to dynamically change the placement configuration!</p>
|
||||||
|
<div class="demo-box">
|
||||||
|
<lion-popup
|
||||||
|
.placementConfig="${object('Placement Configuration', {
|
||||||
|
placement: 'bottom-start',
|
||||||
|
positionFixed: true,
|
||||||
|
modifiers: {
|
||||||
|
keepTogether: {
|
||||||
|
enabled: true /* Prevents detachment of content element from reference element */,
|
||||||
|
},
|
||||||
|
preventOverflow: {
|
||||||
|
enabled: true /* disables shifting/sliding behavior on secondary axis */,
|
||||||
|
padding: 16 /* when enabled, this is the viewport-margin for shifting/sliding */,
|
||||||
|
},
|
||||||
|
flip: {
|
||||||
|
boundariesElement: 'viewport',
|
||||||
|
padding: 4 /* viewport-margin for flipping on primary axis */,
|
||||||
|
},
|
||||||
|
offset: {
|
||||||
|
enabled: true,
|
||||||
|
offset: `0, 4px` /* horizontal and vertical margin (distance between popper and referenceElement) */,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})}"
|
||||||
|
>
|
||||||
|
<div slot="content" class="popup">${text('Content text', 'Hello, World!')}</div>
|
||||||
|
<lion-button slot="invoker">${text('Invoker text', 'Click me!')}</lion-button>
|
||||||
|
</lion-popup>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { expect, fixture, html } from '@open-wc/testing';
|
import { expect, fixture, html, aTimeout } from '@open-wc/testing';
|
||||||
|
|
||||||
import '../lion-popup.js';
|
import '../lion-popup.js';
|
||||||
|
|
||||||
|
|
@ -25,6 +25,7 @@ describe('lion-popup', () => {
|
||||||
const eventOnClick = new Event('click');
|
const eventOnClick = new Event('click');
|
||||||
invoker.dispatchEvent(eventOnClick);
|
invoker.dispatchEvent(eventOnClick);
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
|
await aTimeout();
|
||||||
expect(el.querySelector('[slot="content"]').style.display).to.be.equal('inline-block');
|
expect(el.querySelector('[slot="content"]').style.display).to.be.equal('inline-block');
|
||||||
invoker.dispatchEvent(eventOnClick);
|
invoker.dispatchEvent(eventOnClick);
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
|
|
@ -44,6 +45,21 @@ describe('lion-popup', () => {
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
expect(el.querySelector('strong')).to.not.be.undefined;
|
expect(el.querySelector('strong')).to.not.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should respond to dynamically changing the placementConfig', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<lion-popup>
|
||||||
|
<div slot="content" class="popup">Hey there</div>
|
||||||
|
<lion-button slot="invoker">Popup button</lion-button>
|
||||||
|
</lion-popup>
|
||||||
|
`);
|
||||||
|
await el._controller.show();
|
||||||
|
expect(el._controller._popper.options.placement).to.equal('top');
|
||||||
|
el.placementConfig = { placement: 'left' };
|
||||||
|
// placementConfig setter calls updateConfig method which recreates popper instance and this process is async..
|
||||||
|
await aTimeout();
|
||||||
|
expect(el._controller._popper.options.placement).to.equal('left');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Accessibility', () => {
|
describe('Accessibility', () => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue