From dfd6fa95b84eb542e3edea2c880df0f26c419106 Mon Sep 17 00:00:00 2001 From: Rohit0301 Date: Tue, 28 Jul 2026 17:28:32 +0530 Subject: [PATCH 1/2] =?UTF-8?q?chore(UI):=20upgrade=20react-router-dom=20v?= =?UTF-8?q?6=20=E2=86=92=20v7=20and=20fix=20column=20panel=20race=20condit?= =?UTF-8?q?ion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bump react-router-dom from 6.30.4 to 7.18.1 - In v7, navigate() is wrapped in React.startTransition, deferring the router re-render. This caused the column detail panel to reopen after close because effects still saw stale URL params during the transition. - Fix useFqnDeepLink.ts: move selectedColumn to a ref so it is read without triggering the effect when closeColumnDetailPanel sets it null. - Fix GenericProvider.tsx: add skipNextColumnSync ref that is set before navigate() in closeColumnDetailPanel and consumed once in the URL-sync useEffect, preventing the panel from reopening during the deferred re-render window. Co-Authored-By: Claude Sonnet 4.6 --- .../src/main/resources/ui/package.json | 2 +- .../GenericProvider/GenericProvider.tsx | 8 ++++- .../resources/ui/src/hooks/useFqnDeepLink.ts | 10 ++++-- .../src/main/resources/ui/yarn.lock | 34 +++++++++---------- 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/package.json b/openmetadata-ui/src/main/resources/ui/package.json index 2507986e0c5d..dc905c983f71 100644 --- a/openmetadata-ui/src/main/resources/ui/package.json +++ b/openmetadata-ui/src/main/resources/ui/package.json @@ -151,7 +151,7 @@ "react-quill-new": "^3.4.0", "react-reflex": "^4.1.0", "react-resizable": "^3.1.3", - "react-router-dom": "^6.30.4", + "react-router-dom": "7.18.1", "reactflow": "^11.10.2", "reactjs-localstorage": "^1.0.1", "recharts": "2.10.3", diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Customization/GenericProvider/GenericProvider.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Customization/GenericProvider/GenericProvider.tsx index 29f800bae69b..c046f0bb6093 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Customization/GenericProvider/GenericProvider.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Customization/GenericProvider/GenericProvider.tsx @@ -84,6 +84,7 @@ export const GenericProvider = >({ const pageType = useMemo(() => ENTITY_PAGE_TYPE_MAP[type], [type]); const { tab } = useRequiredParams<{ tab: EntityTabs }>(); const expandedLayout = useRef([]); + const skipNextColumnSync = useRef(false); const [layout, setLayout] = useState( getLayoutFromCustomizedPage(pageType, tab, customizedPage, isVersionView) ); @@ -137,7 +138,11 @@ export const GenericProvider = >({ // Sync selected column from prop (deep link) useEffect(() => { - // If we have a direct columnFqn from props, try to find and select it + if (skipNextColumnSync.current) { + skipNextColumnSync.current = false; + + return; + } if (columnFqn && extractedColumns.length > 0) { const col = findFieldByFQN(extractedColumns as Column[], columnFqn); if (col) { @@ -258,6 +263,7 @@ export const GenericProvider = >({ ); const closeColumnDetailPanel = useCallback(() => { + skipNextColumnSync.current = true; setSelectedColumn(null); // Update URL to remove column FQN diff --git a/openmetadata-ui/src/main/resources/ui/src/hooks/useFqnDeepLink.ts b/openmetadata-ui/src/main/resources/ui/src/hooks/useFqnDeepLink.ts index 19bb41f8f69c..bfa8954a0eee 100644 --- a/openmetadata-ui/src/main/resources/ui/src/hooks/useFqnDeepLink.ts +++ b/openmetadata-ui/src/main/resources/ui/src/hooks/useFqnDeepLink.ts @@ -10,7 +10,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { useEffect } from 'react'; +import { useEffect, useRef } from 'react'; import { findFieldByFQN, getParentKeysToExpand } from '../utils/TablePureUtils'; interface UseFqnDeepLinkProps { @@ -32,6 +32,9 @@ export const useFqnDeepLink = < openColumnDetailPanel, selectedColumn, }: UseFqnDeepLinkProps) => { + const selectedColumnRef = useRef(selectedColumn); + selectedColumnRef.current = selectedColumn; + useEffect(() => { if (!columnPart || !fqn) { return; @@ -47,7 +50,8 @@ export const useFqnDeepLink = < const matchedField = findFieldByFQN(data, fullColumnFqn); if (matchedField) { if ( - selectedColumn?.fullyQualifiedName !== matchedField.fullyQualifiedName + selectedColumnRef.current?.fullyQualifiedName !== + matchedField.fullyQualifiedName ) { openColumnDetailPanel(matchedField); } @@ -57,7 +61,7 @@ export const useFqnDeepLink = < fqn, data, openColumnDetailPanel, - selectedColumn, setExpandedRowKeys, + // selectedColumn intentionally omitted — tracked via ref to avoid re-triggering on close ]); }; diff --git a/openmetadata-ui/src/main/resources/ui/yarn.lock b/openmetadata-ui/src/main/resources/ui/yarn.lock index b60dc07aad34..02ca93796b28 100644 --- a/openmetadata-ui/src/main/resources/ui/yarn.lock +++ b/openmetadata-ui/src/main/resources/ui/yarn.lock @@ -2380,11 +2380,6 @@ resolved "https://registry.yarnpkg.com/@remirror/core-constants/-/core-constants-3.0.0.tgz#96fdb89d25c62e7b6a5d08caf0ce5114370e3b8f" integrity sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg== -"@remix-run/router@1.23.3": - version "1.23.3" - resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.23.3.tgz#957c098d4393d301a8aa7dccf3ef28ea5430e36a" - integrity sha512-4An71tdz9X8+3sI4Qqqd2LWd9vS39J7sqd9EU4Scw7TJE/qB10Flv/UuqbPVgfQV9XoK8Np6jNquZitnZq5i+Q== - "@rjsf/core@5.24.13": version "5.24.13" resolved "https://registry.yarnpkg.com/@rjsf/core/-/core-5.24.13.tgz#ef98e5dc6ac064b2be2f56e0887c99df2b1f8d44" @@ -5603,7 +5598,7 @@ cookie-storage@^6.1.0: resolved "https://registry.yarnpkg.com/cookie-storage/-/cookie-storage-6.1.0.tgz#291b2f662d961be44f999626593421cbfcf23790" integrity sha512-HeVqbVy8BjXhAAuFtL6MTG+witHoLbxfky2jgVh9FmxmyL6IKa9gSSyPNjevXCCCxPu6Tzd9J8+eXTRQzYU/cg== -cookie@0.7.0: +cookie@0.7.0, cookie@^1.0.1: version "0.7.0" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.0.tgz#2148f68a77245d5c2c0005d264bc3e08cfa0655d" integrity sha512-qCf+V4dtlNhSRXGAZatc1TasyFO6GjohcOul807YOb5ik3+kQSnb4d7iajeCL8QHaJ4uZEjCgiCJerKXwdRVlQ== @@ -11425,20 +11420,20 @@ react-resizable@^3.0.5, react-resizable@^3.1.3: prop-types "15.x" react-draggable "^4.5.0" -react-router-dom@^6.30.4: - version "6.30.4" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.30.4.tgz#f7167bf3da6c7d9132130ea985dd06def25e84d5" - integrity sha512-q4HvNl+mmDdkS0g+MqiBZNteQJCuimWoOyHMy4T/RQLAn9Z29+E91QXRaxOujeMl2HTzRSS0KFPd7lxX3PjV0Q== +react-router-dom@7.18.1: + version "7.18.1" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-7.18.1.tgz#0d1b138e291393059ad481c3e10e366385a978a4" + integrity sha512-KaZh+X/6UtEp28x51AUYZDMg9NGoz2ja3dNHa+ta/tk40vCzKhQ/RypCWBMLbmDr6//E24Vv5uPsrqXFozdkAg== dependencies: - "@remix-run/router" "1.23.3" - react-router "6.30.4" + react-router "7.18.1" -react-router@6.30.4: - version "6.30.4" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.30.4.tgz#638f35176527bd243d96d81d35d33b757bad46c2" - integrity sha512-SVUsDe+DybHM/WmYKIVYhZh1o5Dcuf16yM6WjG02Q9XVFMZIJyHYhwrr6bFBXZkVP6z69kNkMyBCujt8FaFLJA== +react-router@7.18.1: + version "7.18.1" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-7.18.1.tgz#61259d1594b95c1ace299ee4c57453570f0c22f1" + integrity sha512-GDLgg3i3uM0aeJO3Fm+TCS+sDQ7gu12T6x0qdTEzcwqEfleci7JwugVNIF3U//0FWKnJT7ptG+20B2jfDqnZAg== dependencies: - "@remix-run/router" "1.23.3" + cookie "^1.0.1" + set-cookie-parser "^2.6.0" react-shallow-renderer@^16.15.0: version "16.15.0" @@ -11940,6 +11935,11 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-cookie-parser@^2.6.0: + version "2.7.2" + resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz#ccd08673a9ae5d2e44ea2a2de25089e67c7edf68" + integrity sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw== + set-function-length@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" From 353126175a4231bf9496afd424cf386505ae9b01 Mon Sep 17 00:00:00 2001 From: Rohit0301 Date: Tue, 28 Jul 2026 17:54:27 +0530 Subject: [PATCH 2/2] minor fix --- .../GenericProvider/GenericProvider.tsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Customization/GenericProvider/GenericProvider.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Customization/GenericProvider/GenericProvider.tsx index c046f0bb6093..4dd43a00e3a1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Customization/GenericProvider/GenericProvider.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Customization/GenericProvider/GenericProvider.tsx @@ -84,7 +84,7 @@ export const GenericProvider = >({ const pageType = useMemo(() => ENTITY_PAGE_TYPE_MAP[type], [type]); const { tab } = useRequiredParams<{ tab: EntityTabs }>(); const expandedLayout = useRef([]); - const skipNextColumnSync = useRef(false); + const selectedColumnRef = useRef(null); const [layout, setLayout] = useState( getLayoutFromCustomizedPage(pageType, tab, customizedPage, isVersionView) ); @@ -96,6 +96,7 @@ export const GenericProvider = >({ const [selectedColumn, setSelectedColumn] = useState( null ); + selectedColumnRef.current = selectedColumn; // State to store the displayed columns (sorted/filtered) from SchemaTable const [displayedColumns, setDisplayedColumns] = useState([]); @@ -138,11 +139,6 @@ export const GenericProvider = >({ // Sync selected column from prop (deep link) useEffect(() => { - if (skipNextColumnSync.current) { - skipNextColumnSync.current = false; - - return; - } if (columnFqn && extractedColumns.length > 0) { const col = findFieldByFQN(extractedColumns as Column[], columnFqn); if (col) { @@ -234,8 +230,11 @@ export const GenericProvider = >({ (column: ColumnOrTask) => { const columnFqn = column.fullyQualifiedName; - // If the column is already selected, don't do anything to avoid loops - if (selectedColumn?.fullyQualifiedName === columnFqn) { + // Read via ref so this callback is not recreated when selectedColumn changes. + // Without this, closeColumnDetailPanel (setSelectedColumn null) would recreate + // this callback, causing useFqnDeepLink to re-fire with stale URL params and + // reopen the panel (react-router v7 defers navigate() via startTransition). + if (selectedColumnRef.current?.fullyQualifiedName === columnFqn) { return; } @@ -258,12 +257,11 @@ export const GenericProvider = >({ tab, navigate, location.pathname, - selectedColumn?.fullyQualifiedName, + // selectedColumn?.fullyQualifiedName intentionally omitted — read via selectedColumnRef ] ); const closeColumnDetailPanel = useCallback(() => { - skipNextColumnSync.current = true; setSelectedColumn(null); // Update URL to remove column FQN