From d9fc9cc1d0eb17f64403aa27bd2f597a5460eacd Mon Sep 17 00:00:00 2001 From: arpanroy41 Date: Fri, 6 Feb 2026 16:07:25 +0530 Subject: [PATCH 1/4] fix(react-drag-drop): support dynamic root element IDs in DragDropContainer Fixes #12217 - Replace hardcoded document.getElementById('root') with dynamic root element lookup - Add getRootElement() helper that tries common root IDs: 'root', 'app', 'main', '__next' - Fallback to document.body if no common root element is found - Enables usage in applications with non-standard root element IDs (e.g., id="app") This fix allows OCP 4.22 and other applications to use @patternfly/react-drag-drop regardless of their React root element configuration. --- .../components/DragDrop/DragDropContainer.tsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx b/packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx index 68bf81048d2..df300a875ff 100644 --- a/packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx +++ b/packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx @@ -288,6 +288,21 @@ export const DragDropContainer: React.FunctionComponent }; const dragOverlay = {activeId && getDragOverlay()}; + + // Find the React root element dynamically instead of hardcoding 'root' + const getRootElement = () => { + // Try common root element IDs + const commonRootIds = ['root', 'app', 'main', '__next']; + for (const id of commonRootIds) { + const element = document.getElementById(id); + if (element) { + return element; + } + } + // Fallback to document.body if no common root is found + return document.body; + }; + return ( {...props} > {children} - {canUseDOM ? ReactDOM.createPortal(dragOverlay, document.getElementById('root')) : dragOverlay} + {canUseDOM ? ReactDOM.createPortal(dragOverlay, getRootElement()) : dragOverlay} ); }; From 49c785f1ea41ae37c0d8d0c00b85ffe303ecc9f0 Mon Sep 17 00:00:00 2001 From: arpanroy41 Date: Sat, 7 Feb 2026 00:52:46 +0530 Subject: [PATCH 2/4] perf: memoize root element lookup to avoid repeated DOM queries --- .../src/components/DragDrop/DragDropContainer.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx b/packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx index df300a875ff..b583b72cbf6 100644 --- a/packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx +++ b/packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import * as ReactDOM from 'react-dom'; import { css } from '@patternfly/react-styles'; import { @@ -290,7 +290,8 @@ export const DragDropContainer: React.FunctionComponent const dragOverlay = {activeId && getDragOverlay()}; // Find the React root element dynamically instead of hardcoding 'root' - const getRootElement = () => { + // Memoized to avoid looking up the root element on every render + const rootElement = useMemo(() => { // Try common root element IDs const commonRootIds = ['root', 'app', 'main', '__next']; for (const id of commonRootIds) { @@ -301,7 +302,7 @@ export const DragDropContainer: React.FunctionComponent } // Fallback to document.body if no common root is found return document.body; - }; + }, []); // Empty deps - root element doesn't change after mount return ( {...props} > {children} - {canUseDOM ? ReactDOM.createPortal(dragOverlay, getRootElement()) : dragOverlay} + {canUseDOM ? ReactDOM.createPortal(dragOverlay, rootElement) : dragOverlay} ); }; From f4bd4d6249b8f81ec1c72025da9f5b4348261769 Mon Sep 17 00:00:00 2001 From: arpanroy41 Date: Sat, 7 Feb 2026 01:02:47 +0530 Subject: [PATCH 3/4] fix(DragDropContainer): handle case when DOM is not available by returning null for root element --- .../src/components/DragDrop/DragDropContainer.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx b/packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx index b583b72cbf6..dc78c52d7d8 100644 --- a/packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx +++ b/packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx @@ -292,6 +292,9 @@ export const DragDropContainer: React.FunctionComponent // Find the React root element dynamically instead of hardcoding 'root' // Memoized to avoid looking up the root element on every render const rootElement = useMemo(() => { + if (!canUseDOM) { + return null; + } // Try common root element IDs const commonRootIds = ['root', 'app', 'main', '__next']; for (const id of commonRootIds) { From 107f96776d5ace57a29ea350061406a9f04dffdd Mon Sep 17 00:00:00 2001 From: arpanroy41 Date: Thu, 12 Feb 2026 20:24:04 +0530 Subject: [PATCH 4/4] feat(DragDropContainer): add appendTo prop for customizable drag overlay target --- .../components/DragDrop/DragDropContainer.tsx | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx b/packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx index dc78c52d7d8..749fe2df33b 100644 --- a/packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx +++ b/packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import * as ReactDOM from 'react-dom'; import { css } from '@patternfly/react-styles'; import { @@ -66,6 +66,8 @@ export interface DragDropContainerProps extends DndContextProps { variant?: 'default' | 'DataList' | 'DualListSelectorList'; /** Additional classes to apply to the drag overlay */ overlayProps?: any; + /** The parent container to append the drag overlay to. Defaults to document.body. */ + appendTo?: HTMLElement | (() => HTMLElement); } export const DragDropContainer: React.FunctionComponent = ({ @@ -77,6 +79,7 @@ export const DragDropContainer: React.FunctionComponent onCancel = () => {}, variant = 'default', overlayProps, + appendTo = () => document.body, ...props }: DragDropContainerProps) => { const itemsCopy = useRef | null>(null); @@ -289,23 +292,7 @@ export const DragDropContainer: React.FunctionComponent const dragOverlay = {activeId && getDragOverlay()}; - // Find the React root element dynamically instead of hardcoding 'root' - // Memoized to avoid looking up the root element on every render - const rootElement = useMemo(() => { - if (!canUseDOM) { - return null; - } - // Try common root element IDs - const commonRootIds = ['root', 'app', 'main', '__next']; - for (const id of commonRootIds) { - const element = document.getElementById(id); - if (element) { - return element; - } - } - // Fallback to document.body if no common root is found - return document.body; - }, []); // Empty deps - root element doesn't change after mount + const portalTarget = typeof appendTo === 'function' ? appendTo() : appendTo || document.body; return ( {...props} > {children} - {canUseDOM ? ReactDOM.createPortal(dragOverlay, rootElement) : dragOverlay} + {canUseDOM && portalTarget ? ReactDOM.createPortal(dragOverlay, portalTarget) : dragOverlay} ); };