diff --git a/.changeset/olive-forks-call.md b/.changeset/olive-forks-call.md new file mode 100644 index 00000000000..9e22381a5c8 --- /dev/null +++ b/.changeset/olive-forks-call.md @@ -0,0 +1,5 @@ +--- +'@shopify/polaris': minor +--- + +Added `PositionedOverlay` scroll support for all scroll containers diff --git a/polaris-react/src/components/Popover/Popover.stories.tsx b/polaris-react/src/components/Popover/Popover.stories.tsx index 5699e5bba12..509e28a6773 100644 --- a/polaris-react/src/components/Popover/Popover.stories.tsx +++ b/polaris-react/src/components/Popover/Popover.stories.tsx @@ -19,6 +19,8 @@ import { EmptySearchResult, Text, BlockStack, + Card, + InlineStack, } from '@shopify/polaris'; import {SearchIcon} from '@shopify/polaris-icons'; @@ -832,6 +834,52 @@ export function WithSubduedPane() { ); } +export function WithScrollContainer() { + const [popoverActive, setPopoverActive] = useState(true); + + const togglePopoverActive = useCallback( + () => setPopoverActive((popoverActive) => !popoverActive), + [], + ); + + const activator = ( + + ); + + return ( + +
+
+ + + + + + + Popover content + + + + + Subdued popover pane + + + + +
+
+
+ ); +} + const StopPropagation = ({children}: React.PropsWithChildren) => { const stopEventPropagation = (event: React.MouseEvent | React.TouchEvent) => { event.stopPropagation(); diff --git a/polaris-react/src/components/PositionedOverlay/PositionedOverlay.tsx b/polaris-react/src/components/PositionedOverlay/PositionedOverlay.tsx index 3eac1769189..c66a37757ee 100644 --- a/polaris-react/src/components/PositionedOverlay/PositionedOverlay.tsx +++ b/polaris-react/src/components/PositionedOverlay/PositionedOverlay.tsx @@ -4,7 +4,6 @@ import {classNames} from '../../utilities/css'; import {getRectForNode, Rect} from '../../utilities/geometry'; // eslint-disable-next-line import/no-deprecated import {EventListener} from '../EventListener'; -import {Scrollable} from '../Scrollable'; import {layer, dataPolarisTopBar} from '../shared'; import { @@ -198,20 +197,44 @@ export class PositionedOverlay extends PureComponent< this.overlay = node; }; - private setScrollableContainers = () => { - const containers: (HTMLElement | Document)[] = []; - let scrollableContainer = Scrollable.forNode(this.props.activator); + private isScrollContainer = (node: HTMLElement) => { + const yOverflow = node.scrollHeight > node.clientHeight; + const xOverflow = node.scrollWidth > node.clientWidth; - if (scrollableContainer) { - containers.push(scrollableContainer); + if (!yOverflow && !xOverflow) { + return false; + } - while (scrollableContainer?.parentElement) { - scrollableContainer = Scrollable.forNode( - scrollableContainer.parentElement, - ); + const styles = window.getComputedStyle(node); + const yScroll = + styles.overflowY === 'auto' || styles.overflowY === 'scroll'; + const xScroll = + styles.overflowX === 'auto' || styles.overflowX === 'scroll'; - containers.push(scrollableContainer); + if ((yOverflow && yScroll) || (xOverflow && xScroll)) { + return true; + } + + return false; + }; + + private setScrollableContainers = () => { + const containers: (HTMLElement | Document)[] = []; + let currentNode: HTMLElement | ParentNode | null = this.props.activator; + + while (currentNode !== null) { + if ( + currentNode instanceof HTMLElement && + this.isScrollContainer(currentNode) + ) { + containers.push(currentNode); } + + currentNode = currentNode.parentNode; + } + + if (!containers.length) { + containers.push(document); } this.scrollableContainers = containers; diff --git a/polaris-react/src/components/PositionedOverlay/utilities/math.ts b/polaris-react/src/components/PositionedOverlay/utilities/math.ts index df4c8ae59be..f6b2d96f0d6 100644 --- a/polaris-react/src/components/PositionedOverlay/utilities/math.ts +++ b/polaris-react/src/components/PositionedOverlay/utilities/math.ts @@ -117,7 +117,12 @@ export function calculateHorizontalPosition( export function rectIsOutsideOfRect(inner: Rect, outer: Rect) { const {center} = inner; - return center.y < outer.top || center.y > outer.top + outer.height; + return ( + center.y < outer.top || + center.y > outer.top + outer.height || + inner.left < outer.left || + inner.left + inner.width > outer.left + outer.width + ); } export function intersectionWithViewport(