From df1bd7dc45758e61d369e64df59146e49313eb44 Mon Sep 17 00:00:00 2001 From: atomicbrainman <> Date: Thu, 14 Feb 2019 14:05:14 +0200 Subject: [PATCH 1/2] Add flipVariationsByContent option to flip modifier --- packages/popper/src/modifiers/flip.js | 14 +++- packages/popper/src/modifiers/index.js | 16 ++++ packages/popper/tests/functional/flips.js | 89 +++++++++++++++++++++-- 3 files changed, 111 insertions(+), 8 deletions(-) diff --git a/packages/popper/src/modifiers/flip.js b/packages/popper/src/modifiers/flip.js index 847ee2da6f..109b4e974e 100644 --- a/packages/popper/src/modifiers/flip.js +++ b/packages/popper/src/modifiers/flip.js @@ -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; diff --git a/packages/popper/src/modifiers/index.js b/packages/popper/src/modifiers/index.js index db8fd7d797..3fd648a8c6 100644 --- a/packages/popper/src/modifiers/index.js +++ b/packages/popper/src/modifiers/index.js @@ -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, }, /** diff --git a/packages/popper/tests/functional/flips.js b/packages/popper/tests/functional/flips.js index 5750299166..4d7b02c3ca 100644 --- a/packages/popper/tests/functional/flips.js +++ b/packages/popper/tests/functional/flips.js @@ -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'; @@ -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; } @@ -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 From 075fdca6bee162717b7f84fdbb6e6b133aec5cca Mon Sep 17 00:00:00 2001 From: atomicbrainman <> Date: Thu, 14 Feb 2019 14:40:42 +0200 Subject: [PATCH 2/2] fix: type definitions for flipVariations and flipVariationsByContent in flip modifier --- packages/popper/index.d.ts | 2 ++ packages/popper/index.js.flow | 2 ++ 2 files changed, 4 insertions(+) diff --git a/packages/popper/index.d.ts b/packages/popper/index.d.ts index a17dee74c2..07147c51d0 100644 --- a/packages/popper/index.d.ts +++ b/packages/popper/index.d.ts @@ -72,6 +72,8 @@ declare namespace Popper { behavior?: Behavior | Position[], padding?: number | Padding, boundariesElement?: Boundary | Element, + flipVariations?: boolean, + flipVariationsByContent?: boolean, }; inner?: BaseModifier; hide?: BaseModifier; diff --git a/packages/popper/index.js.flow b/packages/popper/index.js.flow index 5ca317f46c..b950d3867b 100644 --- a/packages/popper/index.js.flow +++ b/packages/popper/index.js.flow @@ -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,