diff --git a/openmetadata-ui/src/main/resources/ui/package.json b/openmetadata-ui/src/main/resources/ui/package.json index 3f571c1d507f..7009e3c9ce1c 100644 --- a/openmetadata-ui/src/main/resources/ui/package.json +++ b/openmetadata-ui/src/main/resources/ui/package.json @@ -160,7 +160,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..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,6 +84,7 @@ export const GenericProvider = >({ const pageType = useMemo(() => ENTITY_PAGE_TYPE_MAP[type], [type]); const { tab } = useRequiredParams<{ tab: EntityTabs }>(); const expandedLayout = useRef([]); + const selectedColumnRef = useRef(null); const [layout, setLayout] = useState( getLayoutFromCustomizedPage(pageType, tab, customizedPage, isVersionView) ); @@ -95,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([]); @@ -137,7 +139,6 @@ 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 (columnFqn && extractedColumns.length > 0) { const col = findFieldByFQN(extractedColumns as Column[], columnFqn); if (col) { @@ -229,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; } @@ -253,7 +257,7 @@ export const GenericProvider = >({ tab, navigate, location.pathname, - selectedColumn?.fullyQualifiedName, + // selectedColumn?.fullyQualifiedName intentionally omitted — read via selectedColumnRef ] ); 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 36e86826a7cc..f3b9534d2ead 100644 --- a/openmetadata-ui/src/main/resources/ui/yarn.lock +++ b/openmetadata-ui/src/main/resources/ui/yarn.lock @@ -2381,11 +2381,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" @@ -5597,7 +5592,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== @@ -11411,20 +11406,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" @@ -11926,6 +11921,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"