Skip to content

Commit

Permalink
fix: fix handling groups with linking config and types
Browse files Browse the repository at this point in the history
  • Loading branch information
satya164 committed Mar 9, 2024
1 parent 750a6fb commit a847715
Show file tree
Hide file tree
Showing 3 changed files with 319 additions and 49 deletions.
48 changes: 46 additions & 2 deletions example/__typechecks__/static.check.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import type {
NavigationProp,
NavigatorScreenParams,
StaticParamList,
StaticScreenProps,
} from '@react-navigation/native';
Expand Down Expand Up @@ -58,9 +59,9 @@ const RootStack = createStackNavigator({
},
});

type ParamList = StaticParamList<typeof RootStack>;
type RootParamList = StaticParamList<typeof RootStack>;

declare const navigation: NavigationProp<ParamList>;
declare let navigation: NavigationProp<RootParamList>;

/**
* Infer screen names from config
Expand Down Expand Up @@ -198,3 +199,46 @@ createStackNavigator({});
createStackNavigator({
screens: {},
});

/**
* Infer types from group without screens
*/
const MyTabs = createBottomTabNavigator({
groups: {
Test: {
screens: {
Test: (_: StaticScreenProps<{ foo: string }>) => null,
},
},
},
});

const MyStack = createStackNavigator({
groups: {
Guest: {
screens: {
Login: () => null,
},
},
User: {
screens: {
Home: () => null,
Profile: (_: StaticScreenProps<{ id: number }>) => null,
Forum: MyTabs,
},
},
},
});

type MyParamList = StaticParamList<typeof MyStack>;

expectTypeOf<MyParamList>().toMatchTypeOf<{
Login: undefined;
Home: undefined;
Profile: { id: number };
Forum:
| NavigatorScreenParams<{
Test: { foo: string };
}>
| undefined;
}>();
139 changes: 95 additions & 44 deletions packages/core/src/StaticNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,18 +161,38 @@ export type StaticConfig<
> &
DefaultNavigatorOptions<ParamList, State, ScreenOptions, EventMap>,
'screens' | 'children'
> & {
/**
* Screens to render in the navigator and their configuration.
*/
screens: StaticConfigScreens<ParamList, State, ScreenOptions, EventMap>;
/**
* Groups of screens to render in the navigator and their configuration.
*/
groups?: {
[key: string]: GroupConfig<ParamList, State, ScreenOptions, EventMap>;
};
};
> &
(
| {
/**
* Screens to render in the navigator and their configuration.
*/
screens: StaticConfigScreens<ParamList, State, ScreenOptions, EventMap>;
/**
* Groups of screens to render in the navigator and their configuration.
*/
groups?: {
[key: string]: GroupConfig<ParamList, State, ScreenOptions, EventMap>;
};
}
| {
/**
* Screens to render in the navigator and their configuration.
*/
screens?: StaticConfigScreens<
ParamList,
State,
ScreenOptions,
EventMap
>;
/**
* Groups of screens to render in the navigator and their configuration.
*/
groups: {
[key: string]: GroupConfig<ParamList, State, ScreenOptions, EventMap>;
};
}
);

/**
* Props for a screen component which is rendered by a static navigator.
Expand All @@ -190,7 +210,7 @@ export type StaticScreenProps<T extends Record<string, unknown> | undefined> = {
export type StaticParamList<
T extends {
readonly config: {
readonly screens: Record<string, any>;
readonly screens?: Record<string, any>;
readonly groups?: {
[key: string]: {
screens: Record<string, any>;
Expand Down Expand Up @@ -300,13 +320,13 @@ export function createComponentForStaticNavigation(
const { Navigator, Group, Screen, config } = tree;
const { screens, groups, ...rest } = config;

if (screens == null) {
if (screens == null && groups == null) {
throw new Error(
"Couldn't find a 'screens' property. Make sure to define your screens under a 'screens' property in the configuration."
"Couldn't find a 'screens' or 'groups' property. Make sure to define your screens under a 'screens' property in the configuration."
);
}

const items = getItemsFromScreens(Screen, screens);
const items = screens ? getItemsFromScreens(Screen, screens) : [];

if (groups) {
items.push(
Expand Down Expand Up @@ -363,41 +383,72 @@ export function createComponentForStaticNavigation(
*/
export function createPathConfigForStaticNavigation(tree: {
config: {
screens: StaticConfigScreens<
screens?: StaticConfigScreens<
ParamListBase,
NavigationState,
{},
EventMapBase
>;
groups?: {
[key: string]: {
screens: StaticConfigScreens<
ParamListBase,
NavigationState,
{},
EventMapBase
>;
};
};
};
}) {
return Object.fromEntries(
Object.entries(tree.config.screens)
.map(([key, item]) => {
const screenConfig: PathConfig<ParamListBase> = {};

if ('linking' in item) {
if (typeof item.linking === 'string') {
screenConfig.path = item.linking;
} else {
Object.assign(screenConfig, item.linking);
function createPathConfigForScreens(
screens: StaticConfigScreens<
ParamListBase,
NavigationState,
{},
EventMapBase
>
) {
return Object.fromEntries(
Object.entries(screens)
.map(([key, item]) => {
const screenConfig: PathConfig<ParamListBase> = {};

if ('linking' in item) {
if (typeof item.linking === 'string') {
screenConfig.path = item.linking;
} else {
Object.assign(screenConfig, item.linking);
}
}
}

if ('config' in item) {
screenConfig.screens = createPathConfigForStaticNavigation(item);
} else if (
'screen' in item &&
'config' in item.screen &&
item.screen.config.screens
) {
screenConfig.screens = createPathConfigForStaticNavigation(
item.screen
);
}

return [key, screenConfig] as const;
})
.filter(([, screen]) => Object.keys(screen).length > 0)
);
if ('config' in item) {
screenConfig.screens = createPathConfigForStaticNavigation(item);
} else if (
'screen' in item &&
'config' in item.screen &&
(item.screen.config.screens || item.screen.config.groups)
) {
screenConfig.screens = createPathConfigForStaticNavigation(
item.screen
);
}

return [key, screenConfig] as const;
})
.filter(([, screen]) => Object.keys(screen).length > 0)
);
}

const config = tree.config.screens
? createPathConfigForScreens(tree.config.screens)
: {};

if (tree.config.groups) {
Object.entries(tree.config.groups).forEach(([, group]) => {
Object.assign(config, createPathConfigForScreens(group.screens));
});
}

return config;
}
Loading

0 comments on commit a847715

Please sign in to comment.