From 730f49c82900ee9981a94d8bbca8c868498c1bf5 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Thu, 24 Feb 2022 13:54:58 +0100 Subject: [PATCH 1/2] feat: port isIntersectingModifier to v9 --- .../src/stories/Positioning.stories.tsx | 85 +++++++++++++++++++ .../src/isIntersectingModifier.ts | 27 ++++++ packages/react-positioning/src/usePopper.ts | 3 + 3 files changed, 115 insertions(+) create mode 100644 packages/react-positioning/src/isIntersectingModifier.ts diff --git a/apps/vr-tests-react-components/src/stories/Positioning.stories.tsx b/apps/vr-tests-react-components/src/stories/Positioning.stories.tsx index 0e656c6e7e03cc..011ad534f042e3 100644 --- a/apps/vr-tests-react-components/src/stories/Positioning.stories.tsx +++ b/apps/vr-tests-react-components/src/stories/Positioning.stories.tsx @@ -11,6 +11,7 @@ import { useMergedRefs } from '@fluentui/react-utilities'; import { tokens } from '@fluentui/react-theme'; import { storiesOf } from '@storybook/react'; import { useFluent } from '@fluentui/react-shared-contexts'; +import Screener from 'screener-storybook/src/screener'; const useStyles = makeStyles({ wrapper: { @@ -54,6 +55,26 @@ const useStyles = makeStyles({ seeThrough: { opacity: 0.6, }, + + visibilityModifiers: { + backgroundColor: '#ccc', + minHeight: '60px', + width: '200px', + + '[data-popper-reference-hidden]': { + outlineWidth: '5px', + outlineStyle: 'solid', + outlineColor: 'red', + }, + '[data-popper-escaped]': { + backgroundColor: 'yellow', + }, + '[data-popper-is-intersecting]': { + outlineWidth: '5px', + outlineStyle: 'solid', + outlineColor: 'green', + }, + }, }); const positions = [ @@ -454,6 +475,57 @@ const ImperativeTarget = () => { ); }; +const VisibilityModifiers = () => { + const styles = useStyles(); + const popper = usePopper({ align: 'center', position: 'above' }); + + return ( + <> +

+ This visual test asserts that visual styles are applied based on popper element's state: +

+ + +
+ {Array(20) + .fill(null) + .map((_, i) => ( +
+

message: {i}

+
+ ))} +
+ + Box with visibility modifiers + + + ); +}; + storiesOf('Positioning', module) .addDecorator(story => (
) .addStory('target property', () => ) .addStory('imperative target', () => ) + .addStory('visibility modifiers', () => ( + + + + )) .addStory('arrow', () => , { includeRtl: true }); diff --git a/packages/react-positioning/src/isIntersectingModifier.ts b/packages/react-positioning/src/isIntersectingModifier.ts new file mode 100644 index 00000000000000..9f0fcc09938844 --- /dev/null +++ b/packages/react-positioning/src/isIntersectingModifier.ts @@ -0,0 +1,27 @@ +import { detectOverflow, Modifier } from '@popperjs/core'; + +export const isIntersectingModifier: IsIntersectingModifier = { + name: 'is-intersecting-modifier', + enabled: true, + phase: 'main', + requires: ['preventOverflow'], + fn: ({ state, name }) => { + const popperRect = state.rects.popper; + const popperAltOverflow = detectOverflow(state, { altBoundary: true }); + + const isIntersectingTop = popperAltOverflow.top < popperRect.height && popperAltOverflow.top > 0; + const isIntersectingBottom = popperAltOverflow.bottom < popperRect.height && popperAltOverflow.bottom > 0; + + const isIntersecting = isIntersectingTop || isIntersectingBottom; + + state.modifiersData[name] = { + isIntersecting, + }; + state.attributes.popper = { + ...state.attributes.popper, + 'data-popper-is-intersecting': isIntersecting, + }; + }, +}; + +type IsIntersectingModifier = Modifier<'is-intersecting-modifier', never>; diff --git a/packages/react-positioning/src/usePopper.ts b/packages/react-positioning/src/usePopper.ts index f8ebbb67a02b2c..39b9cb6b6c98b0 100644 --- a/packages/react-positioning/src/usePopper.ts +++ b/packages/react-positioning/src/usePopper.ts @@ -3,6 +3,7 @@ import { useFluent } from '@fluentui/react-shared-contexts'; import * as PopperJs from '@popperjs/core'; import * as React from 'react'; +import { isIntersectingModifier } from './isIntersectingModifier'; import { getScrollParent, applyRtlToOffset, @@ -101,6 +102,8 @@ function usePopperOptions(options: PopperOptions, popperOriginalPositionRef: Rea : false; const modifiers: PopperJs.Options['modifiers'] = [ + isIntersectingModifier, + /** * We are setting the position to `fixed` in the first effect to prevent scroll jumps in case of the content * with managed focus. Modifier sets the position to `fixed` before all other modifier effects. Another part of From 76afe40f9e90abe644c5db64756e590ad0fa8cd6 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Thu, 24 Feb 2022 14:24:11 +0100 Subject: [PATCH 2/2] Change files --- ...t-positioning-21fb9bac-746f-4c9a-87ae-5ec9a7915c8a.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-react-positioning-21fb9bac-746f-4c9a-87ae-5ec9a7915c8a.json diff --git a/change/@fluentui-react-positioning-21fb9bac-746f-4c9a-87ae-5ec9a7915c8a.json b/change/@fluentui-react-positioning-21fb9bac-746f-4c9a-87ae-5ec9a7915c8a.json new file mode 100644 index 00000000000000..04f06eb7035728 --- /dev/null +++ b/change/@fluentui-react-positioning-21fb9bac-746f-4c9a-87ae-5ec9a7915c8a.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "feat: add isIntersectingModifier to usePopper", + "packageName": "@fluentui/react-positioning", + "email": "olfedias@microsoft.com", + "dependentChangeType": "patch" +}