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(