Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'flipVariationsByContent' option to flip modifier #754

Merged
merged 2 commits into from Feb 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/popper/index.d.ts
Expand Up @@ -72,6 +72,8 @@ declare namespace Popper {
behavior?: Behavior | Position[],
padding?: number | Padding,
boundariesElement?: Boundary | Element,
flipVariations?: boolean,
flipVariationsByContent?: boolean,
};
inner?: BaseModifier;
hide?: BaseModifier;
Expand Down
2 changes: 2 additions & 0 deletions packages/popper/index.js.flow
Expand Up @@ -83,6 +83,8 @@ declare module 'popper.js' {
behavior?: Behavior | Position[],
padding?: number | Padding,
boundariesElement?: Boundary | Element,
flipVariations?: boolean,
flipVariationsByContent?: boolean,
},
inner?: BaseModifier,
hide?: BaseModifier,
Expand Down
14 changes: 13 additions & 1 deletion packages/popper/src/modifiers/flip.js
Expand Up @@ -95,13 +95,25 @@ export default function flip(data, options) {

// flip the variation if required
const isVertical = ['top', 'bottom'].indexOf(placement) !== -1;
const flippedVariation =

// flips variation if reference element overflows boundaries
const flippedVariationByRef =
!!options.flipVariations &&
((isVertical && variation === 'start' && overflowsLeft) ||
(isVertical && variation === 'end' && overflowsRight) ||
(!isVertical && variation === 'start' && overflowsTop) ||
(!isVertical && variation === 'end' && overflowsBottom));

// flips variation if popper content overflows boundaries
const flippedVariationByContent =
!!options.flipVariationsByContent &&
((isVertical && variation === 'start' && overflowsRight) ||
(isVertical && variation === 'end' && overflowsLeft) ||
(!isVertical && variation === 'start' && overflowsBottom) ||
(!isVertical && variation === 'end' && overflowsTop));

const flippedVariation = flippedVariationByRef || flippedVariationByContent;

if (overlapsRef || overflowsBoundaries || flippedVariation) {
// this boolean to detect any flip loop
data.flipped = true;
Expand Down
16 changes: 16 additions & 0 deletions packages/popper/src/modifiers/index.js
Expand Up @@ -220,6 +220,22 @@ export default {
* (except if `keepTogether` is enabled)
*/
boundariesElement: 'viewport',
/**
* @prop {Boolean} flipVariations=false
* The popper will switch placement variation between `-start` and `-end` when
* the reference element overlaps its boundaries.
*
* The original placement should have a set variation.
*/
flipVariations: false,
/**
* @prop {Boolean} flipVariationsByContent=false
* The popper will switch placement variation between `-start` and `-end` when
* the popper element overlaps its reference boundaries.
*
* The original placement should have a set variation.
*/
flipVariationsByContent: false,
},

/**
Expand Down
89 changes: 82 additions & 7 deletions packages/popper/tests/functional/flips.js
Expand Up @@ -117,14 +117,19 @@ const isIPHONE = window.navigator.userAgent.match(/iPhone/i);
});
});
});
function getSecondaryMargin(val) {

function getSecondaryMarginByReference(val) {
return (val === 'start' ? '-' : '') + '100px';
}

function getSecondaryMarginByContent(val) {
return val === 'start' ? '200px' : '50px';
}

Object.keys(flippingVariations).forEach(val => {
it(`(variations) should flip from ${val} to ${flippingVariations[
it(`(variations)(by reference) should flip from ${val} to ${flippingVariations[
val
]} if boundariesElement is set`, done => {
]} if boundariesElement is set`, done => {
const relative = document.createElement('div');
relative.style.margin = '100px 300px';
relative.style.height = '300px';
Expand All @@ -144,18 +149,18 @@ const isIPHONE = window.navigator.userAgent.match(/iPhone/i);
switch (valElems[0]) {
case 'top':
ref.style.top = '100px';
ref.style.left = getSecondaryMargin(valElems[1]);
ref.style.left = getSecondaryMarginByReference(valElems[1]);
break;
case 'bottom':
ref.style.bottom = '100px';
ref.style.left = getSecondaryMargin(valElems[1]);
ref.style.left = getSecondaryMarginByReference(valElems[1]);
break;
case 'left':
ref.style.top = getSecondaryMargin(valElems[1]);
ref.style.top = getSecondaryMarginByReference(valElems[1]);
ref.style.left = '200px';
break;
case 'right':
ref.style.top = getSecondaryMargin(valElems[1]);
ref.style.top = getSecondaryMarginByReference(valElems[1]);
ref.style.right = '200px';
break;
}
Expand Down Expand Up @@ -185,6 +190,76 @@ const isIPHONE = window.navigator.userAgent.match(/iPhone/i);
});
});

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
Expand Down