import Popper from '../../src/index.js'; import getOppositePlacement from '../../src/utils/getOppositePlacement'; // Utils import appendNewPopper from '@popperjs/test-utils/utils/appendNewPopper'; import appendNewRef from '@popperjs/test-utils/utils/appendNewRef'; import simulateScroll from '@popperjs/test-utils/utils/simulateScroll'; import getRect from '../utils/getRect'; const jasmineWrapper = document.getElementById('jasmineWrapper'); const isIPHONE = window.navigator.userAgent.match(/iPhone/i); [true, false].forEach((positionFixed) => { describe('[flipping]' + (positionFixed ? ' Fixed' : ''), () => { beforeEach(function(){ Popper.Defaults.positionFixed = positionFixed; }); it('should flip from top to bottom', done => { const ref = appendNewRef(1, 'ref', jasmineWrapper); ref.style.marginLeft = '100px'; const popper = appendNewPopper(2, 'popper'); new Popper(ref, popper, { placement: 'top', onCreate: data => { expect(data.placement).toBe('bottom'); data.instance.destroy(); done(); }, }); }); const flippingDefault = [ 'top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-start', 'left-end', 'right', 'right-start', 'right-end', ]; const flippingVariations = { 'top-start': 'top-end', 'top-end': 'top-start', 'bottom-start': 'bottom-end', 'bottom-end': 'bottom-start', 'left-start': 'left-end', 'left-end': 'left-start', 'right-start': 'right-end', 'right-end': 'right-start', }; flippingDefault.forEach(val => { it(`should flip from ${val} to ${getOppositePlacement( val )} if boundariesElement is set`, done => { const relative = document.createElement('div'); relative.style.margin = '100px 300px'; relative.style.height = '100px'; relative.style.width = '100px'; relative.style.background = '#ffff00'; jasmineWrapper.appendChild(relative); const ref = appendNewRef(1, 'ref', relative); ref.style.width = '70px'; ref.style.height = '70px'; ref.style.background = 'green'; // ref.style.marginTop = '100px'; const popper = appendNewPopper(2, 'popper'); new Popper(ref, popper, { placement: val, modifiers: { flip: { boundariesElement: relative }, }, onCreate: data => { expect(data.flipped).toBe(true); expect(data.placement).toBe(getOppositePlacement(val)); expect(data.originalPlacement).toBe(val); data.instance.destroy(); done(); }, }); }); it('should NOT flip if there is no boundariesElement', done => { const relative = document.createElement('div'); relative.style.margin = '100px 300px'; relative.style.height = '100px'; relative.style.width = '100px'; relative.style.background = '#ffff00'; jasmineWrapper.appendChild(relative); const ref = appendNewRef(1, 'ref', relative); ref.style.width = '70px'; ref.style.height = '70px'; ref.style.background = 'green'; // ref.style.marginTop = '100px'; const popper = appendNewPopper(3, 'popper'); new Popper(ref, popper, { placement: val, onCreate: data => { expect(data.flipped).not.toBe(true); expect(data.placement).toBe(val); expect(data.originalPlacement).toBe(val); data.instance.destroy(); done(); }, }); }); }); function getSecondaryMarginByReference(val) { return (val === 'start' ? '-' : '') + '100px'; } function getSecondaryMarginByContent(val) { return val === 'start' ? '200px' : '50px'; } Object.keys(flippingVariations).forEach(val => { it(`(variations)(by reference) should flip from ${val} to ${flippingVariations[ val ]} if boundariesElement is set`, done => { const relative = document.createElement('div'); relative.style.margin = '100px 300px'; relative.style.height = '300px'; relative.style.width = '300px'; relative.style.background = '#ffff00'; relative.style.position = 'relative'; jasmineWrapper.appendChild(relative); const ref = appendNewRef(1, 'ref', relative); ref.style.width = '200px'; ref.style.height = '200px'; ref.style.background = 'green'; ref.style.position = 'absolute'; ref.style.zIndex = '10'; const valElems = val.split('-'); switch (valElems[0]) { case 'top': ref.style.top = '100px'; ref.style.left = getSecondaryMarginByReference(valElems[1]); break; case 'bottom': ref.style.bottom = '100px'; ref.style.left = getSecondaryMarginByReference(valElems[1]); break; case 'left': ref.style.top = getSecondaryMarginByReference(valElems[1]); ref.style.left = '200px'; break; case 'right': ref.style.top = getSecondaryMarginByReference(valElems[1]); ref.style.right = '200px'; break; } const popper = appendNewPopper(2, 'popper'); new Popper(ref, popper, { placement: val, modifiers: { preventOverflow: { enabled: true, escapeWithReference: true, }, flip: { flipVariations: true, boundariesElement: relative, }, }, onCreate: data => { expect(data.flipped).toBe(true); expect(data.placement).toBe(flippingVariations[val]); expect(data.originalPlacement).toBe(val); data.instance.destroy(); done(); }, }); }); }); Object.keys(flippingVariations).forEach(val => { it(`(variations)(by content) should flip from ${val} to ${flippingVariations[ val ]} if boundariesElement is set`, done => { const relative = document.createElement('div'); relative.style.margin = '100px 300px'; relative.style.height = '300px'; relative.style.width = '300px'; relative.style.background = '#ffff00'; relative.style.position = 'relative'; jasmineWrapper.appendChild(relative); const ref = appendNewRef(1, 'ref', relative); ref.style.width = '50px'; ref.style.height = '50px'; ref.style.background = 'green'; ref.style.position = 'absolute'; ref.style.zIndex = '10'; const valElems = val.split('-'); switch (valElems[0]) { case 'top': ref.style.bottom = '20px'; ref.style.left = getSecondaryMarginByContent(valElems[1]); break; case 'bottom': ref.style.top = '20px'; ref.style.left = getSecondaryMarginByContent(valElems[1]); break; case 'left': ref.style.top = getSecondaryMarginByContent(valElems[1]); ref.style.left = '200px'; break; case 'right': ref.style.top = getSecondaryMarginByContent(valElems[1]); ref.style.right = '200px'; break; } const large = document.createElement('div'); large.style.width = '150px'; large.style.height = '150px'; large.style.backgroundColor = 'blue'; const popper = appendNewPopper(2, 'popper'); popper.appendChild(large); new Popper(ref, popper, { placement: val, modifiers: { preventOverflow: { enabled: true, escapeWithReference: true, }, flip: { flipVariationsByContent: true, boundariesElement: relative, }, }, onCreate: data => { expect(data.flipped).toBe(true); expect(data.placement).toBe(flippingVariations[val]); expect(data.originalPlacement).toBe(val); data.instance.destroy(); done(); }, }); }); }); it('flips to opposite side when rendered inside a positioned parent', done => { const page = document.createElement('div'); page.style.paddingTop = '110vh'; // Simulates page content page.style.background = 'lightskyblue'; jasmineWrapper.appendChild(page); const parent = document.createElement('div'); parent.style.position = 'relative'; parent.style.background = 'yellow'; page.appendChild(parent); const ref = appendNewRef(1, 'reference', parent); const popper = appendNewPopper(2, 'popper', parent); new Popper(ref, popper, { onCreate: data => { simulateScroll(page, { scrollTop: '110vh', delay: 10 }); const popperRect = popper.getBoundingClientRect(); const refRect = ref.getBoundingClientRect(); const arrowSize = 5; expect(data.flipped).toBe(true); expect(popperRect.bottom + arrowSize).toBeApprox(refRect.top); data.instance.destroy(); done(); }, }); }); it('flips to bottom when hits top viewport edge', done => { if (isIPHONE) { pending(); } jasmineWrapper.innerHTML = `
reference
popper
`; const reference = document.getElementById('reference'); const popper = document.getElementById('popper'); new Popper(reference, popper, { placement: 'top', onCreate() { simulateScroll(document.body, { scrollTop: 200 }); }, onUpdate(data) { expect(getRect(popper).top).toBeApprox(getRect(reference).bottom); data.instance.destroy(); done(); }, }); }); it('flip properly with large popper width', done => { jasmineWrapper.innerHTML = `
ref
popper
`; const reference = document.getElementById('reference'); const popper = document.getElementById('popper'); new Popper(reference, popper, { placement: 'auto', onCreate(data) { expect(data.placement).toBe('bottom'); expect(getRect(reference).bottom).toBeApprox(getRect(popper).top); data.instance.destroy(); done(); }, }); }); it('init popper on fixed reference aligned to left and flips to right', done => { jasmineWrapper.innerHTML = `
reference
popper
`; const reference = document.getElementById('reference'); const popper = document.getElementById('popper'); new Popper(reference, popper, { placement: 'left-start', onCreate() { expect(popper.getBoundingClientRect().top).toBeApprox( reference.getBoundingClientRect().top ); expect(popper.getBoundingClientRect().left).toBeApprox( reference.getBoundingClientRect().right ); done(); }, }); }); it('init popper on fixed reference aligned to right and flips to left', done => { jasmineWrapper.innerHTML = `
reference
popper
`; const reference = document.getElementById('reference'); const popper = document.getElementById('popper'); new Popper(reference, popper, { placement: 'right-start', onCreate() { expect(popper.getBoundingClientRect().top).toBeApprox( reference.getBoundingClientRect().top ); expect(popper.getBoundingClientRect().right).toBeApprox( reference.getBoundingClientRect().left ); done(); }, }); }); it('init popper on fixed reference aligned to top and flips to bottom', done => { jasmineWrapper.innerHTML = `
reference
popper
`; const reference = document.getElementById('reference'); const popper = document.getElementById('popper'); new Popper(reference, popper, { placement: 'top-start', onCreate() { expect(popper.getBoundingClientRect().top).toBeApprox( reference.getBoundingClientRect().bottom ); expect(popper.getBoundingClientRect().left).toBeApprox( reference.getBoundingClientRect().left ); done(); }, }); }); it('init popper on fixed reference aligned to bottom and flips to top', done => { jasmineWrapper.innerHTML = `
reference
popper
`; const reference = document.getElementById('reference'); const popper = document.getElementById('popper'); new Popper(reference, popper, { placement: 'bottom-start', onCreate() { expect(popper.getBoundingClientRect().bottom).toBeApprox( reference.getBoundingClientRect().top ); expect(popper.getBoundingClientRect().left).toBeApprox( reference.getBoundingClientRect().left ); done(); }, }); }); it('init popper on transformed parent flips to top', done => { jasmineWrapper.innerHTML = `
reference
popper
`; const reference = document.getElementById('reference'); const popper = document.getElementById('popper'); new Popper(reference, popper, { placement: 'bottom-start', onCreate() { expect(popper.getBoundingClientRect().bottom).toBeApprox( reference.getBoundingClientRect().top ); expect(popper.getBoundingClientRect().left).toBeApprox( reference.getBoundingClientRect().left ); done(); }, }); }); // This one will fail on IE10 - See #211 xit('properly positions a bottom popper inside very high body', done => { jasmineWrapper.innerHTML = `
reference
popper
`; document.body.style.height = '200vh'; const reference = document.getElementById('reference'); const popper = document.getElementById('popper'); new Popper(reference, popper, { onCreate() { simulateScroll(document.body, { scrollTop: getRect(document.body).height, delay: 50, }); }, onUpdate(data) { expect(getRect(popper).top).toBeApprox(getRect(reference).bottom); data.instance.destroy(); document.body.style.cssText = null; done(); }, }); }); }); });