import { expect, fixture, html, fixtureSync } from '@open-wc/testing'; import Popper from 'popper.js/dist/esm/popper.min.js'; import { OverlayController } from '../src/OverlayController.js'; import { normalizeTransformStyle } from '../test-helpers/local-positioning-helpers.js'; const withLocalTestConfig = () => ({ placementMode: 'local', contentNode: fixtureSync(html`
my content
`), invokerNode: fixtureSync(html`
Invoker
`), }); describe('Local Positioning', () => { describe('Nodes', () => { // TODO: check if wanted/needed it.skip('sets display to inline-block for contentNode by default', async () => { const invokerNode = await fixture(html`
Invoker
`); const node = document.createElement('div'); node.innerHTML = '
Content
'; const ctrl = new OverlayController({ ...withLocalTestConfig(), contentNode: node, invokerNode, }); const el = await fixture(html`
${ctrl.invokerNode} ${ctrl.content}
`); await ctrl.show(); const contentWrapper = el.querySelector('#content').parentElement; expect(contentWrapper.style.display).to.equal('inline-block'); }); }); // Please use absolute positions in the tests below to prevent the HTML generated by // the test runner from interfering. describe('Positioning', () => { it('creates a Popper instance on the controller when shown, keeps it when hidden', async () => { const ctrl = new OverlayController({ ...withLocalTestConfig(), }); await ctrl.show(); expect(ctrl._popper).to.be.an.instanceof(Popper); expect(ctrl._popper.modifiers).to.exist; await ctrl.hide(); expect(ctrl._popper).to.be.an.instanceof(Popper); expect(ctrl._popper.modifiers).to.exist; }); it('positions correctly', async () => { // smoke test for integration of popper const ctrl = new OverlayController({ ...withLocalTestConfig(), contentNode: fixtureSync(html`
`), invokerNode: fixtureSync(html`
`), }); await fixture(html`
${ctrl.invokerNode}${ctrl.content}
`); await ctrl.show(); expect(normalizeTransformStyle(ctrl.content.style.transform)).to.equal( 'translate3d(-30px, -38px, 0px)', 'translate3d should be -30px [to center = (80 - 20)/2*-1] -38px [to place above = 30 + 8 default padding]', ); }); it('uses top as the default placement', async () => { const ctrl = new OverlayController({ ...withLocalTestConfig(), contentNode: fixtureSync(html`
`), invokerNode: fixtureSync(html`
ctrl.show()}>
`), }); await fixture(html`
${ctrl.invokerNode}${ctrl.content}
`); await ctrl.show(); expect(ctrl.content.getAttribute('x-placement')).to.equal('top'); }); it('positions to preferred place if placement is set and space is available', async () => { const ctrl = new OverlayController({ ...withLocalTestConfig(), contentNode: fixtureSync(html`
`), invokerNode: fixtureSync(html`
ctrl.show()}>
`), popperConfig: { placement: 'left-start', }, }); await fixture(html`
${ctrl.invokerNode}${ctrl.content}
`); await ctrl.show(); expect(ctrl.content.getAttribute('x-placement')).to.equal('left-start'); }); it('positions to different place if placement is set and no space is available', async () => { const ctrl = new OverlayController({ ...withLocalTestConfig(), contentNode: fixtureSync(html`
invoker
`), invokerNode: fixtureSync(html`
ctrl.show()}> content
`), popperConfig: { placement: 'top-start', }, }); await fixture(html`
${ctrl.invokerNode}${ctrl.content}
`); await ctrl.show(); expect(ctrl.content.getAttribute('x-placement')).to.equal('bottom-start'); }); it('allows the user to override default Popper modifiers', async () => { const ctrl = new OverlayController({ ...withLocalTestConfig(), contentNode: fixtureSync(html`
`), invokerNode: fixtureSync(html`
ctrl.show()}>
`), popperConfig: { modifiers: { keepTogether: { enabled: false, }, offset: { enabled: true, offset: `0, 16px`, }, }, }, }); await fixture(html`
${ctrl.invokerNode}${ctrl.content}
`); await ctrl.show(); const keepTogether = ctrl._popper.modifiers.find(item => item.name === 'keepTogether'); const offset = ctrl._popper.modifiers.find(item => item.name === 'offset'); expect(keepTogether.enabled).to.be.false; expect(offset.enabled).to.be.true; expect(offset.offset).to.equal('0, 16px'); }); it('positions the Popper element correctly on show', async () => { const ctrl = new OverlayController({ ...withLocalTestConfig(), contentNode: fixtureSync(html`
`), invokerNode: fixtureSync(html`
ctrl.show()}>
`), popperConfig: { placement: 'top', }, }); await fixture(html`
${ctrl.invokerNode}${ctrl.content}
`); await ctrl.show(); expect(normalizeTransformStyle(ctrl.content.style.transform)).to.equal( 'translate3d(10px, -28px, 0px)', 'Popper positioning values', ); await ctrl.hide(); await ctrl.show(); expect(normalizeTransformStyle(ctrl.content.style.transform)).to.equal( 'translate3d(10px, -28px, 0px)', 'Popper positioning values should be identical after hiding and showing', ); }); // TODO: dom get's removed when hidden so no dom node to update placement it('updates placement properly even during hidden state', async () => { const ctrl = new OverlayController({ ...withLocalTestConfig(), contentNode: fixtureSync(html`
`), invokerNode: fixtureSync(html`
ctrl.show()}>
`), popperConfig: { placement: 'top', modifiers: { offset: { enabled: true, offset: '0, 10px', }, }, }, }); await fixture(html`
${ctrl.invokerNode} ${ctrl.content}
`); await ctrl.show(); expect(normalizeTransformStyle(ctrl.content.style.transform)).to.equal( 'translate3d(10px, -30px, 0px)', 'Popper positioning values', ); await ctrl.hide(); await ctrl.updatePopperConfig({ modifiers: { offset: { enabled: true, offset: '0, 20px', }, }, }); await ctrl.show(); expect(ctrl._popper.options.modifiers.offset.offset).to.equal('0, 20px'); expect(normalizeTransformStyle(ctrl.content.style.transform)).to.equal( 'translate3d(10px, -40px, 0px)', 'Popper positioning Y value should be 10 less than previous, due to the added extra 10px offset', ); }); it('updates positioning correctly during shown state when config gets updated', async () => { const ctrl = new OverlayController({ ...withLocalTestConfig(), contentNode: fixtureSync(html`
`), invokerNode: fixtureSync(html`
ctrl.show()}> Invoker
`), popperConfig: { placement: 'top', modifiers: { offset: { enabled: true, offset: '0, 10px', }, }, }, }); await fixture(html`
${ctrl.invokerNode} ${ctrl.content}
`); await ctrl.show(); expect(normalizeTransformStyle(ctrl.content.style.transform)).to.equal( 'translate3d(10px, -30px, 0px)', 'Popper positioning values', ); await ctrl.updatePopperConfig({ modifiers: { offset: { enabled: true, offset: '0, 20px', }, }, }); expect(normalizeTransformStyle(ctrl.content.style.transform)).to.equal( 'translate3d(10px, -40px, 0px)', 'Popper positioning Y value should be 10 less than previous, due to the added extra 10px offset', ); }); it('can set the contentNode minWidth as the invokerNode width', async () => { const invokerNode = await fixture(html`
invoker
`); const ctrl = new OverlayController({ ...withLocalTestConfig(), inheritsReferenceWidth: 'min', invokerNode, }); await ctrl.show(); expect(ctrl.content.style.minWidth).to.equal('60px'); }); it('can set the contentNode maxWidth as the invokerNode width', async () => { const invokerNode = await fixture(html`
invoker
`); const ctrl = new OverlayController({ ...withLocalTestConfig(), inheritsReferenceWidth: 'max', invokerNode, }); await ctrl.show(); expect(ctrl.content.style.maxWidth).to.equal('60px'); }); it('can set the contentNode width as the invokerNode width', async () => { const invokerNode = await fixture(html`
invoker
`); const ctrl = new OverlayController({ ...withLocalTestConfig(), inheritsReferenceWidth: 'full', invokerNode, }); await ctrl.show(); expect(ctrl.content.style.width).to.equal('60px'); }); }); });