Skip to content

Commit

Permalink
feat: add 'flipVariationsByContent' option to flip modifier (#754)
Browse files Browse the repository at this point in the history
  • Loading branch information
atomicbrainman authored and FezVrasta committed Feb 28, 2019
1 parent c1432d4 commit bc269c8
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 8 deletions.
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

0 comments on commit bc269c8

Please sign in to comment.