Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import splitPathAndQuery from './splitPathAndQuery';
type LeafRoute = {
name: string;
path: string;
params?: Record<string, string>;
params?: Record<string, unknown>;
};

type NestedRoute = {
Expand Down Expand Up @@ -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<string, unknown>) {
const routeConfig = getRouteNamesForDynamicRoute(DYNAMIC_ROUTES[dynamicRouteName].path);
const [, query] = splitPathAndQuery(path);
const params = getParamsFromQuery(query);
Expand All @@ -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,
};
}

Expand Down
2 changes: 1 addition & 1 deletion src/libs/Navigation/helpers/getStateFromPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function getStateFromPath(path: Route): PartialState<NavigationState> {
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<string, unknown> | undefined);
return dynamicRouteState;
}

Expand Down
30 changes: 30 additions & 0 deletions tests/navigation/getStateForDynamicRouteTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ jest.mock('@src/ROUTES', () => ({
type LeafRoute = {
name: string;
path: string;
params?: Record<string, unknown>;
};

type NestedRoute = {
Expand Down Expand Up @@ -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'});
});
});
5 changes: 3 additions & 2 deletions tests/navigation/getStateFromPathTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,18 @@ 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);

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', () => {
Expand Down
Loading