From 2e08df9a1d6c759b8f4f3086d9736973dc8d5d00 Mon Sep 17 00:00:00 2001 From: "satyajit.happy" Date: Thu, 13 Jun 2019 12:07:27 +0200 Subject: [PATCH] feat: add an onStateChange prop to NavigationContainer --- .babelrc | 1 + package.json | 8 +- src/NavigationContainer.tsx | 13 ++- .../__snapshots__/index.test.tsx.snap | 27 ++++++ src/__tests__/index.test.ts | 1 - src/__tests__/index.test.tsx | 87 +++++++++++++++++++ src/useNavigationBuilder.tsx | 2 +- yarn.lock | 57 +++++++++++- 8 files changed, 188 insertions(+), 8 deletions(-) create mode 100644 src/__tests__/__snapshots__/index.test.tsx.snap delete mode 100644 src/__tests__/index.test.ts create mode 100644 src/__tests__/index.test.tsx diff --git a/.babelrc b/.babelrc index 6f491d86..789df224 100644 --- a/.babelrc +++ b/.babelrc @@ -7,6 +7,7 @@ "corejs": 3 } ], + "@babel/preset-react", "@babel/preset-typescript" ] } diff --git a/package.json b/package.json index a94a92eb..98d1d2b1 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "@babel/cli": "^7.4.4", "@babel/core": "^7.4.5", "@babel/preset-env": "^7.4.5", + "@babel/preset-react": "^7.0.0", "@babel/preset-typescript": "^7.3.3", "@commitlint/config-conventional": "^8.0.0", "@release-it/conventional-changelog": "^1.1.0", @@ -58,6 +59,8 @@ "prettier": "^1.18.2", "react": "^16.8.6", "react-dom": "^16.8.6", + "react-native-testing-library": "^1.9.1", + "react-test-renderer": "^16.8.6", "release-it": "^12.3.0", "typescript": "^3.5.1" }, @@ -73,6 +76,9 @@ "/types/", "/lib/" ], - "testRegex": "/__tests__/.*\\.(test|spec)\\.(js|tsx?)$" + "testRegex": "/__tests__/.*\\.(test|spec)\\.(js|tsx?)$", + "transform": { + "^.+\\.(js|ts|tsx)$": "babel-jest" + } } } diff --git a/src/NavigationContainer.tsx b/src/NavigationContainer.tsx index 97aba9fb..c61d80f5 100644 --- a/src/NavigationContainer.tsx +++ b/src/NavigationContainer.tsx @@ -4,6 +4,7 @@ import EnsureSingleNavigator from './EnsureSingleNavigator'; type Props = { initialState?: InitialState; + onStateChange?: (state: NavigationState | InitialState) => void; children: React.ReactNode; }; @@ -23,7 +24,11 @@ export const NavigationStateContext = React.createContext<{ }, }); -export default function NavigationContainer({ initialState, children }: Props) { +export default function NavigationContainer({ + initialState, + onStateChange, + children, +}: Props) { const [state, setState] = React.useState< NavigationState | InitialState | undefined >(initialState); @@ -32,7 +37,11 @@ export default function NavigationContainer({ initialState, children }: Props) { React.useEffect(() => { stateRef.current = state; - }); + + if (onStateChange && state !== undefined) { + onStateChange(state); + } + }, [onStateChange, state]); const getState = React.useCallback(() => stateRef.current, []); const value = React.useMemo(() => ({ state, getState, setState }), [ diff --git a/src/__tests__/__snapshots__/index.test.tsx.snap b/src/__tests__/__snapshots__/index.test.tsx.snap new file mode 100644 index 00000000..8c4129bf --- /dev/null +++ b/src/__tests__/__snapshots__/index.test.tsx.snap @@ -0,0 +1,27 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`initializes state with router 1`] = ` +Object { + "index": 1, + "key": "root", + "names": Array [ + "foo", + "bar", + "baz", + ], + "routes": Array [ + Object { + "key": "foo", + "name": "foo", + }, + Object { + "key": "bar", + "name": "bar", + }, + Object { + "key": "baz", + "name": "baz", + }, + ], +} +`; diff --git a/src/__tests__/index.test.ts b/src/__tests__/index.test.ts deleted file mode 100644 index 1ebecac3..00000000 --- a/src/__tests__/index.test.ts +++ /dev/null @@ -1 +0,0 @@ -it.todo('placeholder'); diff --git a/src/__tests__/index.test.tsx b/src/__tests__/index.test.tsx new file mode 100644 index 00000000..2bcc2b40 --- /dev/null +++ b/src/__tests__/index.test.tsx @@ -0,0 +1,87 @@ +import * as React from 'react'; +import { render } from 'react-native-testing-library'; +import NavigationContainer from '../NavigationContainer'; +import useNavigationBuilder from '../useNavigationBuilder'; +import { Router } from '../types'; +import Screen from '../Screen'; + +const MockRouter: Router = { + getInitialState({ screens, initialRouteName }) { + const routeNames = Object.keys(screens); + + return { + key: 'root', + names: routeNames, + index: routeNames.indexOf(initialRouteName || routeNames[0]), + routes: routeNames.map(name => ({ + key: name, + name, + })), + }; + }, + + getStateForAction() { + return null; + }, + + getStateForChildUpdate(state) { + return state; + }, + + shouldActionPropagateToChildren() { + return false; + }, + + shouldActionChangeFocus() { + return false; + }, + + actionCreators: {}, +}; + +it('initializes state with router', () => { + expect.assertions(1); + + const TestNavigator = (props: any) => { + useNavigationBuilder(MockRouter, props); + return null; + }; + + const element = ( + expect(state).toMatchSnapshot()} + > + + + + + + + ); + + render(element).update(element); +}); + +it('throws if muliple navigators rendered under one container', async () => { + expect.assertions(1); + + const TestNavigator = (props: any) => { + useNavigationBuilder(MockRouter, props); + return null; + }; + + const element = ( + + + + + + + + + ); + + expect(() => render(element).update(element)).toThrowError( + 'Another navigator is already registered for this container' + ); +}); diff --git a/src/useNavigationBuilder.tsx b/src/useNavigationBuilder.tsx index b950fefe..67bd7951 100644 --- a/src/useNavigationBuilder.tsx +++ b/src/useNavigationBuilder.tsx @@ -165,7 +165,7 @@ export default function useNavigationBuilder( const state = getState(); const result = router.getStateForChildUpdate(state, { update, - focus: true, + focus, }); if (handleChildUpdateParent !== undefined) { diff --git a/yarn.lock b/yarn.lock index 98c0c397..b6a95ef3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -570,7 +570,30 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-react-jsx@^7.0.0 <7.4.0": +"@babel/plugin-transform-react-display-name@^7.0.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.2.0.tgz#ebfaed87834ce8dc4279609a4f0c324c156e3eb0" + integrity sha512-Htf/tPa5haZvRMiNSQSFifK12gtr/8vwfr+A9y69uF0QcU77AVu4K7MiHEkTxF7lQoHOL0F9ErqgfNEAKgXj7A== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-react-jsx-self@^7.0.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.2.0.tgz#461e21ad9478f1031dd5e276108d027f1b5240ba" + integrity sha512-v6S5L/myicZEy+jr6ielB0OR8h+EH/1QFx/YJ7c7Ua+7lqsjj/vW6fD5FR9hB/6y7mGbfT4vAURn3xqBxsUcdg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-jsx" "^7.2.0" + +"@babel/plugin-transform-react-jsx-source@^7.0.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.2.0.tgz#20c8c60f0140f5dd3cd63418d452801cf3f7180f" + integrity sha512-A32OkKTp4i5U6aE88GwwcuV4HAprUgHcTq0sSafLxjr6AW0QahrCRCjxogkbbcdtpbXkuTOlgpjophCxb6sh5g== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-jsx" "^7.2.0" + +"@babel/plugin-transform-react-jsx@^7.0.0", "@babel/plugin-transform-react-jsx@^7.0.0 <7.4.0": version "7.3.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.3.0.tgz#f2cab99026631c767e2745a5368b331cfe8f5290" integrity sha512-a/+aRb7R06WcKvQLOu4/TpjKOdvVEKRLWFpKcNuHhiREPgGRB4TQJxq07+EZLS8LFVYpfq1a5lDUnuMdcCpBKg== @@ -750,6 +773,17 @@ js-levenshtein "^1.1.3" semver "^5.5.0" +"@babel/preset-react@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.0.0.tgz#e86b4b3d99433c7b3e9e91747e2653958bc6b3c0" + integrity sha512-oayxyPS4Zj+hF6Et11BwuBkmpgT/zMxyuZgFrMeZID6Hdh3dGlk4sHCAhdBCpuCKW2ppBfl2uCCetlrUIJRY3w== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-transform-react-display-name" "^7.0.0" + "@babel/plugin-transform-react-jsx" "^7.0.0" + "@babel/plugin-transform-react-jsx-self" "^7.0.0" + "@babel/plugin-transform-react-jsx-source" "^7.0.0" + "@babel/preset-typescript@^7.3.3": version "7.3.3" resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.3.3.tgz#88669911053fa16b2b276ea2ede2ca603b3f307a" @@ -7655,7 +7689,7 @@ prettier@^1.18.2: resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea" integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw== -pretty-format@^24.8.0: +pretty-format@^24.0.0, pretty-format@^24.8.0: version "24.8.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.8.0.tgz#8dae7044f58db7cb8be245383b565a963e3c27f2" integrity sha512-P952T7dkrDEplsR+TuY7q3VXDae5Sr7zmQb12JU/NDQa/3CH7/QW0yvqLcGN6jL+zQFKaoJcPc+yJxMTGmosqw== @@ -7831,11 +7865,28 @@ react-dom@^16.8.6: prop-types "^15.6.2" scheduler "^0.13.6" -react-is@^16.8.1, react-is@^16.8.4: +react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6: version "16.8.6" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA== +react-native-testing-library@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/react-native-testing-library/-/react-native-testing-library-1.9.1.tgz#458f32ffa86b4f767f05d90475edd04c119f4595" + integrity sha512-8yOKu47096p/nl6riIXToidWzmvvoYxVFOG84vb7huT4/HBpfq8FqQJlg7Ck+7Uyq//xBK9c26uAKRsNgTQZWQ== + dependencies: + pretty-format "^24.0.0" + +react-test-renderer@^16.8.6: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.8.6.tgz#188d8029b8c39c786f998aa3efd3ffe7642d5ba1" + integrity sha512-H2srzU5IWYT6cZXof6AhUcx/wEyJddQ8l7cLM/F7gDXYyPr4oq+vCIxJYXVGhId1J706sqziAjuOEjyNkfgoEw== + dependencies: + object-assign "^4.1.1" + prop-types "^15.6.2" + react-is "^16.8.6" + scheduler "^0.13.6" + react@^16.8.6: version "16.8.6" resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe"