diff --git a/src/libs/Navigation/helpers/dynamicRoutesUtils/getStateForDynamicRoute.ts b/src/libs/Navigation/helpers/dynamicRoutesUtils/getStateForDynamicRoute.ts index ce35dc567c20a..882b4a8f59c9f 100644 --- a/src/libs/Navigation/helpers/dynamicRoutesUtils/getStateForDynamicRoute.ts +++ b/src/libs/Navigation/helpers/dynamicRoutesUtils/getStateForDynamicRoute.ts @@ -6,7 +6,7 @@ import splitPathAndQuery from './splitPathAndQuery'; type LeafRoute = { name: string; path: string; - params?: Record; + params?: Record; }; type NestedRoute = { @@ -54,7 +54,7 @@ function getRouteNamesForDynamicRoute(dynamicRouteName: DynamicRouteSuffix): str return null; } -function getStateForDynamicRoute(path: string, dynamicRouteName: keyof typeof DYNAMIC_ROUTES) { +function getStateForDynamicRoute(path: string, dynamicRouteName: keyof typeof DYNAMIC_ROUTES, parentRouteParams?: Record) { const routeConfig = getRouteNamesForDynamicRoute(DYNAMIC_ROUTES[dynamicRouteName].path); const [, query] = splitPathAndQuery(path); const params = getParamsFromQuery(query); @@ -67,12 +67,14 @@ function getStateForDynamicRoute(path: string, dynamicRouteName: keyof typeof DY const buildNestedState = (routes: string[], currentIndex: number): RouteNode => { const currentRoute = routes.at(currentIndex); - // If this is the last route, create leaf node with path + // If this is the last route, create leaf node with path and merged params if (currentIndex === routes.length - 1) { + const mergedParams = parentRouteParams || params ? {...(parentRouteParams ?? {}), ...(params ?? {})} : undefined; + const paramsSpread = mergedParams ? {params: mergedParams} : {}; return { name: currentRoute ?? '', path, - params, + ...paramsSpread, }; } diff --git a/src/libs/Navigation/helpers/getStateFromPath.ts b/src/libs/Navigation/helpers/getStateFromPath.ts index 92119fdbf2281..2c4d7a11dcce2 100644 --- a/src/libs/Navigation/helpers/getStateFromPath.ts +++ b/src/libs/Navigation/helpers/getStateFromPath.ts @@ -39,7 +39,7 @@ function getStateFromPath(path: Route): PartialState { if (focusedRoute?.name) { if (entryScreens.includes(focusedRoute.name as Screen)) { // Generate navigation state for the dynamic route - const dynamicRouteState = getStateForDynamicRoute(normalizedPath, dynamicRoute as DynamicRouteKey); + const dynamicRouteState = getStateForDynamicRoute(normalizedPath, dynamicRoute as DynamicRouteKey, focusedRoute?.params as Record | undefined); return dynamicRouteState; } diff --git a/tests/navigation/getStateForDynamicRouteTests.ts b/tests/navigation/getStateForDynamicRouteTests.ts index cf3dc7c588f0e..9e2669201418f 100644 --- a/tests/navigation/getStateForDynamicRouteTests.ts +++ b/tests/navigation/getStateForDynamicRouteTests.ts @@ -31,6 +31,7 @@ jest.mock('@src/ROUTES', () => ({ type LeafRoute = { name: string; path: string; + params?: Record; }; type NestedRoute = { @@ -111,4 +112,33 @@ describe('getStateForDynamicRoute', () => { expect(state?.index).toBe(0); expect(Array.isArray(state?.routes)).toBe(true); }); + + it('should inherit parent route params on the leaf node', () => { + const path = '/r/12345/settings/test-path'; + const parentParams = {reportID: '12345'}; + const result = getStateForDynamicRoute(path, KEY_TEST as unknown as keyof typeof DYNAMIC_ROUTES, parentParams); + + const rootRoute = result.routes.at(0) as NestedRoute | undefined; + const leafRoute = rootRoute?.state.routes.at(0) as LeafRoute | undefined; + expect(leafRoute?.params).toEqual(parentParams); + }); + + it('should not include params on the leaf node when neither parentRouteParams nor query params are provided', () => { + const path = '/some/path/test-path'; + const result = getStateForDynamicRoute(path, KEY_TEST as unknown as keyof typeof DYNAMIC_ROUTES); + + const rootRoute = result.routes.at(0) as NestedRoute | undefined; + const leafRoute = rootRoute?.state.routes.at(0) as LeafRoute | undefined; + expect(leafRoute?.params).toBeUndefined(); + }); + + it('should merge parent route params with query params', () => { + const path = '/r/12345/settings/test-path?country=US'; + const parentParams = {reportID: '12345'}; + const result = getStateForDynamicRoute(path, KEY_TEST as unknown as keyof typeof DYNAMIC_ROUTES, parentParams); + + const rootRoute = result.routes.at(0) as NestedRoute | undefined; + const leafRoute = rootRoute?.state.routes.at(0) as LeafRoute | undefined; + expect(leafRoute?.params).toEqual({reportID: '12345', country: 'US'}); + }); }); diff --git a/tests/navigation/getStateFromPathTests.ts b/tests/navigation/getStateFromPathTests.ts index ffa5cb2ace840..5808b6f164e4d 100644 --- a/tests/navigation/getStateFromPathTests.ts +++ b/tests/navigation/getStateFromPathTests.ts @@ -57,9 +57,10 @@ describe('getStateFromPath', () => { it('should generate dynamic state when authorized screen is focused', () => { const fullPath = '/settings/wallet/verify-account'; const baseRouteState = {routes: [{name: 'Wallet'}]}; + const focusedRouteParams = {walletID: '456'}; mockRNGetStateFromPath.mockReturnValue(baseRouteState); - mockFindFocusedRoute.mockReturnValue({name: 'Wallet'}); + mockFindFocusedRoute.mockReturnValue({name: 'Wallet', params: focusedRouteParams}); const expectedDynamicState = {routes: [{name: 'DynamicRoot'}]}; mockGetStateForDynamicRoute.mockReturnValue(expectedDynamicState); @@ -67,7 +68,7 @@ describe('getStateFromPath', () => { const result = getStateFromPath(fullPath as unknown as Route); expect(result).toBe(expectedDynamicState); - expect(mockGetStateForDynamicRoute).toHaveBeenCalledWith(fullPath, 'VERIFY_ACCOUNT'); + expect(mockGetStateForDynamicRoute).toHaveBeenCalledWith(fullPath, 'VERIFY_ACCOUNT', focusedRouteParams); }); it('should fallback to standard RN parsing if focused screen is NOT authorized for dynamic route', () => {