diff --git a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap index 4ad728b59e2b..6ce59ab366c0 100644 --- a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap +++ b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap @@ -6251,6 +6251,55 @@ Map { "autoAlign": Object { "type": "bool", }, + "autoAlignBoundary": Object { + "args": Array [ + Array [ + Object { + "args": Array [ + Array [ + "clippingAncestors", + ], + ], + "type": "oneOf", + }, + Object { + "type": "elementType", + }, + Object { + "args": Array [ + Object { + "type": "elementType", + }, + ], + "type": "arrayOf", + }, + Object { + "args": Array [ + Object { + "height": Object { + "isRequired": true, + "type": "number", + }, + "width": Object { + "isRequired": true, + "type": "number", + }, + "x": Object { + "isRequired": true, + "type": "number", + }, + "y": Object { + "isRequired": true, + "type": "number", + }, + }, + ], + "type": "exact", + }, + ], + ], + "type": "oneOfType", + }, "caret": Object { "type": "bool", }, diff --git a/packages/react/src/components/Popover/Popover.stories.js b/packages/react/src/components/Popover/Popover.stories.js index 7007f18b6678..95f41d398bc6 100644 --- a/packages/react/src/components/Popover/Popover.stories.js +++ b/packages/react/src/components/Popover/Popover.stories.js @@ -270,6 +270,136 @@ export const ExperimentalAutoAlign = () => { ); }; +export const ExperimentalAutoAlignBoundary = () => { + const [open, setOpen] = useState(true); + const ref = useRef(); + const [boundary, setBoundary] = useState(); + + useEffect(() => { + ref?.current?.scrollIntoView({ block: 'center', inline: 'center' }); + }); + + return ( +
+
+
+ +
+ { + setOpen(!open); + }} + /> +
+ +
+

This popover uses autoAlign

+

+ Scroll the container up, down, left or right to observe how the + popover will automatically change its position in attempt to + stay within the viewport. This works on initial render in + addition to on scroll. +

+
+
+
+
+
+
+ ); +}; + +export const Test = () => { + const [open, setOpen] = useState(); + const align = document?.dir === 'rtl' ? 'bottom-right' : 'bottom-left'; + const alignTwo = document?.dir === 'rtl' ? 'bottom-left' : 'bottom-right'; + return ( +
+ + + + + + + + + + { + if (match(evt, keys.Escape)) { + setOpen(false); + } + }} + isTabTip + onRequestClose={() => setOpen(false)}> + + + + + + +
+
+ Edit columns + + + +
+
+
+
+ ); +}; + export const TabTipExperimentalAutoAlign = () => { const [open, setOpen] = useState(true); const ref = useRef(); diff --git a/packages/react/src/components/Popover/index.tsx b/packages/react/src/components/Popover/index.tsx index 44f5061555d3..36e59ea3c1a3 100644 --- a/packages/react/src/components/Popover/index.tsx +++ b/packages/react/src/components/Popover/index.tsx @@ -28,6 +28,7 @@ import { autoUpdate, arrow, offset, + type Boundary, } from '@floating-ui/react'; import { hide } from '@floating-ui/dom'; import { useFeatureFlag } from '../FeatureFlags'; @@ -102,6 +103,11 @@ interface PopoverBaseProps { */ autoAlign?: boolean; + /** + * Specify a bounding element to be used for autoAlign calculations. The viewport is used by default. This prop is currently experimental and is subject to future changes. + */ + autoAlignBoundary?: Boundary; + /** * Specify whether a caret should be rendered */ @@ -165,6 +171,7 @@ export const Popover: PopoverComponent = React.forwardRef( align: initialAlign = isTabTip ? 'bottom-start' : 'bottom', as: BaseComponent = 'span' as E, autoAlign = false, + autoAlignBoundary, caret = isTabTip ? false : true, className: customClassName, children, @@ -292,6 +299,7 @@ export const Popover: PopoverComponent = React.forwardRef( fallbackStrategy: 'initialPlacement', fallbackAxisSideDirection: 'start', + boundary: autoAlignBoundary, }), arrow({ element: caretRef, @@ -476,7 +484,7 @@ export const Popover: PopoverComponent = React.forwardRef( ); } -); +) as PopoverComponent; // Note: this displayName is temporarily set so that Storybook ArgTable // correctly displays the name of this component @@ -546,6 +554,21 @@ Popover.propTypes = { */ autoAlign: PropTypes.bool, + /** + * Specify a bounding element to be used for autoAlign calculations. The viewport is used by default. This prop is currently experimental and is subject to future changes. + */ + autoAlignBoundary: PropTypes.oneOfType([ + PropTypes.oneOf(['clippingAncestors']), + PropTypes.elementType, + PropTypes.arrayOf(PropTypes.elementType), + PropTypes.exact({ + x: PropTypes.number.isRequired, + y: PropTypes.number.isRequired, + width: PropTypes.number.isRequired, + height: PropTypes.number.isRequired, + }), + ]) as PropTypes.Validator, + /** * Specify whether a caret should be rendered */