501 lines
16 KiB
JavaScript
501 lines
16 KiB
JavaScript
|
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 = `
|
||
|
<div id="s1" style="height: 3000px; background: red;">
|
||
|
<div id="reference" style="background: pink; margin-top: 200px">reference</div>
|
||
|
<div id="popper" style="background: purple">popper</div>
|
||
|
</div>
|
||
|
`;
|
||
|
|
||
|
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 = `
|
||
|
<style>body { margin: 0; }</style>
|
||
|
<div style="background: grey; height: 100vh;">
|
||
|
<div
|
||
|
id="reference"
|
||
|
style="background: yellow; width: 200px; height: 80vh;"
|
||
|
>
|
||
|
ref
|
||
|
</div>
|
||
|
<div
|
||
|
id="popper"
|
||
|
style="background: purple; width: 95vw; height: 30px;"
|
||
|
>
|
||
|
popper
|
||
|
</div>
|
||
|
</div>
|
||
|
`;
|
||
|
|
||
|
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 = `
|
||
|
<div id="reference" style="position: fixed; top: 50px; left: 1px; background: pink">reference</div>
|
||
|
<div id="popper" style="background: purple; width: 5px">popper</div>
|
||
|
`;
|
||
|
|
||
|
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 = `
|
||
|
<div id="reference" style="position: fixed; top: 50px; right: 1px; background: pink">reference</div>
|
||
|
<div id="popper" style="background: purple;">popper</div>
|
||
|
`;
|
||
|
|
||
|
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 = `
|
||
|
<div id="reference" style="position: fixed; top: 1px; left: 50px; background: pink">reference</div>
|
||
|
<div id="popper" style="background: purple;">popper</div>
|
||
|
`;
|
||
|
|
||
|
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 = `
|
||
|
<div id="reference" style="position: fixed; bottom: 1px; right: 50px; background: pink">reference</div>
|
||
|
<div id="popper" style="background: purple;">popper</div>
|
||
|
`;
|
||
|
|
||
|
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 = `
|
||
|
<div id="container" style="position: relative; margin-left: 100px; margin-top:100px; height: 100vh; transform: translateZ(0)">
|
||
|
<div style="height:100%"></div>
|
||
|
<div id="reference" style="background: pink">reference</div>
|
||
|
<div id="popper" style="background: purple;">popper</div>
|
||
|
<div style="height:200%"></div>
|
||
|
</div>
|
||
|
`;
|
||
|
|
||
|
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 = `
|
||
|
<div id="reference" style="background: pink; margin-top: 100vh; width: 100px; height: 100px;">reference</div>
|
||
|
<div id="popper" style="background: purple; width: 100px; height: 100px;">popper</div>
|
||
|
`;
|
||
|
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();
|
||
|
},
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
});
|