Skip to content

Commit 1e6d60d

Browse files
Joren BroekemaJoren Broekema
authored andcommitted
feat(popup): change API to popper based
1 parent 22357ea commit 1e6d60d

File tree

3 files changed

+96
-25
lines changed

3 files changed

+96
-25
lines changed

packages/popup/src/LionPopup.js

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,44 @@ import { overlays, LocalOverlayController } from '@lion/overlays';
44
export class LionPopup extends UpdatingElement {
55
static get properties() {
66
return {
7-
position: {
8-
type: String,
7+
placementConfig: {
8+
type: Object,
99
},
1010
};
1111
}
1212

13+
get placementConfig() {
14+
return this._placementConfig;
15+
}
16+
17+
set placementConfig(config) {
18+
this._placementConfig = {
19+
...this._placementConfig,
20+
...config,
21+
};
22+
23+
if (this._controller && this._controller._popper) {
24+
this._controller.updatePlacementConfig(this._placementConfig);
25+
}
26+
}
27+
1328
connectedCallback() {
1429
super.connectedCallback();
15-
this.contenNode = this.querySelector('[slot="content"]');
30+
this.contentNode = this.querySelector('[slot="content"]');
1631
this.invokerNode = this.querySelector('[slot="invoker"]');
1732

18-
this._popup = overlays.add(
33+
this._controller = overlays.add(
1934
new LocalOverlayController({
2035
hidesOnEsc: true,
2136
hidesOnOutsideClick: true,
22-
placement: this.position,
23-
contentNode: this.contenNode,
37+
placementConfig: this.placementConfig,
38+
contentNode: this.contentNode,
2439
invokerNode: this.invokerNode,
2540
}),
2641
);
27-
this._show = () => this._popup.show();
28-
this._hide = () => this._popup.hide();
29-
this._toggle = () => this._popup.toggle();
42+
this._show = () => this._controller.show();
43+
this._hide = () => this._controller.hide();
44+
this._toggle = () => this._controller.toggle();
3045

3146
this.invokerNode.addEventListener('click', this._toggle);
3247
}

packages/popup/stories/index.stories.js

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { storiesOf, html } from '@open-wc/demoing-storybook';
1+
import { storiesOf, html, withKnobs, object, text } from '@open-wc/demoing-storybook';
22
import { css } from '@lion/core';
33

44
import '@lion/icon/lion-icon.js';
@@ -11,11 +11,11 @@ const popupDemoStyle = css`
1111
background-color: white;
1212
border-radius: 2px;
1313
border: 1px solid grey;
14-
margin: 250px;
14+
margin: 250px 0 0 250px;
1515
padding: 8px;
1616
}
1717
18-
.demo-box_positions {
18+
.demo-box_placements {
1919
display: flex;
2020
flex-direction: column;
2121
width: 173px;
@@ -40,6 +40,8 @@ const popupDemoStyle = css`
4040
background-color: black;
4141
border-radius: 4px;
4242
padding: 8px;
43+
/* To display on top of elements with no z-index that are appear later in the DOM */
44+
z-index: 1;
4345
}
4446
4547
@media (max-width: 480px) {
@@ -50,43 +52,81 @@ const popupDemoStyle = css`
5052
`;
5153

5254
storiesOf('Local Overlay System|Popup', module)
55+
.addDecorator(withKnobs)
5356
.add(
5457
'Button popup',
5558
() => html`
5659
<style>
5760
${popupDemoStyle}
5861
</style>
5962
<div class="demo-box">
60-
<lion-popup position="right">
61-
<div slot="content" class="popup">hey there</div>
63+
<lion-popup .placementConfig="${{ placement: 'top' }}">
64+
<div slot="content" class="popup">Hello there!</div>
6265
<lion-button slot="invoker">Popup</lion-button>
6366
</lion-popup>
6467
</div>
6568
`,
6669
)
6770
.add(
68-
'positions',
71+
'placements',
6972
() => html`
7073
<style>
7174
${popupDemoStyle}
7275
</style>
73-
<div class="demo-box_positions">
74-
<lion-popup position="top">
75-
<div slot="content" class="popup">Its top position</div>
76+
<div class="demo-box_placements">
77+
<lion-popup .placementConfig="${{ placement: 'top' }}">
78+
<div slot="content" class="popup">Its top placement</div>
7679
<lion-button slot="invoker">Top</lion-button>
7780
</lion-popup>
78-
<lion-popup position="right">
79-
<div slot="content" class="popup">Its right position</div>
81+
<lion-popup .placementConfig="${{ placement: 'right' }}">
82+
<div slot="content" class="popup">Its right placement</div>
8083
<lion-button slot="invoker">Right</lion-button>
8184
</lion-popup>
82-
<lion-popup position="bottom">
83-
<div slot="content" class="popup">Its bottom position</div>
85+
<lion-popup .placementConfig="${{ placement: 'bottom' }}">
86+
<div slot="content" class="popup">Its bottom placement</div>
8487
<lion-button slot="invoker">Bottom</lion-button>
8588
</lion-popup>
86-
<lion-popup position="left">
87-
<div slot="content" class="popup">Its left position</div>
89+
<lion-popup .placementConfig="${{ placement: 'left' }}">
90+
<div slot="content" class="popup">Its left placement</div>
8891
<lion-button slot="invoker">Left</lion-button>
8992
</lion-popup>
9093
</div>
9194
`,
95+
)
96+
.add(
97+
'Override placement configuration',
98+
() => html`
99+
<style>
100+
${popupDemoStyle}
101+
</style>
102+
<p>Use the Storybook Knobs to dynamically change the placement configuration!</p>
103+
<div class="demo-box">
104+
<lion-popup
105+
.placementConfig="${object('Placement Configuration', {
106+
placement: 'bottom-start',
107+
positionFixed: true,
108+
modifiers: {
109+
keepTogether: {
110+
enabled: true /* Prevents detachment of content element from reference element */,
111+
},
112+
preventOverflow: {
113+
enabled: true /* disables shifting/sliding behavior on secondary axis */,
114+
padding: 16 /* when enabled, this is the viewport-margin for shifting/sliding */,
115+
},
116+
flip: {
117+
boundariesElement: 'viewport',
118+
padding: 4 /* viewport-margin for flipping on primary axis */,
119+
},
120+
offset: {
121+
enabled: true,
122+
offset: `0, 4px` /* horizontal and vertical margin (distance between popper and referenceElement) */,
123+
},
124+
},
125+
})}"
126+
>
127+
<div slot="content" class="popup">${text('Content text', 'Hello, World!')}</div>
128+
<lion-button slot="invoker">${text('Invoker text', 'Click me!')}</lion-button>
129+
</lion-popup>
130+
</div>
131+
`,
92132
);

packages/popup/test/lion-popup.test.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { expect, fixture, html } from '@open-wc/testing';
1+
import { expect, fixture, html, aTimeout } from '@open-wc/testing';
22

33
import '../lion-popup.js';
44

@@ -25,6 +25,7 @@ describe('lion-popup', () => {
2525
const eventOnClick = new Event('click');
2626
invoker.dispatchEvent(eventOnClick);
2727
await el.updateComplete;
28+
await aTimeout();
2829
expect(el.querySelector('[slot="content"]').style.display).to.be.equal('inline-block');
2930
invoker.dispatchEvent(eventOnClick);
3031
await el.updateComplete;
@@ -44,6 +45,21 @@ describe('lion-popup', () => {
4445
await el.updateComplete;
4546
expect(el.querySelector('strong')).to.not.be.undefined;
4647
});
48+
49+
it('should respond to dynamically changing the placementConfig', async () => {
50+
const el = await fixture(html`
51+
<lion-popup>
52+
<div slot="content" class="popup">Hey there</div>
53+
<lion-button slot="invoker">Popup button</lion-button>
54+
</lion-popup>
55+
`);
56+
await el._controller.show();
57+
expect(el._controller._popper.options.placement).to.equal('top');
58+
el.placementConfig = { placement: 'left' };
59+
// placementConfig setter calls updateConfig method which recreates popper instance and this process is async..
60+
await aTimeout();
61+
expect(el._controller._popper.options.placement).to.equal('left');
62+
});
4763
});
4864

4965
describe('Accessibility', () => {

0 commit comments

Comments
 (0)