forked from Expensify/App
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request Expensify#53466 from software-mansion-labs/fix/use…
…-navigation-reset-on-layout-change The central page page is not rendered when resized to narrow and then to wide view again
- Loading branch information
Showing
4 changed files
with
128 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 10 additions & 2 deletions
12
src/libs/Navigation/AppNavigator/useNavigationResetOnLayoutChange.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
src/libs/Navigation/AppNavigator/useNavigationResetRootOnLayoutChange.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import {useEffect} from 'react'; | ||
import useResponsiveLayout from '@hooks/useResponsiveLayout'; | ||
import navigationRef from '@libs/Navigation/navigationRef'; | ||
|
||
/** | ||
* This hook resets the navigation root state when changing the layout size, resetting the state calls the getRehydredState method in CustomRouter.ts. | ||
* When the screen size is changed, it is necessary to check whether the application displays the content correctly. | ||
* When the app is opened on a small layout and the user resizes it to wide, a second screen has to be present in the navigation state to fill the space. | ||
*/ | ||
function useNavigationResetRootOnLayoutChange() { | ||
const {shouldUseNarrowLayout} = useResponsiveLayout(); | ||
|
||
useEffect(() => { | ||
if (!navigationRef.isReady()) { | ||
return; | ||
} | ||
navigationRef.resetRoot(navigationRef.getRootState()); | ||
}, [shouldUseNarrowLayout]); | ||
} | ||
|
||
export default useNavigationResetRootOnLayoutChange; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import {NavigationContainer} from '@react-navigation/native'; | ||
import {render, renderHook} from '@testing-library/react-native'; | ||
import React from 'react'; | ||
import useResponsiveLayout from '@hooks/useResponsiveLayout'; | ||
import type ResponsiveLayoutResult from '@hooks/useResponsiveLayout/types'; | ||
import getIsNarrowLayout from '@libs/getIsNarrowLayout'; | ||
import createResponsiveStackNavigator from '@libs/Navigation/AppNavigator/createResponsiveStackNavigator'; | ||
import BottomTabNavigator from '@libs/Navigation/AppNavigator/Navigators/BottomTabNavigator'; | ||
import useNavigationResetRootOnLayoutChange from '@libs/Navigation/AppNavigator/useNavigationResetRootOnLayoutChange'; | ||
import navigationRef from '@libs/Navigation/navigationRef'; | ||
import type {AuthScreensParamList} from '@libs/Navigation/types'; | ||
import ProfilePage from '@pages/settings/Profile/ProfilePage'; | ||
import NAVIGATORS from '@src/NAVIGATORS'; | ||
import SCREENS from '@src/SCREENS'; | ||
|
||
const RootStack = createResponsiveStackNavigator<AuthScreensParamList>(); | ||
|
||
jest.mock('@hooks/useResponsiveLayout', () => jest.fn()); | ||
jest.mock('@libs/getIsNarrowLayout', () => jest.fn()); | ||
|
||
jest.mock('@pages/settings/InitialSettingsPage'); | ||
jest.mock('@pages/settings/Profile/ProfilePage'); | ||
jest.mock('@libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar'); | ||
|
||
const DEFAULT_USE_RESPONSIVE_LAYOUT_VALUE: ResponsiveLayoutResult = { | ||
shouldUseNarrowLayout: true, | ||
isSmallScreenWidth: true, | ||
isInNarrowPaneModal: false, | ||
isExtraSmallScreenHeight: false, | ||
isMediumScreenWidth: false, | ||
isLargeScreenWidth: false, | ||
isExtraSmallScreenWidth: false, | ||
isSmallScreen: false, | ||
onboardingIsMediumOrLargerScreenWidth: false, | ||
}; | ||
|
||
const INITIAL_STATE = { | ||
routes: [ | ||
{ | ||
name: NAVIGATORS.BOTTOM_TAB_NAVIGATOR, | ||
state: { | ||
index: 1, | ||
routes: [{name: SCREENS.HOME}, {name: SCREENS.SETTINGS.ROOT}], | ||
}, | ||
}, | ||
], | ||
}; | ||
|
||
const mockedGetIsNarrowLayout = getIsNarrowLayout as jest.MockedFunction<typeof getIsNarrowLayout>; | ||
const mockedUseResponsiveLayout = useResponsiveLayout as jest.MockedFunction<typeof useResponsiveLayout>; | ||
|
||
describe('Resize screen', () => { | ||
it('Should display the settings profile after resizing the screen with the settings page opened to the wide layout', () => { | ||
// Given the initialized navigation on the narrow layout with the settings screen | ||
mockedGetIsNarrowLayout.mockReturnValue(true); | ||
mockedUseResponsiveLayout.mockReturnValue({...DEFAULT_USE_RESPONSIVE_LAYOUT_VALUE, shouldUseNarrowLayout: true}); | ||
|
||
const {rerender} = renderHook(() => useNavigationResetRootOnLayoutChange()); | ||
|
||
render( | ||
<NavigationContainer | ||
ref={navigationRef} | ||
initialState={INITIAL_STATE} | ||
> | ||
<RootStack.Navigator> | ||
<RootStack.Screen | ||
name={NAVIGATORS.BOTTOM_TAB_NAVIGATOR} | ||
component={BottomTabNavigator} | ||
/> | ||
|
||
<RootStack.Screen | ||
name={SCREENS.SETTINGS.PROFILE.ROOT} | ||
component={ProfilePage} | ||
/> | ||
</RootStack.Navigator> | ||
</NavigationContainer>, | ||
); | ||
|
||
const rootStateBeforeResize = navigationRef.current?.getRootState(); | ||
|
||
expect(rootStateBeforeResize?.routes.at(0)?.name).toBe(NAVIGATORS.BOTTOM_TAB_NAVIGATOR); | ||
expect(rootStateBeforeResize?.routes.at(1)).toBeUndefined(); | ||
|
||
// When resizing the screen to the wide layout | ||
mockedGetIsNarrowLayout.mockReturnValue(false); | ||
mockedUseResponsiveLayout.mockReturnValue({...DEFAULT_USE_RESPONSIVE_LAYOUT_VALUE, shouldUseNarrowLayout: false}); | ||
rerender({}); | ||
|
||
const rootStateAfterResize = navigationRef.current?.getRootState(); | ||
|
||
// Then the settings profile page should be displayed on the screen | ||
expect(rootStateAfterResize?.routes.at(0)?.name).toBe(NAVIGATORS.BOTTOM_TAB_NAVIGATOR); | ||
expect(rootStateAfterResize?.routes.at(1)?.name).toBe(SCREENS.SETTINGS.PROFILE.ROOT); | ||
}); | ||
}); |