mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-01-12 22:51:18 +08:00
feat: add ability to specify root param list
This commit is contained in:
@@ -29,6 +29,7 @@ import {
|
||||
DarkTheme,
|
||||
PathConfigMap,
|
||||
useNavigationContainerRef,
|
||||
NavigatorScreenParams,
|
||||
} from '@react-navigation/native';
|
||||
import { createDrawerNavigator } from '@react-navigation/drawer';
|
||||
import {
|
||||
@@ -62,6 +63,13 @@ if (Platform.OS !== 'web') {
|
||||
|
||||
enableScreens();
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace ReactNavigation {
|
||||
interface RootParamList extends RootStackParamList {}
|
||||
}
|
||||
}
|
||||
|
||||
type RootDrawerParamList = {
|
||||
Examples: undefined;
|
||||
};
|
||||
@@ -120,7 +128,7 @@ const SCREENS = {
|
||||
};
|
||||
|
||||
type RootStackParamList = {
|
||||
Home: undefined;
|
||||
Home: NavigatorScreenParams<RootDrawerParamList>;
|
||||
NotFound: undefined;
|
||||
} & {
|
||||
[P in keyof typeof SCREENS]: undefined;
|
||||
@@ -233,7 +241,9 @@ export default function App() {
|
||||
prefixes: [createURL('/')],
|
||||
config: {
|
||||
initialRouteName: 'Home',
|
||||
screens: Object.keys(SCREENS).reduce<PathConfigMap>(
|
||||
screens: Object.keys(SCREENS).reduce<
|
||||
PathConfigMap<RootStackParamList>
|
||||
>(
|
||||
(acc, name) => {
|
||||
// Convert screen names such as SimpleStack to kebab case (simple-stack)
|
||||
const path = name
|
||||
@@ -241,13 +251,15 @@ export default function App() {
|
||||
.replace(/^-/, '')
|
||||
.toLowerCase();
|
||||
|
||||
// @ts-expect-error: these types aren't accurate
|
||||
// But we aren't too concerned for now
|
||||
acc[name] = {
|
||||
path,
|
||||
screens: {
|
||||
Article: {
|
||||
path: 'article/:author?',
|
||||
parse: {
|
||||
author: (author) =>
|
||||
author: (author: string) =>
|
||||
author.charAt(0).toUpperCase() +
|
||||
author.slice(1).replace(/-/g, ' '),
|
||||
},
|
||||
|
||||
@@ -33,8 +33,10 @@ it('converts state to path string', () => {
|
||||
|
||||
const path = '/foo/bar/baz%20qux?author=jane&valid=true';
|
||||
|
||||
expect(getPathFromState(state)).toBe(path);
|
||||
expect(getPathFromState(getStateFromPath(path) as State)).toBe(path);
|
||||
expect(getPathFromState<object>(state)).toBe(path);
|
||||
expect(
|
||||
getPathFromState<object>(getStateFromPath<object>(path) as State)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
it('converts state to path string with config', () => {
|
||||
@@ -97,9 +99,12 @@ it('converts state to path string with config', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -116,8 +121,10 @@ it('handles route without param', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state)).toBe(path);
|
||||
expect(getPathFromState(getStateFromPath(path) as State)).toBe(path);
|
||||
expect(getPathFromState<object>(state)).toBe(path);
|
||||
expect(
|
||||
getPathFromState<object>(getStateFromPath<object>(path) as State)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
it("doesn't add query param for empty params", () => {
|
||||
@@ -131,8 +138,10 @@ it("doesn't add query param for empty params", () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state)).toBe(path);
|
||||
expect(getPathFromState(getStateFromPath(path) as State)).toBe(path);
|
||||
expect(getPathFromState<object>(state)).toBe(path);
|
||||
expect(
|
||||
getPathFromState<object>(getStateFromPath<object>(path) as State)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
it('handles state with config with nested screens', () => {
|
||||
@@ -208,9 +217,12 @@ it('handles state with config with nested screens', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -287,9 +299,12 @@ it('handles state with config with nested screens and exact', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -352,9 +367,12 @@ it('handles state with config with nested screens and unused configs', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -418,9 +436,12 @@ it('handles state with config with nested screens and unused configs with exact'
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -498,9 +519,12 @@ it('handles nested object with stringify in it', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -580,9 +604,12 @@ it('handles nested object with stringify in it with exact', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -623,9 +650,12 @@ it('handles nested object for second route depth', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -669,9 +699,12 @@ it('handles nested object for second route depth with exact', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -719,9 +752,12 @@ it('handles nested object for second route depth and path and stringify in roots
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -774,9 +810,12 @@ it('handles nested object for second route depth and path and stringify in roots
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -805,9 +844,12 @@ it('ignores empty string paths', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -850,9 +892,12 @@ it('keeps query params if path is empty', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toEqual(path);
|
||||
});
|
||||
|
||||
@@ -894,9 +939,12 @@ it('cuts nested configs too', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -939,9 +987,12 @@ it('cuts nested configs too with exact', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -977,9 +1028,12 @@ it('handles empty path at the end', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -1012,9 +1066,12 @@ it('returns "/" for empty path', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -1042,9 +1099,12 @@ it('parses no path specified', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -1121,9 +1181,12 @@ it('strips undefined query params', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -1201,9 +1264,12 @@ it('strips undefined query params with exact', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -1278,9 +1344,12 @@ it('handles stripping all query params', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -1357,9 +1426,12 @@ it('handles stripping all query params with exact', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -1380,9 +1452,12 @@ it('replaces undefined query params', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -1405,9 +1480,12 @@ it('matches wildcard patterns at root', () => {
|
||||
routes: [{ name: '404' }],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe('/404');
|
||||
expect(getPathFromState<object>(state, config)).toBe('/404');
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe('/404');
|
||||
});
|
||||
|
||||
@@ -1447,9 +1525,12 @@ it('matches wildcard patterns at nested level', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe('/bar/42/404');
|
||||
expect(getPathFromState<object>(state, config)).toBe('/bar/42/404');
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe('/bar/42/404');
|
||||
});
|
||||
|
||||
@@ -1492,9 +1573,12 @@ it('matches wildcard patterns at nested level with exact', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe('/404');
|
||||
expect(getPathFromState<object>(state, config)).toBe('/404');
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe('/404');
|
||||
});
|
||||
|
||||
@@ -1535,9 +1619,12 @@ it('tries to match wildcard patterns at the end', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -1570,8 +1657,11 @@ it('uses nearest parent wildcard match for unmatched paths', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe('/404');
|
||||
expect(getPathFromState<object>(state, config)).toBe('/404');
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe('/404');
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@ const changePath = <T extends InitialState>(state: T, path: string): T =>
|
||||
});
|
||||
|
||||
it('returns undefined for invalid path', () => {
|
||||
expect(getStateFromPath('//')).toBe(undefined);
|
||||
expect(getStateFromPath<object>('//')).toBe(undefined);
|
||||
});
|
||||
|
||||
it('converts path string to initial state', () => {
|
||||
@@ -41,8 +41,8 @@ it('converts path string to initial state', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state))).toEqual(
|
||||
expect(getStateFromPath<object>(path)).toEqual(state);
|
||||
expect(getStateFromPath<object>(getPathFromState<object>(state))).toEqual(
|
||||
changePath(state, '/foo/bar/baz%20qux?author=jane%20%26%20co&valid=true')
|
||||
);
|
||||
});
|
||||
@@ -106,16 +106,16 @@ it('converts path string to initial state with config', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles leading slash when converting', () => {
|
||||
const path = '/foo/bar/?count=42';
|
||||
|
||||
expect(getStateFromPath(path)).toEqual({
|
||||
expect(getStateFromPath<object>(path)).toEqual({
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
@@ -136,7 +136,7 @@ it('handles leading slash when converting', () => {
|
||||
it('handles ending slash when converting', () => {
|
||||
const path = 'foo/bar/?count=42';
|
||||
|
||||
expect(getStateFromPath(path)).toEqual({
|
||||
expect(getStateFromPath<object>(path)).toEqual({
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
@@ -167,8 +167,8 @@ it('handles route without param', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state))).toEqual(
|
||||
expect(getStateFromPath<object>(path)).toEqual(state);
|
||||
expect(getStateFromPath<object>(getPathFromState<object>(state))).toEqual(
|
||||
changePath(state, '/foo/bar')
|
||||
);
|
||||
});
|
||||
@@ -245,10 +245,10 @@ it('converts path string to initial state with config with nested screens', () =
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('converts path string to initial state with config with nested screens and unused parse functions', () => {
|
||||
@@ -308,10 +308,10 @@ it('converts path string to initial state with config with nested screens and un
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/foe/baz/Jane?count=10&answer=42&valid=true')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/foe/baz/Jane?count=10&answer=42&valid=true'));
|
||||
});
|
||||
|
||||
it('handles nested object with unused configs and with parse in it', () => {
|
||||
@@ -400,10 +400,10 @@ it('handles nested object with unused configs and with parse in it', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles parse in nested object for second route depth', () => {
|
||||
@@ -450,10 +450,10 @@ it('handles parse in nested object for second route depth', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles parse in nested object for second route depth and and path and parse in roots', () => {
|
||||
@@ -501,10 +501,10 @@ it('handles parse in nested object for second route depth and and path and parse
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles initialRouteName at top level', () => {
|
||||
@@ -545,10 +545,10 @@ it('handles initialRouteName at top level', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles initialRouteName inside a screen', () => {
|
||||
@@ -591,10 +591,10 @@ it('handles initialRouteName inside a screen', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles initialRouteName included in path', () => {
|
||||
@@ -633,10 +633,10 @@ it('handles initialRouteName included in path', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles two initialRouteNames', () => {
|
||||
@@ -728,10 +728,10 @@ it('handles two initialRouteNames', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('accepts initialRouteName without config for it', () => {
|
||||
@@ -823,10 +823,10 @@ it('accepts initialRouteName without config for it', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('returns undefined if path is empty and no matching screen is present', () => {
|
||||
@@ -847,7 +847,7 @@ it('returns undefined if path is empty and no matching screen is present', () =>
|
||||
|
||||
const path = '';
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(undefined);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('returns matching screen if path is empty', () => {
|
||||
@@ -886,10 +886,10 @@ it('returns matching screen if path is empty', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/'));
|
||||
});
|
||||
|
||||
it('returns matching screen with params if path is empty', () => {
|
||||
@@ -931,10 +931,10 @@ it('returns matching screen with params if path is empty', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/?foo=42')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/?foo=42'));
|
||||
});
|
||||
|
||||
it("doesn't match nested screen if path is empty", () => {
|
||||
@@ -959,7 +959,7 @@ it("doesn't match nested screen if path is empty", () => {
|
||||
|
||||
const path = '';
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(undefined);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('chooses more exhaustive pattern', () => {
|
||||
@@ -1004,10 +1004,10 @@ it('chooses more exhaustive pattern', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles same paths beginnings', () => {
|
||||
@@ -1048,10 +1048,10 @@ it('handles same paths beginnings', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles same paths beginnings with params', () => {
|
||||
@@ -1096,10 +1096,10 @@ it('handles same paths beginnings with params', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles not taking path with too many segments', () => {
|
||||
@@ -1151,10 +1151,10 @@ it('handles not taking path with too many segments', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles differently ordered params v1', () => {
|
||||
@@ -1206,10 +1206,10 @@ it('handles differently ordered params v1', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles differently ordered params v2', () => {
|
||||
@@ -1261,10 +1261,10 @@ it('handles differently ordered params v2', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles differently ordered params v3', () => {
|
||||
@@ -1316,10 +1316,10 @@ it('handles differently ordered params v3', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles differently ordered params v4', () => {
|
||||
@@ -1371,10 +1371,10 @@ it('handles differently ordered params v4', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/5/foos/res/20')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/5/foos/res/20'));
|
||||
});
|
||||
|
||||
it('handles simple optional params', () => {
|
||||
@@ -1426,10 +1426,10 @@ it('handles simple optional params', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handle 2 optional params at the end v1', () => {
|
||||
@@ -1481,10 +1481,10 @@ it('handle 2 optional params at the end v1', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handle 2 optional params at the end v2', () => {
|
||||
@@ -1536,10 +1536,10 @@ it('handle 2 optional params at the end v2', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handle 2 optional params at the end v3', () => {
|
||||
@@ -1592,10 +1592,10 @@ it('handle 2 optional params at the end v3', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handle optional params in the middle v1', () => {
|
||||
@@ -1648,10 +1648,10 @@ it('handle optional params in the middle v1', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handle optional params in the middle v2', () => {
|
||||
@@ -1704,10 +1704,10 @@ it('handle optional params in the middle v2', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handle optional params in the middle v3', () => {
|
||||
@@ -1761,10 +1761,10 @@ it('handle optional params in the middle v3', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handle optional params in the middle v4', () => {
|
||||
@@ -1818,10 +1818,10 @@ it('handle optional params in the middle v4', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handle optional params in the middle v5', () => {
|
||||
@@ -1875,10 +1875,10 @@ it('handle optional params in the middle v5', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handle optional params in the beginning v1', () => {
|
||||
@@ -1932,10 +1932,10 @@ it('handle optional params in the beginning v1', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/5/10/foos/15')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/5/10/foos/15'));
|
||||
});
|
||||
|
||||
it('handle optional params in the beginning v2', () => {
|
||||
@@ -1989,10 +1989,10 @@ it('handle optional params in the beginning v2', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/5/10/foos/15')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/5/10/foos/15'));
|
||||
});
|
||||
|
||||
it('merges parent patterns if needed', () => {
|
||||
@@ -2030,10 +2030,10 @@ it('merges parent patterns if needed', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/foo/42/baz/babel')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/foo/42/baz/babel'));
|
||||
});
|
||||
|
||||
it('ignores extra slashes in the pattern', () => {
|
||||
@@ -2067,10 +2067,10 @@ it('ignores extra slashes in the pattern', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('matches wildcard patterns at root', () => {
|
||||
@@ -2092,10 +2092,10 @@ it('matches wildcard patterns at root', () => {
|
||||
routes: [{ name: '404', path }],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/404')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/404'));
|
||||
});
|
||||
|
||||
it('matches wildcard patterns at nested level', () => {
|
||||
@@ -2134,10 +2134,10 @@ it('matches wildcard patterns at nested level', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/bar/42/404')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/bar/42/404'));
|
||||
});
|
||||
|
||||
it('matches wildcard patterns at nested level with exact', () => {
|
||||
@@ -2179,10 +2179,10 @@ it('matches wildcard patterns at nested level with exact', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/404')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/404'));
|
||||
});
|
||||
|
||||
it('tries to match wildcard patterns at the end', () => {
|
||||
@@ -2222,10 +2222,10 @@ it('tries to match wildcard patterns at the end', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('uses nearest parent wildcard match for unmatched paths', () => {
|
||||
@@ -2257,17 +2257,17 @@ it('uses nearest parent wildcard match for unmatched paths', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/404')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/404'));
|
||||
});
|
||||
|
||||
it('throws if two screens map to the same pattern', () => {
|
||||
const path = '/bar/42/baz/test';
|
||||
|
||||
expect(() =>
|
||||
getStateFromPath(path, {
|
||||
getStateFromPath<object>(path, {
|
||||
screens: {
|
||||
Foo: {
|
||||
screens: {
|
||||
@@ -2287,7 +2287,7 @@ it('throws if two screens map to the same pattern', () => {
|
||||
);
|
||||
|
||||
expect(() =>
|
||||
getStateFromPath(path, {
|
||||
getStateFromPath<object>(path, {
|
||||
screens: {
|
||||
Foo: {
|
||||
screens: {
|
||||
@@ -2354,10 +2354,10 @@ it('correctly applies initialRouteName for config with similar route names', ()
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('correctly applies initialRouteName for config with similar route names v2', () => {
|
||||
@@ -2414,8 +2414,8 @@ it('correctly applies initialRouteName for config with similar route names v2',
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { CommonActions, ParamListBase } from '@react-navigation/routers';
|
||||
import { CommonActions } from '@react-navigation/routers';
|
||||
import type { NavigationContainerRefWithCurrent } from './types';
|
||||
|
||||
export const NOT_INITIALIZED_ERROR =
|
||||
"The 'navigation' object hasn't been initialized yet. This might happen if you don't have a navigator mounted, or if the navigator hasn't finished mounting. See https://reactnavigation.org/docs/navigating-without-navigation-prop#handling-initialization for more details.";
|
||||
|
||||
export default function createNavigationContainerRef<
|
||||
ParamList extends ParamListBase
|
||||
ParamList extends {} = ReactNavigation.RootParamList
|
||||
>(): NavigationContainerRefWithCurrent<ParamList> {
|
||||
const methods = [
|
||||
...Object.keys(CommonActions),
|
||||
|
||||
@@ -13,7 +13,10 @@ type ConfigItem = {
|
||||
screens?: Record<string, ConfigItem>;
|
||||
};
|
||||
|
||||
type Options = { initialRouteName?: string; screens: PathConfigMap };
|
||||
type Options = {
|
||||
initialRouteName?: string;
|
||||
screens: PathConfigMap<object>;
|
||||
};
|
||||
|
||||
type NavigateAction<State extends NavigationState> = {
|
||||
type: 'NAVIGATE';
|
||||
@@ -29,7 +32,9 @@ export default function getActionFromState(
|
||||
options?: Options
|
||||
): NavigateAction<NavigationState> | CommonActions.Action | undefined {
|
||||
// Create a normalized configs object which will be easier to use
|
||||
const normalizedConfig = options ? createNormalizedConfigItem(options) : {};
|
||||
const normalizedConfig = options
|
||||
? createNormalizedConfigItem(options as PathConfig<object> | string)
|
||||
: {};
|
||||
|
||||
const routes =
|
||||
state.index != null ? state.routes.slice(0, state.index + 1) : state.routes;
|
||||
@@ -130,7 +135,7 @@ export default function getActionFromState(
|
||||
};
|
||||
}
|
||||
|
||||
const createNormalizedConfigItem = (config: PathConfig | string) =>
|
||||
const createNormalizedConfigItem = (config: PathConfig<object> | string) =>
|
||||
typeof config === 'object' && config != null
|
||||
? {
|
||||
initialRouteName: config.initialRouteName,
|
||||
@@ -141,7 +146,7 @@ const createNormalizedConfigItem = (config: PathConfig | string) =>
|
||||
}
|
||||
: {};
|
||||
|
||||
const createNormalizedConfigs = (options: PathConfigMap) =>
|
||||
const createNormalizedConfigs = (options: PathConfigMap<object>) =>
|
||||
Object.entries(options).reduce<Record<string, ConfigItem>>((acc, [k, v]) => {
|
||||
acc[k] = createNormalizedConfigItem(v);
|
||||
return acc;
|
||||
|
||||
@@ -7,7 +7,10 @@ import type {
|
||||
import fromEntries from './fromEntries';
|
||||
import type { PathConfig, PathConfigMap } from './types';
|
||||
|
||||
type Options = { initialRouteName?: string; screens: PathConfigMap };
|
||||
type Options<ParamList> = {
|
||||
initialRouteName?: string;
|
||||
screens: PathConfigMap<ParamList>;
|
||||
};
|
||||
|
||||
type State = NavigationState | Omit<PartialState<NavigationState>, 'stale'>;
|
||||
|
||||
@@ -61,9 +64,9 @@ const getActiveRoute = (state: State): { name: string; params?: object } => {
|
||||
* @param options Extra options to fine-tune how to serialize the path.
|
||||
* @returns Path representing the state, e.g. /foo/bar?count=42.
|
||||
*/
|
||||
export default function getPathFromState(
|
||||
export default function getPathFromState<ParamList extends {}>(
|
||||
state: State,
|
||||
options?: Options
|
||||
options?: Options<ParamList>
|
||||
): string {
|
||||
if (state == null) {
|
||||
throw Error(
|
||||
@@ -238,7 +241,7 @@ const joinPaths = (...paths: string[]): string =>
|
||||
.join('/');
|
||||
|
||||
const createConfigItem = (
|
||||
config: PathConfig | string,
|
||||
config: PathConfig<object> | string,
|
||||
parentPattern?: string
|
||||
): ConfigItem => {
|
||||
if (typeof config === 'string') {
|
||||
@@ -276,7 +279,7 @@ const createConfigItem = (
|
||||
};
|
||||
|
||||
const createNormalizedConfigs = (
|
||||
options: PathConfigMap,
|
||||
options: PathConfigMap<object>,
|
||||
pattern?: string
|
||||
): Record<string, ConfigItem> =>
|
||||
fromEntries(
|
||||
|
||||
@@ -8,9 +8,9 @@ import type {
|
||||
import findFocusedRoute from './findFocusedRoute';
|
||||
import type { PathConfigMap } from './types';
|
||||
|
||||
type Options = {
|
||||
type Options<ParamList extends {}> = {
|
||||
initialRouteName?: string;
|
||||
screens: PathConfigMap;
|
||||
screens: PathConfigMap<ParamList>;
|
||||
};
|
||||
|
||||
type ParseConfig = Record<string, (value: string) => any>;
|
||||
@@ -60,9 +60,9 @@ type ParsedRoute = {
|
||||
* @param path Path string to parse and convert, e.g. /foo/bar?count=42.
|
||||
* @param options Extra options to fine-tune how to parse the path.
|
||||
*/
|
||||
export default function getStateFromPath(
|
||||
export default function getStateFromPath<ParamList extends {}>(
|
||||
path: string,
|
||||
options?: Options
|
||||
options?: Options<ParamList>
|
||||
): ResultState | undefined {
|
||||
let initialRoutes: InitialRouteConfig[] = [];
|
||||
|
||||
@@ -106,7 +106,7 @@ export default function getStateFromPath(
|
||||
...Object.keys(screens).map((key) =>
|
||||
createNormalizedConfigs(
|
||||
key,
|
||||
screens as PathConfigMap,
|
||||
screens as PathConfigMap<object>,
|
||||
[],
|
||||
initialRoutes,
|
||||
[]
|
||||
@@ -307,7 +307,7 @@ const matchAgainstConfigs = (remaining: string, configs: RouteConfig[]) => {
|
||||
|
||||
const createNormalizedConfigs = (
|
||||
screen: string,
|
||||
routeConfig: PathConfigMap,
|
||||
routeConfig: PathConfigMap<object>,
|
||||
routeNames: string[] = [],
|
||||
initials: InitialRouteConfig[],
|
||||
parentScreens: string[],
|
||||
@@ -319,6 +319,7 @@ const createNormalizedConfigs = (
|
||||
|
||||
parentScreens.push(screen);
|
||||
|
||||
// @ts-expect-error: we can't strongly typecheck this for now
|
||||
const config = routeConfig[screen];
|
||||
|
||||
if (typeof config === 'string') {
|
||||
@@ -345,7 +346,13 @@ const createNormalizedConfigs = (
|
||||
: config.path || '';
|
||||
|
||||
configs.push(
|
||||
createConfigItem(screen, routeNames, pattern, config.path, config.parse)
|
||||
createConfigItem(
|
||||
screen,
|
||||
routeNames,
|
||||
pattern!,
|
||||
config.path,
|
||||
config.parse
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -361,7 +368,7 @@ const createNormalizedConfigs = (
|
||||
Object.keys(config.screens).forEach((nestedConfig) => {
|
||||
const result = createNormalizedConfigs(
|
||||
nestedConfig,
|
||||
config.screens as PathConfigMap,
|
||||
config.screens as PathConfigMap<object>,
|
||||
routeNames,
|
||||
initials,
|
||||
[...parentScreens],
|
||||
|
||||
@@ -9,6 +9,14 @@ import type {
|
||||
ParamListBase,
|
||||
} from '@react-navigation/routers';
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace ReactNavigation {
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
interface RootParamList {}
|
||||
}
|
||||
}
|
||||
|
||||
type Keyof<T extends {}> = Extract<keyof T, string>;
|
||||
|
||||
export type DefaultNavigatorOptions<
|
||||
@@ -265,7 +273,7 @@ export type NavigationContainerProps = {
|
||||
};
|
||||
|
||||
export type NavigationProp<
|
||||
ParamList extends ParamListBase,
|
||||
ParamList extends {},
|
||||
RouteName extends keyof ParamList = Keyof<ParamList>,
|
||||
State extends NavigationState = NavigationState<ParamList>,
|
||||
ScreenOptions extends {} = {},
|
||||
@@ -510,7 +518,7 @@ export type NavigationContainerEventMap = {
|
||||
};
|
||||
|
||||
export type NavigationContainerRef<
|
||||
ParamList extends ParamListBase
|
||||
ParamList extends {}
|
||||
> = NavigationHelpers<ParamList> &
|
||||
EventConsumer<NavigationContainerEventMap> & {
|
||||
/**
|
||||
@@ -538,7 +546,7 @@ export type NavigationContainerRef<
|
||||
};
|
||||
|
||||
export type NavigationContainerRefWithCurrent<
|
||||
ParamList extends ParamListBase
|
||||
ParamList extends {}
|
||||
> = NavigationContainerRef<ParamList> & {
|
||||
current: NavigationContainerRef<ParamList> | null;
|
||||
};
|
||||
@@ -601,15 +609,20 @@ export type NavigatorScreenParams<
|
||||
};
|
||||
}[keyof ParamList];
|
||||
|
||||
export type PathConfig = {
|
||||
export type PathConfig<ParamList extends {}> = {
|
||||
path?: string;
|
||||
exact?: boolean;
|
||||
parse?: Record<string, (value: string) => any>;
|
||||
stringify?: Record<string, (value: any) => string>;
|
||||
screens?: PathConfigMap;
|
||||
initialRouteName?: string;
|
||||
screens?: PathConfigMap<ParamList>;
|
||||
initialRouteName?: keyof ParamList;
|
||||
};
|
||||
|
||||
export type PathConfigMap = {
|
||||
[routeName: string]: string | PathConfig;
|
||||
export type PathConfigMap<ParamList extends {}> = {
|
||||
[RouteName in keyof ParamList]?: ParamList[RouteName] extends NavigatorScreenParams<
|
||||
infer T,
|
||||
any
|
||||
>
|
||||
? string | PathConfig<T>
|
||||
: string | Omit<PathConfig<{}>, 'screens' | 'initialRouteName'>;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import type { ParamListBase } from '@react-navigation/routers';
|
||||
import NavigationContext from './NavigationContext';
|
||||
import type { NavigationProp } from './types';
|
||||
|
||||
@@ -9,7 +8,7 @@ import type { NavigationProp } from './types';
|
||||
* @returns Navigation prop of the parent screen.
|
||||
*/
|
||||
export default function useNavigation<
|
||||
T extends NavigationProp<ParamListBase>
|
||||
T extends NavigationProp<any> = NavigationProp<ReactNavigation.RootParamList>
|
||||
>(): T {
|
||||
const navigation = React.useContext(NavigationContext);
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import * as React from 'react';
|
||||
import type { ParamListBase } from '@react-navigation/core';
|
||||
import type { LinkingOptions } from './types';
|
||||
|
||||
const LinkingContext = React.createContext<{
|
||||
options: LinkingOptions | undefined;
|
||||
options: LinkingOptions<ParamListBase> | undefined;
|
||||
}>({ options: undefined });
|
||||
|
||||
LinkingContext.displayName = 'LinkingContext';
|
||||
|
||||
@@ -14,9 +14,9 @@ import useDocumentTitle from './useDocumentTitle';
|
||||
import useBackButton from './useBackButton';
|
||||
import type { Theme, LinkingOptions, DocumentTitleOptions } from './types';
|
||||
|
||||
type Props = NavigationContainerProps & {
|
||||
type Props<ParamList extends {}> = NavigationContainerProps & {
|
||||
theme?: Theme;
|
||||
linking?: LinkingOptions;
|
||||
linking?: LinkingOptions<ParamList>;
|
||||
fallback?: React.ReactNode;
|
||||
documentTitle?: DocumentTitleOptions;
|
||||
onReady?: () => void;
|
||||
@@ -36,7 +36,7 @@ type Props = NavigationContainerProps & {
|
||||
* @param props.children Child elements to render the content.
|
||||
* @param props.ref Ref object which refers to the navigation object containing helper methods.
|
||||
*/
|
||||
const NavigationContainer = React.forwardRef(function NavigationContainer(
|
||||
function NavigationContainerInner(
|
||||
{
|
||||
theme = DefaultTheme,
|
||||
linking,
|
||||
@@ -44,7 +44,7 @@ const NavigationContainer = React.forwardRef(function NavigationContainer(
|
||||
documentTitle,
|
||||
onReady,
|
||||
...rest
|
||||
}: Props,
|
||||
}: Props<ParamListBase>,
|
||||
ref?: React.Ref<NavigationContainerRef<ParamListBase> | null>
|
||||
) {
|
||||
const isLinkingEnabled = linking ? linking.enabled !== false : false;
|
||||
@@ -101,6 +101,14 @@ const NavigationContainer = React.forwardRef(function NavigationContainer(
|
||||
</ThemeProvider>
|
||||
</LinkingContext.Provider>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const NavigationContainer = React.forwardRef(NavigationContainerInner) as <
|
||||
RootParamList extends {} = ReactNavigation.RootParamList
|
||||
>(
|
||||
props: Props<RootParamList> & {
|
||||
ref?: React.Ref<NavigationContainerRef<RootParamList>>;
|
||||
}
|
||||
) => React.ReactElement;
|
||||
|
||||
export default NavigationContainer;
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
StackRouter,
|
||||
TabRouter,
|
||||
NavigationHelpersContext,
|
||||
NavigatorScreenParams,
|
||||
} from '@react-navigation/core';
|
||||
import { renderToString } from 'react-dom/server';
|
||||
import NavigationContainer from '../NavigationContainer';
|
||||
@@ -36,24 +37,37 @@ it('renders correct state with location', () => {
|
||||
);
|
||||
});
|
||||
|
||||
const Stack = createStackNavigator();
|
||||
type StackAParamList = {
|
||||
Home: NavigatorScreenParams<StackBParamList>;
|
||||
Chat: undefined;
|
||||
};
|
||||
|
||||
type StackBParamList = {
|
||||
Profile: undefined;
|
||||
Settings: undefined;
|
||||
Feed: undefined;
|
||||
Updates: undefined;
|
||||
};
|
||||
|
||||
const StackA = createStackNavigator<StackAParamList>();
|
||||
const StackB = createStackNavigator<StackBParamList>();
|
||||
|
||||
const TestScreen = ({ route }: any): any =>
|
||||
`${route.name} ${JSON.stringify(route.params)}`;
|
||||
|
||||
const NestedStack = () => {
|
||||
return (
|
||||
<Stack.Navigator initialRouteName="Feed">
|
||||
<Stack.Screen name="Profile" component={TestScreen} />
|
||||
<Stack.Screen name="Settings" component={TestScreen} />
|
||||
<Stack.Screen name="Feed" component={TestScreen} />
|
||||
<Stack.Screen name="Updates" component={TestScreen} />
|
||||
</Stack.Navigator>
|
||||
<StackB.Navigator initialRouteName="Feed">
|
||||
<StackB.Screen name="Profile" component={TestScreen} />
|
||||
<StackB.Screen name="Settings" component={TestScreen} />
|
||||
<StackB.Screen name="Feed" component={TestScreen} />
|
||||
<StackB.Screen name="Updates" component={TestScreen} />
|
||||
</StackB.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const element = (
|
||||
<NavigationContainer
|
||||
<NavigationContainer<StackAParamList>
|
||||
linking={{
|
||||
prefixes: [],
|
||||
config: {
|
||||
@@ -73,10 +87,10 @@ it('renders correct state with location', () => {
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack.Navigator>
|
||||
<Stack.Screen name="Home" component={NestedStack} />
|
||||
<Stack.Screen name="Chat" component={TestScreen} />
|
||||
</Stack.Navigator>
|
||||
<StackA.Navigator>
|
||||
<StackA.Screen name="Home" component={NestedStack} />
|
||||
<StackA.Screen name="Chat" component={TestScreen} />
|
||||
</StackA.Navigator>
|
||||
</NavigationContainer>
|
||||
);
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ export type Theme = {
|
||||
};
|
||||
};
|
||||
|
||||
export type LinkingOptions = {
|
||||
export type LinkingOptions<ParamList extends {}> = {
|
||||
/**
|
||||
* Whether deep link handling should be enabled.
|
||||
* Defaults to true.
|
||||
@@ -53,7 +53,10 @@ export type LinkingOptions = {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
config?: { initialRouteName?: string; screens: PathConfigMap };
|
||||
config?: {
|
||||
initialRouteName?: keyof ParamList;
|
||||
screens: PathConfigMap<ParamList>;
|
||||
};
|
||||
/**
|
||||
* Custom function to get the initial URL used for linking.
|
||||
* Uses `Linking.getInitialURL()` by default.
|
||||
|
||||
@@ -45,7 +45,7 @@ export default function useLinking(
|
||||
};
|
||||
},
|
||||
getStateFromPath = getStateFromPathDefault,
|
||||
}: LinkingOptions
|
||||
}: LinkingOptions<ParamListBase>
|
||||
) {
|
||||
React.useEffect(() => {
|
||||
if (enabled !== false && isUsingLinking) {
|
||||
|
||||
@@ -295,7 +295,7 @@ export default function useLinking(
|
||||
config,
|
||||
getStateFromPath = getStateFromPathDefault,
|
||||
getPathFromState = getPathFromStateDefault,
|
||||
}: LinkingOptions
|
||||
}: LinkingOptions<ParamListBase>
|
||||
) {
|
||||
React.useEffect(() => {
|
||||
if (enabled !== false && isUsingLinking) {
|
||||
|
||||
Reference in New Issue
Block a user