Compare commits

...

42 Commits

Author SHA1 Message Date
Satyajit Sahoo
0cca1309ec chore: publish
- @react-navigation/bottom-tabs@5.0.3
 - @react-navigation/compat@5.0.3
 - @react-navigation/core@5.1.2
 - @react-navigation/drawer@5.0.3
 - @react-navigation/material-bottom-tabs@5.0.3
 - @react-navigation/material-top-tabs@5.0.3
 - @react-navigation/native-stack@5.0.3
 - @react-navigation/native@5.0.3
 - @react-navigation/stack@5.0.3
2020-02-12 16:58:48 +01:00
Satyajit Sahoo
6c9447a38c fix: check if we can go baack before dispatching pop 2020-02-12 13:17:08 +01:00
Satyajit Sahoo
030c63c89f fix: fix false positives for circular object check
fixes #6827
2020-02-12 11:42:36 +01:00
Abhinandan Ramaprasath
2bf0958502 fix: static container memo check (#6825)
Memo check compared elements of prevProps to nextProps but failed
to take new props into account. Fixed the logic and added a new
test.
2020-02-12 10:56:23 +01:00
Satyajit Sahoo
94cff2380a chore: publish
- @react-navigation/bottom-tabs@5.0.2
 - @react-navigation/compat@5.0.2
 - @react-navigation/core@5.1.1
 - @react-navigation/drawer@5.0.2
 - @react-navigation/material-bottom-tabs@5.0.2
 - @react-navigation/material-top-tabs@5.0.2
 - @react-navigation/native-stack@5.0.2
 - @react-navigation/native@5.0.2
 - @react-navigation/stack@5.0.2
2020-02-11 18:57:48 +01:00
Satyajit Sahoo
359ae1bfac fix: don't cleanup state on switching navigator
This leads to a glitch. Switching navigators should be handled by the router properly.
2020-02-11 18:56:27 +01:00
Satyajit Sahoo
031136f7c8 fix: remove unnecessary borderless from drawer item
closes #6801
2020-02-11 17:21:05 +01:00
Satyajit Sahoo
b6e7e08b9a fix: provide route context to header and bottom tabs 2020-02-11 15:42:00 +01:00
Satyajit Sahoo
6c6102b459 fix: make getInitialState async on web 2020-02-11 15:40:49 +01:00
David Govea
0c59ef7328 fix: initialize keyboard-hiding tabBar to visible=true (#6740, #6799)
Looks like this was an accidental refactor bug introduced with commit
38a38b0 (refactor from 	class component to function component)
2020-02-11 12:16:41 +01:00
Satyajit Sahoo
297eabb90e chore: fix typo 2020-02-11 00:34:04 +01:00
Satyajit Sahoo
b234b035c3 chore: publish
- @react-navigation/bottom-tabs@5.0.1
 - @react-navigation/compat@5.0.1
 - @react-navigation/core@5.1.0
 - @react-navigation/drawer@5.0.1
 - @react-navigation/material-bottom-tabs@5.0.1
 - @react-navigation/material-top-tabs@5.0.1
 - @react-navigation/native-stack@5.0.1
 - @react-navigation/native@5.0.1
 - @react-navigation/routers@5.0.1
 - @react-navigation/stack@5.0.1
2020-02-10 17:22:48 +01:00
Satyajit Sahoo
80629bf30b fix: merge initial params on replace
fixes 6792
2020-02-10 17:19:07 +01:00
Satyajit Sahoo
688d16de5d fix: prevent ripple from bleeding out of drawer item
closes #6801
2020-02-10 16:28:29 +01:00
Satyajit Sahoo
13b4e07348 fix: add some links in the error messages 2020-02-10 16:16:54 +01:00
Satyajit Sahoo
86c39d2e0e refactor: move types and base router to routers package 2020-02-10 16:04:20 +01:00
Satyajit Sahoo
7160a511e6 chore: set initial insets for safe areas 2020-02-10 16:04:20 +01:00
Satyajit Sahoo
ae680a1e3c chore: upgrade depenendecies 2020-02-10 16:04:20 +01:00
Satyajit Sahoo
7c72337c33 chore: lock yarn version
The latest version of yarn has a bug where trying to upgrade a package fails with an error such as 'expected workspace package to exist for ...'. Downgrading to an older version fixes it.
2020-02-10 16:04:20 +01:00
Satyajit Sahoo
6c188addc6 chore: add comment for question label 2020-02-10 13:32:30 +01:00
Satyajit Sahoo
cff2d06adc chore: update contributing guidelines 2020-02-10 09:22:56 +01:00
Satyajit Sahoo
4c9f87df6d chore: update contributing guidelines 2020-02-10 09:17:19 +01:00
Satyajit Sahoo
fa48c9d42a chore: add a action for triage 2020-02-10 08:13:26 +01:00
Satyajit Sahoo
abb595830e chore: update README 2020-02-07 14:10:08 +01:00
Satyajit Sahoo
8ad2922f35 chore: add stale action 2020-02-07 14:05:04 +01:00
Satyajit Sahoo
c715fef2bd chore: update issue templates 2020-02-07 14:01:50 +01:00
Satyajit Sahoo
cea2fc29ba chore: fix missing dev dependency 2020-02-05 15:06:54 +01:00
Satyajit Sahoo
79ab56fe41 chore: release stable version 2020-02-05 14:54:44 +01:00
Satyajit Sahoo
a121844148 chore: prepare for stable release (#354) 2020-02-05 14:47:36 +01:00
Wojciech Lewicki
61b1134f90 feat: support ignoring empty path strings (#349) 2020-02-05 13:31:40 +01:00
Satyajit Sahoo
9fcf3be364 chore: publish
- @react-navigation/stack@5.0.0-alpha.71
2020-02-05 09:43:24 +01:00
Satyajit Sahoo
f746ece61b fix: use addListener only when available 2020-02-05 09:43:00 +01:00
Satyajit Sahoo
264537bdb4 chore: publish
- @react-navigation/bottom-tabs@5.0.0-alpha.45
 - @react-navigation/compat@5.0.0-alpha.34
 - @react-navigation/core@5.0.0-alpha.43
 - @react-navigation/drawer@5.0.0-alpha.47
 - @react-navigation/material-bottom-tabs@5.0.0-alpha.42
 - @react-navigation/material-top-tabs@5.0.0-alpha.41
 - @react-navigation/native-stack@5.0.0-alpha.35
 - @react-navigation/native@5.0.0-alpha.35
 - @react-navigation/routers@5.0.0-alpha.33
 - @react-navigation/stack@5.0.0-alpha.70
2020-02-04 17:34:55 +01:00
Satyajit Sahoo
ca4a36070a fix: improve error message for unhandled action 2020-02-04 17:33:03 +01:00
Wojciech Lewicki
4ca5cc6329 feat: add initialRouteName property to config (#322) 2020-02-04 14:44:57 +01:00
osdnk
25c3fc440f chore: publish
- @react-navigation/bottom-tabs@5.0.0-alpha.44
 - @react-navigation/compat@5.0.0-alpha.33
 - @react-navigation/core@5.0.0-alpha.42
 - @react-navigation/drawer@5.0.0-alpha.46
 - @react-navigation/material-bottom-tabs@5.0.0-alpha.41
 - @react-navigation/material-top-tabs@5.0.0-alpha.40
 - @react-navigation/native-stack@5.0.0-alpha.34
 - @react-navigation/native@5.0.0-alpha.34
 - @react-navigation/routers@5.0.0-alpha.32
 - @react-navigation/stack@5.0.0-alpha.69
2020-02-04 11:04:43 +01:00
Satyajit Sahoo
89fa363883 chore: don't run expo preview for contributors 2020-02-04 10:54:12 +01:00
Michał Osadnik
bec2f754d4 refactor: rename NavigationNativeContainer to NavigationContainer (#344) 2020-02-04 10:21:16 +01:00
Evan Bacon
b277927925 feat: disable pan gesture by default in the browser for Apple devices
fixes #287
2020-02-04 09:12:40 +01:00
osdnk
72993c6463 chore: delete .gitattributes 2020-02-03 14:53:11 +01:00
Satyajit Sahoo
3fbfb70699 chore: publish
- @react-navigation/stack@5.0.0-alpha.68
2020-02-03 05:48:24 +01:00
Satyajit Sahoo
abdf9d12b5 fix: use .native for masked view instead of .web 2020-02-03 05:47:25 +01:00
157 changed files with 151947 additions and 2864 deletions

View File

@@ -31,7 +31,7 @@ jobs:
- run: |
yarn lint
yarn typescript
unit-test:
unit-tests:
<<: *defaults
steps:
- attach_workspace:
@@ -59,7 +59,7 @@ workflows:
- lint-and-typecheck:
requires:
- install-dependencies
- unit-test:
- unit-tests:
requires:
- install-dependencies
- build-packages:

View File

@@ -4,3 +4,7 @@ dist/
lib/
web-build/
web-report/
.expo/
.yarn/
.vscode/

1
.gitattributes vendored
View File

@@ -1 +0,0 @@
* text eol=lf

View File

@@ -0,0 +1,43 @@
---
name: React Navigation 4
about: Report an issue with React Navigation 4
title: ''
labels: bug, version-4
assignees: ''
---
**Current Behavior**
- What code are you running and what is happening?
- Include a screenshot or video if it makes sense.
**Expected Behavior**
- What do you expect should be happening?
- Include a screenshot or video if it makes sense.
**How to reproduce**
- You must provide a way to reproduce the problem. If you are having an issue with your machine or build tools, the issue belongs on another repository as that is outside of the scope of React Navigation.
- Either re-create the bug on [Snack](https://snack.expo.io) or link to a GitHub repository with code that reproduces the bug.
- Explain how to run the example app and any steps that we need to take to reproduce the issue from the example app.
- Keep the repro code as simple as possible, with the minimum amount of code required to repro the issue.
- Before reporting an issue, make sure you are on latest version of the package.
**Your Environment**
| software | version |
| ------------------------------ | ------- |
| iOS or Android |
| react-navigation |
| react-navigation-stack |
| react-navigation-tabs |
| react-navigation-drawer |
| react-native-reanimated |
| react-native-gesture-handler |
| react-native-safe-area-context |
| react-native-screens |
| react-native |
| expo |
| node |
| npm or yarn |

View File

@@ -1,10 +1,10 @@
blank_issues_enabled: false
contact_links:
- name: Troubleshooting
url: https://reactnavigation.org/docs/en/next/troubleshooting.html
url: https://reactnavigation.org/docs/troubleshooting.html
about: Read how to troubleshoot and fix common issues and mistakes.
- name: Documentation
url: https://next.reactnavigation.org
url: https://reactnavigation.org
about: Read the official documentation.
- name: Feature requests
url: https://react-navigation.canny.io/feature-requests

View File

@@ -5,6 +5,7 @@ jobs:
publish:
name: Install and publish
runs-on: ubuntu-latest
if: github.event.pull_request.head.repo.owner.login == 'react-navigation'
steps:
- name: Checkout
uses: actions/checkout@v1
@@ -43,7 +44,7 @@ jobs:
- name: Get expo link
id: expo
run: echo "::set-output name=path::@react-navigation/react-navigation-example?release-channel=pr-${{ github.event.number }}"
run: echo "::set-output name=path::@react-navigation/react-react-navigationample?release-channel=pr-${{ github.event.number }}"
- name: Comment on PR
uses: unsplash/comment-on-pr@master

20
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: "Close stale issues and pull requests"
on:
schedule:
- cron: "0 0 * * *"
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'Hello 👋, this issue has been open for more than 3 months with no activity on it. If the issue is still present in the latest version, please leave a comment within 7 days to keep it open, otherwise it will be closed automatically. If you found a solution on workaround for the issue, please comment here for others to find. If this issue is critical for you, please consider sending a pull request to fix the issue.'
stale-pr-message: 'Hello 👋, this pull request has been open for more than 3 months with no activity on it. If you think this is still necessary with the latest version, please comment and ping a maintainer to get this reviewed, otherwise it will be closed automatically in 7 days.'
days-before-stale: 90
days-before-close: 7
stale-issue-label: 'stale'
stale-pr-label: 'stale'
exempt-issue-label: 'keep open'
exempt-pr-label: 'keep open'

38
.github/workflows/triage.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
name: Triage
on:
issues:
types: [labeled]
jobs:
needs-more-info:
runs-on: ubuntu-latest
if: github.event.label.name == 'needs more info'
steps:
- uses: actions/checkout@master
- uses: actions/github@v1.0.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: comment "Hey! Thanks for opening the issue. Can you provide more information about the issue? Please fill the issue template when opening the issue without deleting any section. We need all the information we can to be able to help. Make sure to at least provide - Current behaviour, Expected behaviour, A way to reproduce the issue with minimal code (link to [snack.expo.io](https://snack.expo.io)) or a repo on GitHub, and the information about your environment (such as the platform of the device, exact versions of all the packages mentioned in the template etc.)."
needs-repro:
runs-on: ubuntu-latest
if: github.event.label.name == 'needs repro'
steps:
- uses: actions/checkout@master
- uses: actions/github@v1.0.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: comment "Hey! Thanks for opening the issue. Can you provide a minimal repro which demonstrates the issue? Posting a snippet of your code in the issue is useful, but it's not usually straightforward to run. A repro will help us debug the issue faster. Please try to keep the repro as small as possible. The easiest way to provide a repro is on [snack.expo.io](https://snack.expo.io). If it's not possible to repro it on [snack.expo.io](https://snack.expo.io), then you can also provide the repro in a GitHub repository."
question:
runs-on: ubuntu-latest
if: github.event.label.name == 'question'
steps:
- uses: actions/checkout@master
- uses: actions/github@v1.0.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: comment "Hey! Thanks for opening the issue. The issue tracker is intended for only tracking bug reports. This helps us prioritize fixing bugs in the library. Seems you have a usage question. Please ask the question on [StackOverflow](https://stackoverflow.com/questions/tagged/react-navigation) instead using the `react-navigation` label. You can also chat with other community members on [Reactiflux Discord server](https://www.reactiflux.com/) in the `#react-navigation` channel."

147155
.yarn/releases/yarn-1.18.0.js vendored Executable file

File diff suppressed because one or more lines are too long

5
.yarnrc Normal file
View File

@@ -0,0 +1,5 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
yarn-path ".yarn/releases/yarn-1.18.0.js"

204
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,204 @@
# Contributing
This library is a community effort: it can only be great if we all help out in one way or another! If you feel like you aren't experienced enough using React Navigation to contribute, you can still make an impact by:
- Responding to one of the open [issues](https://github.com/react-navigation/react-navigation/issues). Even if you can't resolve or fully answer a question, asking for more information or clarity on an issue is extremely beneficial for someone to come after you to resolve the issue.
- Creating public example repositories or [Snacks](https://snack.expo.io/) of navigation problems you have solved and sharing the links.
- Answering questions on [Stack Overflow](https://stackoverflow.com/search?q=react-navigation).
- Answering questions in our [Reactiflux](https://www.reactiflux.com/) channel.
- Providing feedback on the open [PRs](https://github.com/react-navigation/react-navigation/pulls).
- Providing feedback on the open [RFCs](https://github.com/react-navigation/rfcs).
- Improving the [website](https://github.com/react-navigation/react-navigation.github.io).
If you don't know where to start, check the ones with the label [`good first issue`](https://github.com/react-navigation/react-navigation/labels/good%20first%20issue) - even fixing a typo in the documentation is a worthy contribution!
## Development workflow
The project uses a monorepo structure for the packages managed by [yarn workspaces](https://yarnpkg.com/lang/en/docs/workspaces/) and [lerna](https://lerna.js.org). To get started with the project, run `yarn` in the root directory to install the required dependencies for each package:
```sh
yarn
```
While developing, you can run the [example app](/example/) with [Expo](https://expo.io/) to test your changes:
```sh
yarn example start
```
Make sure your code passes TypeScript and ESLint. Run the following to verify:
```sh
yarn typescript
yarn lint
```
To fix formatting errors, run the following:
```sh
yarn lint --fix
```
Remember to add tests for your change if possible. Run the unit tests by:
```sh
yarn test
```
Running the e2e tests with Detox (on iOS) requires the following:
- Mac with macOS (at least macOS High Sierra 10.13.6)
- Xcode 10.1+ with Xcode command line tools
First you need to install `applesimutils` and `detox-cli`:
```sh
brew tap wix/brew
brew install applesimutils
yarn global add detox-cli
```
Then you can build and run the tests:
```sh
detox build -c ios.sim.debug
detox test -c ios.sim.debug
```
### Commit message convention
We follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages:
- `fix`: bug fixes, e.g. fix crash due to deprecated method.
- `feat`: new features, e.g. add new method to the module.
- `refactor`: code refactor, e.g. migrate from class components to hooks.
- `docs`: changes into documentation, e.g. add usage example for the module..
- `test`: adding or updating tests, eg add integration tests using detox.
- `chore`: tooling changes, e.g. change CI config.
Our pre-commit hooks verify that your commit message matches this format when committing.
### Linting and tests
[ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/)
We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing.
Our pre-commit hooks verify that the linter and tests pass when committing.
### Scripts
The `package.json` file contains various scripts for common tasks:
- `yarn install`: setup project by installing all dependencies and pods.
- `yarn typescript`: type-check files with TypeScript.
- `yarn lint`: lint files with ESLint.
- `yarn test`: run unit tests with Jest.
- `yarn example start`: run the example app with Expo.
### Sending a pull request
> **Working on your first pull request?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github).
When you're sending a pull request:
- Prefer small pull requests focused on one change.
- Verify that linters and tests are passing.
- Review the documentation to make sure it looks good.
- Follow the pull request template when opening a pull request.
- For pull requests that change the API or implementation, discuss with maintainers first by opening an issue.
## Publishing
Maintainers with write access to the GitHub repo and the npm organization can publish new versions. To publish a new version, first, you need to export a `GH_TOKEN` environment variable as mentioned [here](https://github.com/lerna/lerna/tree/master/commands/version#--create-release-type). Then run:
```sh
yarn lerna publish
```
This will automatically bump the version and publish the packages. It'll also publish the changelogs on GitHub for each package.
## Code of Conduct
### Our Pledge
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
### Our Standards
Examples of behavior that contributes to a positive environment for our community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
- Focusing on what is best not just for us as individuals, but for the overall community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or
advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email
address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
### Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
### Scope
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
### Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to Brent Vatne ([brentvatne@gmail.com](mailto:brentvatne@gmail.com)), Satyajit Sahoo ([satyajit.happy@gmail.com](mailto:satyajit.happy@gmail.com)) or Michał Osadnik ([micosa97@gmail.com](mailto:micosa97@gmail.com)). All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
### Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
#### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
#### 2. Warning
**Community Impact**: A violation through a single incident or series of actions.
**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
#### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
#### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the community.
### Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.

View File

@@ -4,72 +4,15 @@
[![Code Coverage][coverage-badge]][coverage]
[![MIT License][license-badge]][license]
Routing and navigation for your React Native apps with a component-first API.
Routing and navigation for your React Native apps.
Documentation can be found at [next.reactnavigation.org](https://next.reactnavigation.org/).
Documentation can be found at [reactnavigation.org](https://reactnavigation.org/).
If you are looking for version 4, the code can be found in the [4.x branch](https://github.com/react-navigation/react-navigation/tree/4.x).
## Contributing
The project uses a monorepo structure for the packages managed by [yarn workspaces](https://yarnpkg.com/lang/en/docs/workspaces/) and [lerna](https://lerna.js.org). To get started with the project, run `yarn` in the root directory to install the required dependencies for each package:
```sh
yarn
```
While developing, you can run the [example app](/example/) with [Expo](https://expo.io/) to test your changes:
```sh
yarn example start
```
Make sure your code passes TypeScript and ESLint. Run the following to verify:
```sh
yarn typescript
yarn lint
```
To fix formatting errors, run the following:
```sh
yarn lint --fix
```
Remember to add tests for your change if possible. Run the unit tests by:
```sh
yarn test
```
Running the e2e tests with Detox (on iOS) requires the following:
- Mac with macOS (at least macOS High Sierra 10.13.6)
- Xcode 10.1+ with Xcode command line tools
First you need to install `applesimutils` and `detox-cli`:
```sh
brew tap wix/brew
brew install applesimutils
yarn global add detox-cli
```
Then you can build and run the tests:
```sh
detox build -c ios.sim.debug
detox test -c ios.sim.debug
```
## Publishing
To publish a new version, first we need to export a `GH_TOKEN` environment variable as mentioned [here](https://github.com/lerna/lerna/tree/master/commands/version#--create-release-type). Then run:
```sh
yarn lerna publish
```
This will automatically bump the version and publish the packages. It'll also publish the changelogs on GitHub for each package.
Please read through our [contribution guide](CONTRIBUTING.md) a to get started!
## Installing from a fork on GitHub
@@ -106,9 +49,9 @@ Remember to replace `<user>`, `<repo>` and `<name>` with right values.
<!-- badges -->
[build-badge]: https://img.shields.io/circleci/project/github/react-navigation/navigation-ex/master.svg?style=flat-square
[build]: https://circleci.com/gh/react-navigation/navigation-ex
[coverage-badge]: https://img.shields.io/codecov/c/github/react-navigation/navigation-ex.svg?style=flat-square
[coverage]: https://codecov.io/github/react-navigation/navigation-ex
[build-badge]: https://img.shields.io/circleci/project/github/react-navigation/react-navigation/master.svg?style=flat-square
[build]: https://circleci.com/gh/react-navigation/react-navigation
[coverage-badge]: https://img.shields.io/codecov/c/github/react-navigation/react-navigation.svg?style=flat-square
[coverage]: https://codecov.io/github/react-navigation/react-navigation
[license-badge]: https://img.shields.io/npm/l/@react-navigation/core.svg?style=flat-square
[license]: https://opensource.org/licenses/MIT

View File

@@ -1,230 +0,0 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.23](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.22...@react-navigation/example@5.0.0-alpha.23) (2019-11-20)
**Note:** Version bump only for package @react-navigation/example
# [5.0.0-alpha.22](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.21...@react-navigation/example@5.0.0-alpha.22) (2019-11-17)
**Note:** Version bump only for package @react-navigation/example
# [5.0.0-alpha.21](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.20...@react-navigation/example@5.0.0-alpha.21) (2019-11-10)
**Note:** Version bump only for package @react-navigation/example
# [5.0.0-alpha.20](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.19...@react-navigation/example@5.0.0-alpha.20) (2019-11-08)
**Note:** Version bump only for package @react-navigation/example
# [5.0.0-alpha.19](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.18...@react-navigation/example@5.0.0-alpha.19) (2019-11-04)
**Note:** Version bump only for package @react-navigation/example
# [5.0.0-alpha.18](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.17...@react-navigation/example@5.0.0-alpha.18) (2019-11-02)
### Bug Fixes
* minor tweaks for web and fix example ([67fd69a](https://github.com/satya164/navigation-ex/commit/67fd69a))
# [5.0.0-alpha.17](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.16...@react-navigation/example@5.0.0-alpha.17) (2019-10-29)
### Bug Fixes
* improve type annotation for screens ([8f16085](https://github.com/satya164/navigation-ex/commit/8f16085))
# [5.0.0-alpha.16](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.15...@react-navigation/example@5.0.0-alpha.16) (2019-10-22)
**Note:** Version bump only for package @react-navigation/example
# [5.0.0-alpha.15](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.14...@react-navigation/example@5.0.0-alpha.15) (2019-10-22)
**Note:** Version bump only for package @react-navigation/example
# [5.0.0-alpha.14](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.13...@react-navigation/example@5.0.0-alpha.14) (2019-10-17)
**Note:** Version bump only for package @react-navigation/example
# [5.0.0-alpha.13](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.12...@react-navigation/example@5.0.0-alpha.13) (2019-10-15)
### Bug Fixes
* block GH interactions in Native Stack example ([#126](https://github.com/react-navigation/navigation-ex/issues/126)) ([386d1c0](https://github.com/react-navigation/navigation-ex/commit/386d1c0))
* make it possible to run the example on web ([7a901af](https://github.com/react-navigation/navigation-ex/commit/7a901af))
### Features
* initial version of native stack ([#102](https://github.com/react-navigation/navigation-ex/issues/102)) ([ba3f718](https://github.com/react-navigation/navigation-ex/commit/ba3f718))
# [5.0.0-alpha.12](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.11...@react-navigation/example@5.0.0-alpha.12) (2019-10-06)
### Features
* drop header: null in favor of more explitit headerShown option ([ba6b6ae](https://github.com/satya164/navigation-ex/commit/ba6b6ae))
# [5.0.0-alpha.11](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.10...@react-navigation/example@5.0.0-alpha.11) (2019-10-03)
**Note:** Version bump only for package @react-navigation/example
# [5.0.0-alpha.10](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.9...@react-navigation/example@5.0.0-alpha.10) (2019-10-03)
**Note:** Version bump only for package @react-navigation/example
# [5.0.0-alpha.9](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.8...@react-navigation/example@5.0.0-alpha.9) (2019-10-03)
**Note:** Version bump only for package @react-navigation/example
# [5.0.0-alpha.8](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.7...@react-navigation/example@5.0.0-alpha.8) (2019-09-27)
### Bug Fixes
* close drawer on navigate ([655a220](https://github.com/react-navigation/navigation-ex/commit/655a220))
# [5.0.0-alpha.7](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.6...@react-navigation/example@5.0.0-alpha.7) (2019-09-16)
### Features
* compatibility layer ([e0f28a4](https://github.com/satya164/navigation-ex/commit/e0f28a4))
* make example run as bare react-native project as well ([#85](https://github.com/satya164/navigation-ex/issues/85)) ([d16c20c](https://github.com/satya164/navigation-ex/commit/d16c20c))
# [5.0.0-alpha.6](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.4...@react-navigation/example@5.0.0-alpha.6) (2019-08-31)
**Note:** Version bump only for package @react-navigation/example
# [5.0.0-alpha.5](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.4...@react-navigation/example@5.0.0-alpha.5) (2019-08-31)
**Note:** Version bump only for package @react-navigation/example
# [5.0.0-alpha.4](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.3...@react-navigation/example@5.0.0-alpha.4) (2019-08-31)
### Bug Fixes
* handle route names change when all routes are removed ([#86](https://github.com/satya164/navigation-ex/issues/86)) ([1b2983e](https://github.com/satya164/navigation-ex/commit/1b2983e))
# [5.0.0-alpha.3](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.2...@react-navigation/example@5.0.0-alpha.3) (2019-08-29)
### Features
* handle more methods in useScrollToTop ([f9e8c7e](https://github.com/react-navigation/navigation-ex/commit/f9e8c7e))
# [5.0.0-alpha.2](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.1...@react-navigation/example@5.0.0-alpha.2) (2019-08-27)
### Features
* add native container ([d26b77f](https://github.com/react-navigation/navigation-ex/commit/d26b77f))
# 5.0.0-alpha.1 (2019-08-21)
### Bug Fixes
* add margin on left when left button is specified in header ([f1f1541](https://github.com/satya164/navigation-ex/commit/f1f1541))
### Features
* add a simple stack and material tabs integration ([#39](https://github.com/satya164/navigation-ex/issues/39)) ([e0bee10](https://github.com/satya164/navigation-ex/commit/e0bee10))
* add hook for deep link support ([35987ae](https://github.com/satya164/navigation-ex/commit/35987ae))
* add integration for paper's bottom navigation ([f3b6d1f](https://github.com/satya164/navigation-ex/commit/f3b6d1f))
* add native container with back button integration ([#48](https://github.com/satya164/navigation-ex/issues/48)) ([b7735af](https://github.com/satya164/navigation-ex/commit/b7735af))
* integrate reanimated based stack ([#42](https://github.com/satya164/navigation-ex/issues/42)) ([dcf57c0](https://github.com/satya164/navigation-ex/commit/dcf57c0))

View File

@@ -1,7 +1,7 @@
{
"name": "@react-navigation/example",
"description": "Demo app to showcase various functionality of React Navigation",
"version": "5.0.0-alpha.23",
"version": "5.0.0",
"private": true,
"scripts": {
"start": "expo start",
@@ -12,7 +12,7 @@
},
"dependencies": {
"@expo/vector-icons": "^10.0.0",
"@react-native-community/masked-view": "0.1.5",
"@react-native-community/masked-view": "0.1.6",
"@types/react-native-restart": "^0.0.0",
"color": "^3.1.2",
"expo": "^36.0.2",
@@ -21,23 +21,22 @@
"react": "~16.9.0",
"react-dom": "~16.9.0",
"react-native": "~0.61.5",
"react-native-gesture-handler": "^1.5.5",
"react-native-paper": "^3.5.0",
"react-native-reanimated": "^1.4.0",
"react-native-gesture-handler": "^1.5.6",
"react-native-paper": "^3.6.0",
"react-native-reanimated": "^1.7.0",
"react-native-restart": "^0.0.13",
"react-native-safe-area-context": "^0.6.2",
"react-native-screens": "^2.0.0-alpha.33",
"react-native-safe-area-context": "^0.7.2",
"react-native-screens": "^2.0.0-beta.2",
"react-native-tab-view": "2.13.0",
"react-native-unimodules": "^0.7.0",
"react-native-web": "^0.11.7"
},
"devDependencies": {
"@babel/core": "^7.7.7",
"@expo/webpack-config": "^0.10.9",
"@types/react": "^16.9.17",
"@expo/webpack-config": "^0.10.12",
"@types/react": "^16.9.19",
"@types/react-native": "^0.60.30",
"babel-preset-expo": "^8.0.0",
"expo-cli": "^3.11.5",
"typescript": "^3.7.4"
"expo-cli": "^3.11.9",
"typescript": "^3.7.5"
}
}

View File

@@ -22,7 +22,7 @@ import {
InitialState,
useLinking,
NavigationContainerRef,
NavigationNativeContainer,
NavigationContainer,
DefaultTheme,
DarkTheme,
} from '@react-navigation/native';
@@ -202,7 +202,7 @@ export default function App() {
{Platform.OS === 'ios' && (
<StatusBar barStyle={theme.dark ? 'light-content' : 'dark-content'} />
)}
<NavigationNativeContainer
<NavigationContainer
ref={containerRef}
initialState={initialState}
onStateChange={state =>
@@ -307,7 +307,7 @@ export default function App() {
)}
</Drawer.Screen>
</Drawer.Navigator>
</NavigationNativeContainer>
</NavigationContainer>
</PaperProvider>
);
}

View File

@@ -11,8 +11,6 @@
"allowBranch": "master",
"conventionalCommits": true,
"createRelease": "github",
"preId": "alpha",
"preDistTag": "next",
"message": "chore: publish",
"ignoreChanges": [
"**/__fixtures__/**",

View File

@@ -23,25 +23,26 @@
"example": "yarn --cwd example"
},
"devDependencies": {
"@babel/plugin-proposal-class-properties": "^7.7.0",
"@babel/plugin-proposal-optional-chaining": "^7.7.5",
"@babel/preset-env": "^7.7.7",
"@babel/preset-react": "^7.7.0",
"@babel/preset-typescript": "^7.7.7",
"@babel/runtime": "^7.7.7",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/plugin-proposal-optional-chaining": "^7.8.3",
"@babel/preset-env": "^7.8.4",
"@babel/preset-flow": "^7.8.3",
"@babel/preset-react": "^7.8.3",
"@babel/preset-typescript": "^7.8.3",
"@babel/runtime": "^7.8.4",
"@commitlint/config-conventional": "^8.3.4",
"@types/jest": "^24.0.25",
"codecov": "^3.6.1",
"commitlint": "^8.3.4",
"core-js": "^3.6.2",
"detox": "^15.0.0",
"@types/jest": "^25.1.2",
"codecov": "^3.6.5",
"commitlint": "^8.3.5",
"core-js": "^3.6.4",
"detox": "^15.1.4",
"eslint": "^6.8.0",
"eslint-config-satya164": "^3.1.5",
"husky": "^4.0.1",
"jest": "^24.9.0",
"husky": "^4.2.1",
"jest": "^25.1.0",
"lerna": "^3.20.2",
"prettier": "^1.19.1",
"typescript": "^3.7.4"
"typescript": "^3.7.5"
},
"resolutions": {
"react": "~16.9.0",

View File

@@ -3,6 +3,50 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.0.3](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.0.2...@react-navigation/bottom-tabs@5.0.3) (2020-02-12)
**Note:** Version bump only for package @react-navigation/bottom-tabs
## [5.0.2](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.0.1...@react-navigation/bottom-tabs@5.0.2) (2020-02-11)
### Bug Fixes
* initialize keyboard-hiding tabBar to visible=true ([#6740](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/issues/6740), [#6799](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/issues/6799)) ([0c59ef7](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/0c59ef7328c63108a2a2c04e927794d73cead63a))
* provide route context to header and bottom tabs ([b6e7e08](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/b6e7e08b9a05be6c04ed21e938b9580876239116))
## [5.0.1](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.0.0-alpha.45...@react-navigation/bottom-tabs@5.0.1) (2020-02-10)
**Note:** Version bump only for package @react-navigation/bottom-tabs
# [5.0.0-alpha.45](https://github.com/react-navigation/navigation-ex/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.0.0-alpha.44...@react-navigation/bottom-tabs@5.0.0-alpha.45) (2020-02-04)
**Note:** Version bump only for package @react-navigation/bottom-tabs
# [5.0.0-alpha.44](https://github.com/react-navigation/navigation-ex/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.0.0-alpha.43...@react-navigation/bottom-tabs@5.0.0-alpha.44) (2020-02-04)
**Note:** Version bump only for package @react-navigation/bottom-tabs
# [5.0.0-alpha.43](https://github.com/react-navigation/navigation-ex/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.0.0-alpha.42...@react-navigation/bottom-tabs@5.0.0-alpha.43) (2020-02-03)
**Note:** Version bump only for package @react-navigation/bottom-tabs

View File

@@ -2,80 +2,4 @@
Bottom tab navigator for React Navigation following iOS design guidelines.
Documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/en/next/bottom-tab-navigator.html).
## Installation
Open a Terminal in your project's folder and run,
```sh
yarn add @react-navigation/native @react-navigation/bottom-tabs
```
Now we need to install [`react-native-safe-area-context`](https://github.com/th3rdwave/react-native-safe-area-context).
If you are using Expo, to ensure that you get the compatible versions of the libraries, run:
```sh
expo install react-native-safe-area-context
```
If you are not using Expo, run the following:
```sh
yarn add react-native-safe-area-context
```
If you are using Expo, you are done. Otherwise, continue to the next steps.
To complete the linking on iOS, make sure you have [Cocoapods](https://cocoapods.org/) installed. Then run:
```sh
cd ios
pod install
cd ..
```
## Usage
```js
import { MaterialCommunityIcons } from 'react-native-vector-icons';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
const getTabBarIcon = name => ({ color, size }) => (
<MaterialCommunityIcons name={name} color={color} size={size} />
);
const BottomTabs = createBottomTabNavigator();
export default function App() {
return (
<BottomTabs.Navigator>
<BottomTabs.Screen
name="article"
component={Article}
options={{
tabBarLabel: 'Article',
tabBarIcon: getTabBarIcon('file-document-box'),
}}
/>
<BottomTabs.Screen
name="chat"
component={Chat}
options={{
tabBarLabel: 'Chat',
tabBarIcon: getTabBarIcon('message-reply'),
}}
/>
<BottomTabs.Screen
name="contacts"
component={Contacts}
options={{
tabBarLabel: 'Contacts',
tabBarIcon: getTabBarIcon('contacts'),
}}
/>
</BottomTabs.Navigator>
);
}
```
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/bottom-tab-navigator.html).

View File

@@ -10,9 +10,9 @@
"android",
"tab"
],
"version": "5.0.0-alpha.43",
"version": "5.0.3",
"license": "MIT",
"repository": "https://github.com/react-navigation/navigation-ex/tree/master/packages/bottom-tabs",
"repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs",
"main": "lib/commonjs/index.js",
"react-native": "src/index.tsx",
"module": "lib/module/index.js",
@@ -30,23 +30,23 @@
"clean": "del lib"
},
"dependencies": {
"@react-navigation/routers": "^5.0.0-alpha.31",
"color": "^3.1.2",
"react-native-iphone-x-helper": "^1.2.1"
},
"devDependencies": {
"@react-native-community/bob": "^0.8.0",
"@react-native-community/bob": "^0.9.3",
"@react-navigation/native": "^5.0.3",
"@types/color": "^3.0.1",
"@types/react": "^16.9.17",
"@types/react": "^16.9.19",
"@types/react-native": "^0.60.30",
"del-cli": "^3.0.0",
"react": "~16.9.0",
"react-native": "~0.61.5",
"react-native-safe-area-context": "^0.6.2",
"typescript": "^3.7.4"
"react-native-safe-area-context": "^0.7.2",
"typescript": "^3.7.5"
},
"peerDependencies": {
"@react-navigation/native": "^5.0.0-alpha.0",
"@react-navigation/native": "^5.0.0",
"react": "*",
"react-native": "*",
"react-native-safe-area-context": "^0.6.0"

View File

@@ -3,12 +3,10 @@ import {
useNavigationBuilder,
createNavigatorFactory,
DefaultNavigatorOptions,
} from '@react-navigation/native';
import {
TabRouter,
TabRouterOptions,
TabNavigationState,
} from '@react-navigation/routers';
} from '@react-navigation/native';
import BottomTabView from '../views/BottomTabView';
import {
BottomTabNavigationConfig,

View File

@@ -10,8 +10,8 @@ import {
NavigationProp,
ParamListBase,
Descriptor,
TabNavigationState,
} from '@react-navigation/native';
import { TabNavigationState } from '@react-navigation/routers';
export type BottomTabNavigationEventMap = {
/**

View File

@@ -11,6 +11,7 @@ import {
} from 'react-native';
import {
NavigationContext,
NavigationRouteContext,
CommonActions,
useTheme,
} from '@react-navigation/native';
@@ -56,7 +57,7 @@ export default function BottomTabBar({
});
const [keyboardShown, setKeyboardShown] = React.useState(false);
const [visible] = React.useState(() => new Animated.Value(0));
const [visible] = React.useState(() => new Animated.Value(1));
const { routes } = state;
@@ -237,27 +238,29 @@ export default function BottomTabBar({
key={route.key}
value={descriptors[route.key].navigation}
>
<BottomTabItem
route={route}
focused={focused}
horizontal={shouldUseHorizontalLabels()}
onPress={onPress}
onLongPress={onLongPress}
accessibilityLabel={accessibilityLabel}
testID={options.tabBarTestID}
allowFontScaling={allowFontScaling}
activeTintColor={activeTintColor}
inactiveTintColor={inactiveTintColor}
activeBackgroundColor={activeBackgroundColor}
inactiveBackgroundColor={inactiveBackgroundColor}
button={options.tabBarButton}
icon={options.tabBarIcon}
label={label}
showIcon={showIcon}
showLabel={showLabel}
labelStyle={labelStyle}
style={tabStyle}
/>
<NavigationRouteContext.Provider value={route}>
<BottomTabItem
route={route}
focused={focused}
horizontal={shouldUseHorizontalLabels()}
onPress={onPress}
onLongPress={onLongPress}
accessibilityLabel={accessibilityLabel}
testID={options.tabBarTestID}
allowFontScaling={allowFontScaling}
activeTintColor={activeTintColor}
inactiveTintColor={inactiveTintColor}
activeBackgroundColor={activeBackgroundColor}
inactiveBackgroundColor={inactiveBackgroundColor}
button={options.tabBarButton}
icon={options.tabBarIcon}
label={label}
showIcon={showIcon}
showLabel={showLabel}
labelStyle={labelStyle}
style={tabStyle}
/>
</NavigationRouteContext.Provider>
</NavigationContext.Provider>
);
})}

View File

@@ -1,8 +1,7 @@
import * as React from 'react';
import { View, StyleSheet } from 'react-native';
import { TabNavigationState } from '@react-navigation/routers';
import { useTheme } from '@react-navigation/native';
import { TabNavigationState, useTheme } from '@react-navigation/native';
// eslint-disable-next-line import/no-unresolved
import { ScreenContainer } from 'react-native-screens';

View File

@@ -2,17 +2,25 @@ import * as React from 'react';
import {
SafeAreaProvider,
SafeAreaConsumer,
initialWindowSafeAreaInsets,
} from 'react-native-safe-area-context';
import {
getStatusBarHeight,
getBottomSpace,
} from 'react-native-iphone-x-helper';
// The provider component for safe area initializes asynchornously
// Until the insets are available, there'll be blank screen
// To avoid the blank screen, we specify some initial values
const initialSafeAreaInsets = {
// Approximate values which are good enough for most cases
top: getStatusBarHeight(true),
bottom: getBottomSpace(),
right: 0,
left: 0,
// If we are on a newer version of the library, we can get the correct window insets
// The component might not be filling the window, but this is good enough for most cases
...initialWindowSafeAreaInsets,
};
type Props = {
@@ -26,7 +34,7 @@ export default function SafeAreaProviderCompat({ children }: Props) {
if (insets) {
// If we already have insets, don't wrap the stack in another safe area provider
// This avoids an issue with updates at the cost of potentially incorrect values
// https://github.com/react-navigation/navigation-ex/issues/174
// https://github.com/react-navigation/react-navigation/issues/174
return children;
}

View File

@@ -2,8 +2,8 @@
"extends": "../../tsconfig",
"references": [
{ "path": "../core" },
{ "path": "../native" },
{ "path": "../routers" }
{ "path": "../routers" },
{ "path": "../native" }
],
"compilerOptions": {
"outDir": "./lib/typescript"

View File

@@ -3,6 +3,46 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.0.3](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.0.2...@react-navigation/compat@5.0.3) (2020-02-12)
**Note:** Version bump only for package @react-navigation/compat
## [5.0.2](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.0.1...@react-navigation/compat@5.0.2) (2020-02-11)
**Note:** Version bump only for package @react-navigation/compat
## [5.0.1](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.0.0-alpha.34...@react-navigation/compat@5.0.1) (2020-02-10)
**Note:** Version bump only for package @react-navigation/compat
# [5.0.0-alpha.34](https://github.com/react-navigation/navigation-ex/tree/master/packages/compat/compare/@react-navigation/compat@5.0.0-alpha.33...@react-navigation/compat@5.0.0-alpha.34) (2020-02-04)
**Note:** Version bump only for package @react-navigation/compat
# [5.0.0-alpha.33](https://github.com/react-navigation/navigation-ex/tree/master/packages/compat/compare/@react-navigation/compat@5.0.0-alpha.32...@react-navigation/compat@5.0.0-alpha.33) (2020-02-04)
**Note:** Version bump only for package @react-navigation/compat
# [5.0.0-alpha.32](https://github.com/react-navigation/navigation-ex/tree/master/packages/compat/compare/@react-navigation/compat@5.0.0-alpha.31...@react-navigation/compat@5.0.0-alpha.32) (2020-02-03)
**Note:** Version bump only for package @react-navigation/compat

View File

@@ -2,29 +2,4 @@
Compatibility layer to write navigator definitions in static configuration format.
Documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/en/next/compatibility.html).
## Installation
Open a Terminal in your project's folder and run,
```sh
yarn add @react-navigation/native @react-navigation/compat
```
## Usage
```js
import { createCompatNavigatorFactory } from '@react-navigation/compat';
import { createStackNavigator } from '@react-navigation/stack';
const RootStack = createCompatNavigatorFactory(createStackNavigator)(
{
Home: { screen: HomeScreen },
Profile: { screen: ProfileScreen },
},
{
initialRouteName: 'Profile',
}
);
```
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/compatibility.html).

View File

@@ -1,9 +1,13 @@
{
"name": "@react-navigation/compat",
"description": "Compatibility layer to write navigator definitions in static configuration format",
"version": "5.0.0-alpha.32",
"version": "5.0.3",
"license": "MIT",
"repository": "https://github.com/react-navigation/navigation-ex/tree/master/packages/compat",
"repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/compat",
"bugs": {
"url": "https://github.com/react-navigation/react-navigation/issues"
},
"homepage": "https://reactnavigation.org/docs/compatibility.html",
"main": "lib/commonjs/index.js",
"react-native": "src/index.tsx",
"module": "lib/module/index.js",
@@ -20,16 +24,15 @@
"prepare": "bob build",
"clean": "del lib"
},
"dependencies": {
"@react-navigation/routers": "^5.0.0-alpha.31"
},
"devDependencies": {
"@types/react": "^16.9.17",
"@react-native-community/bob": "^0.9.3",
"@react-navigation/native": "^5.0.3",
"@types/react": "^16.9.19",
"react": "~16.9.0",
"typescript": "^3.7.4"
"typescript": "^3.7.5"
},
"peerDependencies": {
"@react-navigation/native": "^5.0.0-alpha.0",
"@react-navigation/native": "^5.0.0",
"react": "~16.9.0"
},
"@react-native-community/bob": {

View File

@@ -1,4 +1,4 @@
import { DrawerActions, DrawerActionType } from '@react-navigation/routers';
import { DrawerActions, DrawerActionType } from '@react-navigation/native';
export function openDrawer(): DrawerActionType {
return DrawerActions.openDrawer();

View File

@@ -1,5 +1,8 @@
import { CommonActions } from '@react-navigation/native';
import { StackActions, StackActionType } from '@react-navigation/routers';
import {
CommonActions,
StackActions,
StackActionType,
} from '@react-navigation/native';
export function reset(): CommonActions.Action {
throw new Error(

View File

@@ -1,4 +1,4 @@
import { TabActions, TabActionType } from '@react-navigation/routers';
import { TabActions, TabActionType } from '@react-navigation/native';
export function jumpTo({
routeName,

View File

@@ -2,12 +2,10 @@ import {
useNavigationBuilder,
createNavigatorFactory,
DefaultNavigatorOptions,
} from '@react-navigation/native';
import {
TabRouter,
TabRouterOptions,
TabNavigationState,
} from '@react-navigation/routers';
} from '@react-navigation/native';
import createCompatNavigatorFactory from './createCompatNavigatorFactory';
type Props = DefaultNavigatorOptions<{}> & TabRouterOptions;

View File

@@ -2,8 +2,8 @@
"extends": "../../tsconfig",
"references": [
{ "path": "../core" },
{ "path": "../native" },
{ "path": "../routers" }
{ "path": "../routers" },
{ "path": "../native" }
],
"compilerOptions": {
"outDir": "./lib/typescript"

View File

@@ -3,6 +3,69 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.1.2](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.1.1...@react-navigation/core@5.1.2) (2020-02-12)
### Bug Fixes
* fix false positives for circular object check ([030c63c](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/030c63c89fe447aa484b767831c8f8e26e90431c)), closes [#6827](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/6827)
* static container memo check ([#6825](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/6825)) ([2bf0958](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/2bf09585021470f500d967e9242836840efe970f))
## [5.1.1](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.1.0...@react-navigation/core@5.1.1) (2020-02-11)
### Bug Fixes
* don't cleanup state on switching navigator ([359ae1b](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/359ae1bfacec5ef880b3944f465c881aedb16767))
# [5.1.0](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.0.0-alpha.43...@react-navigation/core@5.1.0) (2020-02-10)
### Bug Fixes
* add some links in the error messages ([13b4e07](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/13b4e07348496f7cb516d625b44a6a7d310ef9af))
### Features
* support ignoring empty path strings ([#349](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/349)) ([61b1134](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/61b1134f90310390fe819622c1f33273fca0bd42))
# [5.0.0-alpha.43](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/compare/@react-navigation/core@5.0.0-alpha.42...@react-navigation/core@5.0.0-alpha.43) (2020-02-04)
### Bug Fixes
* improve error message for unhandled action ([ca4a360](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/commit/ca4a36070a21c4fe86cb1cc55a4452dca293f215))
### Features
* add initialRouteName property to config ([#322](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/issues/322)) ([4ca5cc6](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/commit/4ca5cc632992187f12870281e4cf4c7d1f799967))
# [5.0.0-alpha.42](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/compare/@react-navigation/core@5.0.0-alpha.41...@react-navigation/core@5.0.0-alpha.42) (2020-02-04)
**Note:** Version bump only for package @react-navigation/core
# [5.0.0-alpha.41](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/compare/@react-navigation/core@5.0.0-alpha.40...@react-navigation/core@5.0.0-alpha.41) (2020-02-03)

View File

@@ -1,6 +1,6 @@
# `@react-navigation/core`
Core utilities for building navigators.
Core utilities for building navigators independent of the platform.
## Installation

View File

@@ -1,14 +1,18 @@
{
"name": "@react-navigation/core",
"description": "Core utilities for building navigators",
"version": "5.1.2",
"keywords": [
"react",
"react-native",
"react-navigation"
],
"version": "5.0.0-alpha.41",
"license": "MIT",
"repository": "https://github.com/react-navigation/navigation-ex/tree/master/packages/core",
"repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/core",
"bugs": {
"url": "https://github.com/react-navigation/react-navigation/issues"
},
"homepage": "https://reactnavigation.org",
"main": "lib/commonjs/index.js",
"react-native": "src/index.tsx",
"module": "lib/module/index.js",
@@ -25,16 +29,16 @@
"clean": "del lib"
},
"dependencies": {
"@react-navigation/routers": "^5.0.1",
"escape-string-regexp": "^2.0.0",
"query-string": "^6.9.0",
"query-string": "^6.10.1",
"react-is": "^16.12.0",
"shortid": "^2.2.15",
"use-subscription": "^1.3.0"
},
"devDependencies": {
"@babel/core": "^7.7.7",
"@react-native-community/bob": "^0.8.0",
"@types/react": "^16.9.17",
"@react-native-community/bob": "^0.9.3",
"@types/react": "^16.9.19",
"@types/react-is": "^16.7.1",
"@types/shortid": "^0.0.29",
"@types/use-subscription": "^1.0.0",
@@ -42,7 +46,7 @@
"react": "~16.9.0",
"react-native-testing-library": "^1.12.0",
"react-test-renderer": "~16.12.0",
"typescript": "^3.7.4"
"typescript": "^3.7.5"
},
"peerDependencies": {
"react": "~16.9.0"

View File

@@ -0,0 +1,316 @@
import * as React from 'react';
import {
CommonActions,
Route,
NavigationState,
InitialState,
PartialState,
NavigationAction,
} from '@react-navigation/routers';
import EnsureSingleNavigator from './EnsureSingleNavigator';
import NavigationBuilderContext from './NavigationBuilderContext';
import useFocusedListeners from './useFocusedListeners';
import useDevTools from './useDevTools';
import useStateGetters from './useStateGetters';
import isSerializable from './isSerializable';
import { NavigationContainerRef, NavigationContainerProps } from './types';
import useEventEmitter from './useEventEmitter';
type State = NavigationState | PartialState<NavigationState> | undefined;
const MISSING_CONTEXT_ERROR =
"We couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'? See https://reactnavigation.org/docs/en/getting-started.html for setup instructions.";
export const NavigationStateContext = React.createContext<{
isDefault?: true;
state?: NavigationState | PartialState<NavigationState>;
getKey: () => string | undefined;
setKey: (key: string) => void;
getState: () => NavigationState | PartialState<NavigationState> | undefined;
setState: (
state: NavigationState | PartialState<NavigationState> | undefined
) => void;
performTransaction: (action: () => void) => void;
}>({
isDefault: true,
get getKey(): any {
throw new Error(MISSING_CONTEXT_ERROR);
},
get setKey(): any {
throw new Error(MISSING_CONTEXT_ERROR);
},
get getState(): any {
throw new Error(MISSING_CONTEXT_ERROR);
},
get setState(): any {
throw new Error(MISSING_CONTEXT_ERROR);
},
get performTransaction(): any {
throw new Error(MISSING_CONTEXT_ERROR);
},
});
let hasWarnedForSerialization = false;
/**
* Remove `key` and `routeNames` from the state objects recursively to get partial state.
*
* @param state Initial state object.
*/
const getPartialState = (
state: InitialState | undefined
): PartialState<NavigationState> | undefined => {
if (state === undefined) {
return;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { key, routeNames, ...partialState } = state;
// @ts-ignore
return {
...partialState,
stale: true,
routes: state.routes.map(route => {
if (route.state === undefined) {
return route as Route<string> & {
state?: PartialState<NavigationState>;
};
}
return { ...route, state: getPartialState(route.state) };
}),
};
};
/**
* Container component which holds the navigation state.
* This should be rendered at the root wrapping the whole app.
*
* @param props.initialState Initial state object for the navigation tree.
* @param props.onStateChange Callback which is called with the latest navigation state when it changes.
* @param props.children Child elements to render the content.
* @param props.ref Ref object which refers to the navigation object containing helper methods.
*/
const BaseNavigationContainer = React.forwardRef(
function BaseNavigationContainer(
{
initialState,
onStateChange,
independent,
children,
}: NavigationContainerProps,
ref: React.Ref<NavigationContainerRef>
) {
const parent = React.useContext(NavigationStateContext);
if (!parent.isDefault && !independent) {
throw new Error(
"Looks like you have nested a 'NavigationContainer' inside another. Normally you need only one container at the root of the app, so this was probably an error. If this was intentional, pass 'independent={true}' explicitely. Note that this will make the child navigators disconnected from the parent and you won't be able to navigate between them."
);
}
const [state, setNavigationState] = React.useState<State>(() =>
getPartialState(initialState == null ? undefined : initialState)
);
const navigationStateRef = React.useRef<State>();
const transactionStateRef = React.useRef<State | null>(null);
const isTransactionActiveRef = React.useRef<boolean>(false);
const isFirstMountRef = React.useRef<boolean>(true);
const skipTrackingRef = React.useRef<boolean>(false);
const navigatorKeyRef = React.useRef<string | undefined>();
const getKey = React.useCallback(() => navigatorKeyRef.current, []);
const setKey = React.useCallback((key: string) => {
navigatorKeyRef.current = key;
}, []);
const performTransaction = React.useCallback((callback: () => void) => {
if (isTransactionActiveRef.current) {
throw new Error(
"Only one transaction can be active at a time. Did you accidentally nest 'performTransaction'?"
);
}
setNavigationState((navigationState: State) => {
isTransactionActiveRef.current = true;
transactionStateRef.current = navigationState;
try {
callback();
} finally {
isTransactionActiveRef.current = false;
}
return transactionStateRef.current;
});
}, []);
const getState = React.useCallback(
() =>
transactionStateRef.current !== null
? transactionStateRef.current
: navigationStateRef.current,
[]
);
const setState = React.useCallback((navigationState: State) => {
if (transactionStateRef.current === null) {
throw new Error(
"Any 'setState' calls need to be done inside 'performTransaction'"
);
}
transactionStateRef.current = navigationState;
}, []);
const reset = React.useCallback(
(state: NavigationState) => {
performTransaction(() => {
skipTrackingRef.current = true;
setState(state);
});
},
[performTransaction, setState]
);
const { trackState, trackAction } = useDevTools({
name: '@react-navigation',
reset,
state,
});
const {
listeners,
addListener: addFocusedListener,
} = useFocusedListeners();
const { getStateForRoute, addStateGetter } = useStateGetters();
const dispatch = (
action: NavigationAction | ((state: NavigationState) => NavigationAction)
) => {
listeners[0](navigation => navigation.dispatch(action));
};
const canGoBack = () => {
const { result, handled } = listeners[0](navigation =>
navigation.canGoBack()
);
if (handled) {
return result;
} else {
return false;
}
};
const resetRoot = React.useCallback(
(state?: PartialState<NavigationState> | NavigationState) => {
performTransaction(() => {
trackAction('@@RESET_ROOT');
setState(state);
});
},
[performTransaction, setState, trackAction]
);
const getRootState = React.useCallback(() => {
return getStateForRoute('root');
}, [getStateForRoute]);
const emitter = useEventEmitter();
React.useImperativeHandle(ref, () => ({
...(Object.keys(CommonActions) as (keyof typeof CommonActions)[]).reduce<
any
>((acc, name) => {
acc[name] = (...args: any[]) =>
dispatch(
CommonActions[name](
// @ts-ignore
...args
)
);
return acc;
}, {}),
...emitter.create('root'),
resetRoot,
dispatch,
canGoBack,
getRootState,
}));
const builderContext = React.useMemo(
() => ({
addFocusedListener,
addStateGetter,
trackAction,
}),
[addFocusedListener, trackAction, addStateGetter]
);
const context = React.useMemo(
() => ({
state,
performTransaction,
getState,
setState,
getKey,
setKey,
}),
[getKey, getState, performTransaction, setKey, setState, state]
);
React.useEffect(() => {
if (process.env.NODE_ENV !== 'production') {
if (
state !== undefined &&
!isSerializable(state) &&
!hasWarnedForSerialization
) {
hasWarnedForSerialization = true;
console.warn(
"We found non-serializable values in the navigation state, which can break usage such as persisting and restoring state. This might happen if you passed non-serializable values such as function, class instances etc. in params. If you need to use components with callbacks in your options, you can use 'navigation.setOptions' instead. See https://reactnavigation.org/docs/en/header-buttons.html#header-interaction-with-its-screen-component for docs."
);
}
}
emitter.emit({
type: 'state',
data: { state },
});
if (skipTrackingRef.current) {
skipTrackingRef.current = false;
} else {
trackState(getRootState);
}
navigationStateRef.current = state;
transactionStateRef.current = null;
if (!isFirstMountRef.current && onStateChange) {
onStateChange(getRootState());
}
isFirstMountRef.current = false;
}, [state, onStateChange, trackState, getRootState, emitter]);
return (
<NavigationBuilderContext.Provider value={builderContext}>
<NavigationStateContext.Provider value={context}>
<EnsureSingleNavigator>{children}</EnsureSingleNavigator>
</NavigationStateContext.Provider>
</NavigationBuilderContext.Provider>
);
}
);
export default BaseNavigationContainer;

View File

@@ -4,7 +4,7 @@ type Props = {
children: React.ReactNode;
};
const MULTIPLE_NAVIGATOR_ERROR = `Another navigator is already registered for this container. You likely have multiple navigators under a single "NavigationContainer" or "Screen". Make sure each navigator is under a separate "Screen" container.`;
const MULTIPLE_NAVIGATOR_ERROR = `Another navigator is already registered for this container. You likely have multiple navigators under a single "NavigationContainer" or "Screen". Make sure each navigator is under a separate "Screen" container. See https://reactnavigation.org/docs/en/nesting-navigators.html for a guide on nesting.`;
export const SingleNavigatorContext = React.createContext<
| {

View File

@@ -1,10 +1,10 @@
import * as React from 'react';
import {
NavigationAction,
NavigationHelpers,
NavigationState,
ParamListBase,
} from './types';
} from '@react-navigation/routers';
import { NavigationHelpers } from './types';
export type ChildActionListener = (
action: NavigationAction,

View File

@@ -1,295 +0,0 @@
import * as React from 'react';
import * as CommonActions from './CommonActions';
import EnsureSingleNavigator from './EnsureSingleNavigator';
import NavigationBuilderContext from './NavigationBuilderContext';
import useFocusedListeners from './useFocusedListeners';
import useDevTools from './useDevTools';
import useStateGetters from './useStateGetters';
import isSerializable from './isSerializable';
import {
Route,
NavigationState,
InitialState,
PartialState,
NavigationAction,
NavigationContainerRef,
NavigationContainerProps,
} from './types';
import useEventEmitter from './useEventEmitter';
type State = NavigationState | PartialState<NavigationState> | undefined;
const MISSING_CONTEXT_ERROR =
"We couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'?";
export const NavigationStateContext = React.createContext<{
isDefault?: true;
state?: NavigationState | PartialState<NavigationState>;
getState: () => NavigationState | PartialState<NavigationState> | undefined;
setState: (
state: NavigationState | PartialState<NavigationState> | undefined
) => void;
key?: string;
performTransaction: (action: () => void) => void;
}>({
isDefault: true,
get getState(): any {
throw new Error(MISSING_CONTEXT_ERROR);
},
get setState(): any {
throw new Error(MISSING_CONTEXT_ERROR);
},
get performTransaction(): any {
throw new Error(MISSING_CONTEXT_ERROR);
},
});
let hasWarnedForSerialization = false;
/**
* Remove `key` and `routeNames` from the state objects recursively to get partial state.
*
* @param state Initial state object.
*/
const getPartialState = (
state: InitialState | undefined
): PartialState<NavigationState> | undefined => {
if (state === undefined) {
return;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { key, routeNames, ...partialState } = state;
// @ts-ignore
return {
...partialState,
stale: true,
routes: state.routes.map(route => {
if (route.state === undefined) {
return route as Route<string> & {
state?: PartialState<NavigationState>;
};
}
return { ...route, state: getPartialState(route.state) };
}),
};
};
/**
* Container component which holds the navigation state.
* This should be rendered at the root wrapping the whole app.
*
* @param props.initialState Initial state object for the navigation tree.
* @param props.onStateChange Callback which is called with the latest navigation state when it changes.
* @param props.children Child elements to render the content.
* @param props.ref Ref object which refers to the navigation object containing helper methods.
*/
const Container = React.forwardRef(function NavigationContainer(
{
initialState,
onStateChange,
independent,
children,
}: NavigationContainerProps,
ref: React.Ref<NavigationContainerRef>
) {
const parent = React.useContext(NavigationStateContext);
if (!parent.isDefault && !independent) {
throw new Error(
"Looks like you have nested a 'NavigationContainer' inside another. Normally you need only one container at the root of the app, so this was probably an error. If this was intentional, pass 'independent={true}' explicitely."
);
}
const [state, setNavigationState] = React.useState<State>(() =>
getPartialState(initialState == null ? undefined : initialState)
);
const navigationStateRef = React.useRef<State>();
const transactionStateRef = React.useRef<State | null>(null);
const isTransactionActiveRef = React.useRef<boolean>(false);
const isFirstMountRef = React.useRef<boolean>(true);
const skipTrackingRef = React.useRef<boolean>(false);
const performTransaction = React.useCallback((callback: () => void) => {
if (isTransactionActiveRef.current) {
throw new Error(
"Only one transaction can be active at a time. Did you accidentally nest 'performTransaction'?"
);
}
setNavigationState((navigationState: State) => {
isTransactionActiveRef.current = true;
transactionStateRef.current = navigationState;
try {
callback();
} finally {
isTransactionActiveRef.current = false;
}
return transactionStateRef.current;
});
}, []);
const getState = React.useCallback(
() =>
transactionStateRef.current !== null
? transactionStateRef.current
: navigationStateRef.current,
[]
);
const setState = React.useCallback((navigationState: State) => {
if (transactionStateRef.current === null) {
throw new Error(
"Any 'setState' calls need to be done inside 'performTransaction'"
);
}
transactionStateRef.current = navigationState;
}, []);
const reset = React.useCallback(
(state: NavigationState) => {
performTransaction(() => {
skipTrackingRef.current = true;
setState(state);
});
},
[performTransaction, setState]
);
const { trackState, trackAction } = useDevTools({
name: '@react-navigation',
reset,
state,
});
const { listeners, addListener: addFocusedListener } = useFocusedListeners();
const { getStateForRoute, addStateGetter } = useStateGetters();
const dispatch = (
action: NavigationAction | ((state: NavigationState) => NavigationAction)
) => {
listeners[0](navigation => navigation.dispatch(action));
};
const canGoBack = () => {
const { result, handled } = listeners[0](navigation =>
navigation.canGoBack()
);
if (handled) {
return result;
} else {
return false;
}
};
const resetRoot = React.useCallback(
(state?: PartialState<NavigationState> | NavigationState) => {
performTransaction(() => {
trackAction('@@RESET_ROOT');
setState(state);
});
},
[performTransaction, setState, trackAction]
);
const getRootState = React.useCallback(() => {
return getStateForRoute('root');
}, [getStateForRoute]);
const emitter = useEventEmitter();
React.useImperativeHandle(ref, () => ({
...(Object.keys(CommonActions) as (keyof typeof CommonActions)[]).reduce<
any
>((acc, name) => {
acc[name] = (...args: any[]) =>
dispatch(
CommonActions[name](
// @ts-ignore
...args
)
);
return acc;
}, {}),
...emitter.create('root'),
resetRoot,
dispatch,
canGoBack,
getRootState,
}));
const builderContext = React.useMemo(
() => ({
addFocusedListener,
addStateGetter,
trackAction,
}),
[addFocusedListener, trackAction, addStateGetter]
);
const context = React.useMemo(
() => ({
state,
performTransaction,
getState,
setState,
}),
[getState, performTransaction, setState, state]
);
React.useEffect(() => {
if (process.env.NODE_ENV !== 'production') {
if (
state !== undefined &&
!isSerializable(state) &&
!hasWarnedForSerialization
) {
hasWarnedForSerialization = true;
console.warn(
"We found non-serializable values in the navigation state, which can break usage such as persisting and restoring state. This might happen if you passed non-serializable values such as function, class instances etc. in params. If you need to use functions in your options, you can use 'navigation.setOptions' instead."
);
}
}
emitter.emit({
type: 'state',
data: { state },
});
if (skipTrackingRef.current) {
skipTrackingRef.current = false;
} else {
trackState(getRootState);
}
navigationStateRef.current = state;
transactionStateRef.current = null;
if (!isFirstMountRef.current && onStateChange) {
onStateChange(getRootState());
}
isFirstMountRef.current = false;
}, [state, onStateChange, trackState, getRootState, emitter]);
return (
<NavigationBuilderContext.Provider value={builderContext}>
<NavigationStateContext.Provider value={context}>
<EnsureSingleNavigator>{children}</EnsureSingleNavigator>
</NavigationStateContext.Provider>
</NavigationBuilderContext.Provider>
);
});
export default Container;

View File

@@ -1,5 +1,6 @@
import * as React from 'react';
import { NavigationProp, ParamListBase } from './types';
import { ParamListBase } from '@react-navigation/routers';
import { NavigationProp } from './types';
/**
* Context which holds the navigation prop for a screen.

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import { Route } from './types';
import { Route } from '@react-navigation/routers';
/**
* Context which holds the route prop for a screen.

View File

@@ -1,17 +1,16 @@
import * as React from 'react';
import { NavigationStateContext } from './NavigationContainer';
import NavigationContext from './NavigationContext';
import NavigationRouteContext from './NavigationRouteContext';
import StaticContainer from './StaticContainer';
import EnsureSingleNavigator from './EnsureSingleNavigator';
import {
Route,
ParamListBase,
NavigationState,
NavigationProp,
RouteConfig,
PartialState,
} from './types';
} from '@react-navigation/routers';
import { NavigationStateContext } from './BaseNavigationContainer';
import NavigationContext from './NavigationContext';
import NavigationRouteContext from './NavigationRouteContext';
import StaticContainer from './StaticContainer';
import EnsureSingleNavigator from './EnsureSingleNavigator';
import { NavigationProp, RouteConfig } from './types';
type Props<State extends NavigationState, ScreenOptions extends object> = {
screen: RouteConfig<ParamListBase, string, ScreenOptions>;
@@ -39,6 +38,14 @@ export default function SceneView<
}: Props<State, ScreenOptions>) {
const { performTransaction } = React.useContext(NavigationStateContext);
const navigatorKeyRef = React.useRef<string | undefined>();
const getKey = React.useCallback(() => navigatorKeyRef.current, []);
const setKey = React.useCallback((key: string) => {
navigatorKeyRef.current = key;
}, []);
const getCurrentState = React.useCallback(() => {
const state = getState();
const currentRoute = state.routes.find(r => r.key === route.key);
@@ -66,14 +73,16 @@ export default function SceneView<
getState: getCurrentState,
setState: setCurrentState,
performTransaction,
key: route.key,
getKey,
setKey,
}),
[
getCurrentState,
getKey,
performTransaction,
route.key,
route.state,
setCurrentState,
setKey,
]
);

View File

@@ -1,4 +1,5 @@
import { RouteConfig, ParamListBase } from './types';
import { ParamListBase } from '@react-navigation/routers';
import { RouteConfig } from './types';
/**
* Empty component used for specifying route configuration.

View File

@@ -8,12 +8,19 @@ function StaticContainer(props: any) {
}
export default React.memo(StaticContainer, (prevProps: any, nextProps: any) => {
for (const prop in prevProps) {
if (prop === 'children') {
const prevPropKeys = Object.keys(prevProps);
const nextPropKeys = Object.keys(nextProps);
if (prevPropKeys.length !== nextPropKeys.length) {
return false;
}
for (const key of prevPropKeys) {
if (key === 'children') {
continue;
}
if (prevProps[prop] !== nextProps[prop]) {
if (prevProps[key] !== nextProps[key]) {
return false;
}
}

View File

@@ -1,17 +1,17 @@
import * as React from 'react';
import { act, render } from 'react-native-testing-library';
import NavigationContainer, {
NavigationStateContext,
} from '../NavigationContainer';
import MockRouter, { MockActions } from './__fixtures__/MockRouter';
import useNavigationBuilder from '../useNavigationBuilder';
import Screen from '../Screen';
import {
DefaultRouterOptions,
NavigationState,
Router,
NavigationContainerRef,
} from '../types';
} from '@react-navigation/routers';
import BaseNavigationContainer, {
NavigationStateContext,
} from '../BaseNavigationContainer';
import MockRouter, { MockActions } from './__fixtures__/MockRouter';
import useNavigationBuilder from '../useNavigationBuilder';
import Screen from '../Screen';
import { NavigationContainerRef } from '../types';
it('throws when getState is accessed without a container', () => {
expect.assertions(1);
@@ -85,9 +85,9 @@ it('throws when setState is called outside performTransaction', () => {
};
const element = (
<NavigationContainer>
<BaseNavigationContainer>
<Test />
</NavigationContainer>
</BaseNavigationContainer>
);
expect(() => render(element).update(element)).toThrowError(
@@ -112,9 +112,9 @@ it('throws when nesting performTransaction', () => {
};
const element = (
<NavigationContainer>
<BaseNavigationContainer>
<Test />
</NavigationContainer>
</BaseNavigationContainer>
);
expect(() => render(element).update(element)).toThrowError(
@@ -125,11 +125,11 @@ it('throws when nesting performTransaction', () => {
it('throws when nesting containers', () => {
expect(() =>
render(
<NavigationContainer>
<NavigationContainer>
<BaseNavigationContainer>
<BaseNavigationContainer>
<React.Fragment />
</NavigationContainer>
</NavigationContainer>
</BaseNavigationContainer>
</BaseNavigationContainer>
)
).toThrowError(
"Looks like you have nested a 'NavigationContainer' inside another."
@@ -137,11 +137,11 @@ it('throws when nesting containers', () => {
expect(() =>
render(
<NavigationContainer>
<NavigationContainer independent>
<BaseNavigationContainer>
<BaseNavigationContainer independent>
<React.Fragment />
</NavigationContainer>
</NavigationContainer>
</BaseNavigationContainer>
</BaseNavigationContainer>
)
).not.toThrowError(
"Looks like you have nested a 'NavigationContainer' inside another."
@@ -223,7 +223,7 @@ it('handle dispatching with ref', () => {
};
const element = (
<NavigationContainer
<BaseNavigationContainer
ref={ref}
initialState={initialState}
onStateChange={onStateChange}
@@ -248,7 +248,7 @@ it('handle dispatching with ref', () => {
)}
</Screen>
</ParentNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element).update(element);
@@ -301,7 +301,7 @@ it('handle resetting state with ref', () => {
const onStateChange = jest.fn();
const element = (
<NavigationContainer ref={ref} onStateChange={onStateChange}>
<BaseNavigationContainer ref={ref} onStateChange={onStateChange}>
<TestNavigator>
<Screen name="foo">{() => null}</Screen>
<Screen name="foo2">
@@ -322,7 +322,7 @@ it('handle resetting state with ref', () => {
)}
</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element).update(element);
@@ -389,7 +389,7 @@ it('handles getRootState', () => {
const ref = React.createRef<NavigationContainerRef>();
const element = (
<NavigationContainer ref={ref}>
<BaseNavigationContainer ref={ref}>
<TestNavigator initialRouteName="foo">
<Screen name="foo">
{() => (
@@ -401,7 +401,7 @@ it('handles getRootState', () => {
</Screen>
<Screen name="bar">{() => null}</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element);
@@ -450,13 +450,13 @@ it('emits state events when the state changes', () => {
const ref = React.createRef<NavigationContainerRef>();
const element = (
<NavigationContainer ref={ref}>
<BaseNavigationContainer ref={ref}>
<TestNavigator>
<Screen name="foo">{() => null}</Screen>
<Screen name="bar">{() => null}</Screen>
<Screen name="baz">{() => null}</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element).update(element);

View File

@@ -1,43 +0,0 @@
import * as React from 'react';
import { render } from 'react-native-testing-library';
import Screen from '../Screen';
import NavigationContainer from '../NavigationContainer';
import useNavigationBuilder from '../useNavigationBuilder';
import MockRouter, { MockRouterKey } from './__fixtures__/MockRouter';
beforeEach(() => (MockRouterKey.current = 0));
it('throws if NAVIGATE dispatched neither key nor name', () => {
const TestNavigator = (props: any) => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
return descriptors[state.routes[state.index].key].render();
};
const FooScreen = (props: any) => {
React.useEffect(() => {
props.navigation.navigate({});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return null;
};
const onStateChange = jest.fn();
const element = (
<NavigationContainer onStateChange={onStateChange}>
<TestNavigator initialRouteName="foo">
<Screen
name="foo"
component={FooScreen}
initialParams={{ count: 10 }}
/>
</TestNavigator>
</NavigationContainer>
);
expect(() => render(element).update(element)).toThrowError(
'While calling navigate with an object as the argument, you need to specify name or key'
);
});

View File

@@ -49,3 +49,27 @@ it('updates element if any props changed', () => {
expect(root).toMatchInlineSnapshot(`"second"`);
});
it('updates element if any props are added', () => {
expect.assertions(2);
const Test = ({ label }: any) => {
return label;
};
const root = render(
<StaticContainer count={42}>
<Test label="first" />
</StaticContainer>
);
expect(root).toMatchInlineSnapshot(`"first"`);
root.update(
<StaticContainer count={42} moreCounts={12}>
<Test label="second" />
</StaticContainer>
);
expect(root).toMatchInlineSnapshot(`"second"`);
});

View File

@@ -1,13 +1,13 @@
import BaseRouter from '../../BaseRouter';
import {
BaseRouter,
Router,
CommonAction,
CommonNavigationAction,
NavigationState,
Route,
DefaultRouterOptions,
} from '../../types';
} from '@react-navigation/routers';
export type MockActions = CommonAction | { type: 'NOOP' | 'UPDATE' };
export type MockActions = CommonNavigationAction | { type: 'NOOP' | 'UPDATE' };
export const MockRouterKey = { current: 0 };

View File

@@ -236,12 +236,14 @@ it('handles state with config with nested screens and unused configs', () => {
});
it('handles nested object with stringify in it', () => {
const path = '/bar/sweet/apple/foe/bis/jane?answer=42&count=10&valid=true';
const path = '/bar/sweet/apple/foo/bis/jane?answer=42&count=10&valid=true';
const config = {
Foo: {
path: 'foo',
screens: {
Foe: 'foe',
Foe: {
path: 'foe',
},
},
},
Bar: 'bar/:type/:fruit',
@@ -278,23 +280,16 @@ it('handles nested object with stringify in it', () => {
state: {
routes: [
{
name: 'Foe',
name: 'Baz',
state: {
routes: [
{
name: 'Baz',
state: {
routes: [
{
name: 'Bis',
params: {
author: 'Jane',
count: 10,
answer: '42',
valid: true,
},
},
],
name: 'Bis',
params: {
author: 'Jane',
count: 10,
answer: '42',
valid: true,
},
},
],
@@ -399,3 +394,98 @@ it('handles nested object for second route depth and and path and stringify in r
expect(getPathFromState(state, config)).toBe(path);
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
});
it('ignores empty string paths', () => {
const path = '/bar';
const config = {
Foo: {
path: '',
screens: {
Foe: 'foe',
},
},
Bar: 'bar',
};
const state = {
routes: [
{
name: 'Foo',
state: {
routes: [{ name: 'Bar' }],
},
},
],
};
expect(getPathFromState(state, config)).toBe(path);
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
});
it('cuts nested configs too', () => {
const path = '/baz';
const config = {
Foo: {
path: 'foo',
screens: {
Bar: '',
},
},
Baz: { path: 'baz' },
};
const state = {
routes: [
{
name: 'Foo',
state: {
routes: [
{
name: 'Bar',
state: {
routes: [{ name: 'Baz' }],
},
},
],
},
},
],
};
expect(getPathFromState(state, config)).toBe(path);
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
});
it('handles empty path at the end', () => {
const path = '/bar';
const config = {
Foo: {
path: 'foo',
screens: {
Bar: 'bar',
},
},
Baz: { path: '' },
};
const state = {
routes: [
{
name: 'Foo',
state: {
routes: [
{
name: 'Bar',
state: {
routes: [{ name: 'Baz' }],
},
},
],
},
},
],
};
expect(getPathFromState(state, config)).toBe(path);
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
});

View File

@@ -394,13 +394,6 @@ it('handles parse in nested object for second route depth and and path and parse
screens: {
Foe: 'foe',
Bar: {
path: 'bar/:id',
parse: {
id: Number,
},
stringify: {
id: (id: number) => `id=${id}`,
},
screens: {
Baz: 'baz',
},
@@ -433,24 +426,260 @@ it('handles parse in nested object for second route depth and and path and parse
);
});
it('returns undefined if path is empty', () => {
it('handles initialRouteName', () => {
const path = '/baz';
const config = {
Foo: {
path: 'foo/:id',
starting: true,
stringify: {
id: (id: number) => `id=${id}`,
},
initialRouteName: 'Foe',
screens: {
Foe: 'foe',
Bar: {
path: 'bar/:id',
parse: {
id: Number,
screens: {
Baz: 'baz',
},
},
},
},
};
const state = {
routes: [
{
name: 'Foo',
state: {
index: 1,
routes: [
{
name: 'Foe',
},
{
name: 'Bar',
state: {
routes: [{ name: 'Baz' }],
},
},
],
},
},
],
};
expect(getStateFromPath(path, config)).toEqual(state);
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
state
);
});
it('handles initialRouteName included in path', () => {
const path = '/baz';
const config = {
Foo: {
initialRouteName: 'Foe',
screens: {
Foe: {
screens: {
Baz: 'baz',
},
},
Bar: 'bar',
},
},
};
const state = {
routes: [
{
name: 'Foo',
state: {
routes: [
{
name: 'Foe',
state: {
routes: [{ name: 'Baz' }],
},
},
],
},
},
],
};
expect(getStateFromPath(path, config)).toEqual(state);
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
state
);
});
it('handles two initialRouteNames', () => {
const path = '/bar/sweet/apple/foe/bis/jane?count=10&answer=42&valid=true';
const config = {
Foo: {
path: 'foo',
screens: {
Foe: 'foe',
},
},
Bar: 'bar/:type/:fruit',
Baz: {
initialRouteName: 'Bos',
screens: {
Bos: 'bos',
Bis: {
path: 'bis/:author',
stringify: {
id: (id: number) => `id=${id}`,
author: (author: string) =>
author.replace(/^\w/, c => c.toLowerCase()),
},
parse: {
author: (author: string) =>
author.replace(/^\w/, c => c.toUpperCase()),
count: Number,
valid: Boolean,
},
},
},
},
};
const state = {
routes: [
{
name: 'Bar',
params: { fruit: 'apple', type: 'sweet' },
state: {
routes: [
{
name: 'Foo',
state: {
routes: [
{
name: 'Foe',
state: {
routes: [
{
name: 'Baz',
state: {
index: 1,
routes: [
{ name: 'Bos' },
{
name: 'Bis',
params: {
author: 'Jane',
count: 10,
answer: '42',
valid: true,
},
},
],
},
},
],
},
},
],
},
},
],
},
},
],
};
expect(getStateFromPath(path, config)).toEqual(state);
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
state
);
});
it('accepts initialRouteName without config for it', () => {
const path = '/bar/sweet/apple/foe/bis/jane?count=10&answer=42&valid=true';
const config = {
Foo: {
path: 'foo',
screens: {
Foe: 'foe',
},
},
Bar: 'bar/:type/:fruit',
Baz: {
initialRouteName: 'Bas',
screens: {
Bos: 'bos',
Bis: {
path: 'bis/:author',
stringify: {
author: (author: string) =>
author.replace(/^\w/, c => c.toLowerCase()),
},
parse: {
author: (author: string) =>
author.replace(/^\w/, c => c.toUpperCase()),
count: Number,
valid: Boolean,
},
},
},
},
};
const state = {
routes: [
{
name: 'Bar',
params: { fruit: 'apple', type: 'sweet' },
state: {
routes: [
{
name: 'Foo',
state: {
routes: [
{
name: 'Foe',
state: {
routes: [
{
name: 'Baz',
state: {
index: 1,
routes: [
{ name: 'Bas' },
{
name: 'Bis',
params: {
author: 'Jane',
count: 10,
answer: '42',
valid: true,
},
},
],
},
},
],
},
},
],
},
},
],
},
},
],
};
expect(getStateFromPath(path, config)).toEqual(state);
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
state
);
});
it('returns undefined if path is empty', () => {
const config = {
Foo: {
screens: {
Foe: 'foe',
Bar: {
screens: {
Baz: 'baz',
},

View File

@@ -1,11 +1,12 @@
import * as React from 'react';
import { render, act } from 'react-native-testing-library';
import { NavigationState } from '@react-navigation/routers';
import Screen from '../Screen';
import NavigationContainer from '../NavigationContainer';
import BaseNavigationContainer from '../BaseNavigationContainer';
import useNavigationBuilder from '../useNavigationBuilder';
import useNavigation from '../useNavigation';
import MockRouter, { MockRouterKey } from './__fixtures__/MockRouter';
import { NavigationState, NavigationContainerRef } from '../types';
import { NavigationContainerRef } from '../types';
beforeEach(() => (MockRouterKey.current = 0));
@@ -28,7 +29,7 @@ it('initializes state for a navigator on navigation', () => {
const onStateChange = jest.fn();
const element = (
<NavigationContainer onStateChange={onStateChange}>
<BaseNavigationContainer onStateChange={onStateChange}>
<TestNavigator initialRouteName="foo">
<Screen
name="foo"
@@ -44,7 +45,7 @@ it('initializes state for a navigator on navigation', () => {
)}
</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element).update(element);
@@ -75,11 +76,11 @@ it("doesn't crash when initialState is null", () => {
const element = (
// @ts-ignore
<NavigationContainer initialState={null}>
<BaseNavigationContainer initialState={null}>
<TestNavigator>
<Screen name="foo" component={TestScreen} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(() => render(element)).not.toThrowError();
@@ -112,7 +113,7 @@ it('rehydrates state for a navigator on navigation', () => {
const onStateChange = jest.fn();
const element = (
<NavigationContainer
<BaseNavigationContainer
initialState={initialState}
onStateChange={onStateChange}
>
@@ -120,7 +121,7 @@ it('rehydrates state for a navigator on navigation', () => {
<Screen name="foo" component={jest.fn()} />
<Screen name="bar" component={BarScreen} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element).update(element);
@@ -166,7 +167,7 @@ it("doesn't rehydrate state if the type of state didn't match router", () => {
const onStateChange = jest.fn();
const element = (
<NavigationContainer
<BaseNavigationContainer
initialState={initialState}
onStateChange={onStateChange}
>
@@ -178,7 +179,7 @@ it("doesn't rehydrate state if the type of state didn't match router", () => {
/>
<Screen name="bar" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element).update(element);
@@ -219,7 +220,7 @@ it('initializes state for nested screens in React.Fragment', () => {
const onStateChange = jest.fn();
const element = (
<NavigationContainer onStateChange={onStateChange}>
<BaseNavigationContainer onStateChange={onStateChange}>
<TestNavigator>
<Screen name="foo" component={TestScreen} />
<React.Fragment>
@@ -227,7 +228,7 @@ it('initializes state for nested screens in React.Fragment', () => {
<Screen name="baz" component={jest.fn()} />
</React.Fragment>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element).update(element);
@@ -266,7 +267,7 @@ it('initializes state for nested navigator on navigation', () => {
const onStateChange = jest.fn();
const element = (
<NavigationContainer onStateChange={onStateChange}>
<BaseNavigationContainer onStateChange={onStateChange}>
<TestNavigator initialRouteName="baz">
<Screen name="foo" component={jest.fn()} />
<Screen name="bar" component={jest.fn()} />
@@ -278,7 +279,7 @@ it('initializes state for nested navigator on navigation', () => {
)}
</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element).update(element);
@@ -328,12 +329,12 @@ it("doesn't update state if nothing changed", () => {
const onStateChange = jest.fn();
render(
<NavigationContainer onStateChange={onStateChange}>
<BaseNavigationContainer onStateChange={onStateChange}>
<TestNavigator initialRouteName="foo">
<Screen name="foo" component={FooScreen} />
<Screen name="bar" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(onStateChange).toBeCalledTimes(0);
@@ -360,12 +361,12 @@ it("doesn't update state if action wasn't handled", () => {
const spy = jest.spyOn(console, 'error').mockImplementation();
render(
<NavigationContainer onStateChange={onStateChange}>
<BaseNavigationContainer onStateChange={onStateChange}>
<TestNavigator initialRouteName="foo">
<Screen name="foo" component={FooScreen} />
<Screen name="bar" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(onStateChange).toBeCalledTimes(0);
@@ -396,12 +397,12 @@ it('cleans up state when the navigator unmounts', () => {
const onStateChange = jest.fn();
const element = (
<NavigationContainer onStateChange={onStateChange}>
<BaseNavigationContainer onStateChange={onStateChange}>
<TestNavigator>
<Screen name="foo" component={FooScreen} />
<Screen name="bar" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
const root = render(element);
@@ -422,7 +423,7 @@ it('cleans up state when the navigator unmounts', () => {
});
root.update(
<NavigationContainer onStateChange={onStateChange} children={null} />
<BaseNavigationContainer onStateChange={onStateChange} children={null} />
);
expect(onStateChange).toBeCalledTimes(2);
@@ -454,12 +455,12 @@ it('allows state updates by dispatching a function returning an action', () => {
const onStateChange = jest.fn();
const element = (
<NavigationContainer onStateChange={onStateChange}>
<BaseNavigationContainer onStateChange={onStateChange}>
<TestNavigator initialRouteName="foo">
<Screen name="foo" component={FooScreen} />
<Screen name="bar" component={BarScreen} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element).update(element);
@@ -496,12 +497,12 @@ it('updates route params with setParams', () => {
const onStateChange = jest.fn();
render(
<NavigationContainer onStateChange={onStateChange}>
<BaseNavigationContainer onStateChange={onStateChange}>
<TestNavigator initialRouteName="foo">
<Screen name="foo" component={FooScreen} />
<Screen name="bar" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
act(() => setParams({ username: 'alice' }));
@@ -556,7 +557,7 @@ it('updates route params with setParams applied to parent', () => {
const onStateChange = jest.fn();
render(
<NavigationContainer onStateChange={onStateChange}>
<BaseNavigationContainer onStateChange={onStateChange}>
<TestNavigator initialRouteName="foo">
<Screen name="foo">
{() => (
@@ -567,7 +568,7 @@ it('updates route params with setParams applied to parent', () => {
</Screen>
<Screen name="bar" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
act(() => setParams({ username: 'alice' }));
@@ -634,22 +635,22 @@ it('handles change in route names', () => {
const onStateChange = jest.fn();
const root = render(
<NavigationContainer onStateChange={onStateChange}>
<BaseNavigationContainer onStateChange={onStateChange}>
<TestNavigator initialRouteName="bar">
<Screen name="foo" component={jest.fn()} />
<Screen name="bar" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
root.update(
<NavigationContainer onStateChange={onStateChange}>
<BaseNavigationContainer onStateChange={onStateChange}>
<TestNavigator>
<Screen name="foo" component={jest.fn()} />
<Screen name="baz" component={jest.fn()} />
<Screen name="qux" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(onStateChange).toBeCalledWith({
@@ -677,7 +678,7 @@ it('navigates to nested child in a navigator', () => {
const navigation = React.createRef<NavigationContainerRef>();
const element = render(
<NavigationContainer ref={navigation} onStateChange={onStateChange}>
<BaseNavigationContainer ref={navigation} onStateChange={onStateChange}>
<TestNavigator>
<Screen name="foo">
{() => (
@@ -704,7 +705,7 @@ it('navigates to nested child in a navigator', () => {
)}
</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(element).toMatchInlineSnapshot(`"[foo-a, undefined]"`);
@@ -752,11 +753,11 @@ it('gives access to internal state', () => {
};
const root = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator initialRouteName="bar">
<Screen name="bar" component={Test} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(root).update(root);
@@ -778,9 +779,9 @@ it("throws if navigator doesn't have any screens", () => {
};
const element = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator />
</NavigationContainer>
</BaseNavigationContainer>
);
expect(() => render(element).update(element)).toThrowError(
@@ -812,14 +813,14 @@ it('throws if multiple navigators rendered under one container', () => {
};
const element = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name="foo" component={jest.fn()} />
</TestNavigator>
<TestNavigator>
<Screen name="foo" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(() => render(element).update(element)).toThrowError(
@@ -836,12 +837,12 @@ it('throws when Screen is not the direct children', () => {
const Bar = () => null;
const element = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name="foo" component={jest.fn()} />
<Bar />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(() => render(element).update(element)).toThrowError(
@@ -856,12 +857,12 @@ it('throws when a React Element is not the direct children', () => {
};
const element = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name="foo" component={jest.fn()} />
Hello world
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(() => render(element).update(element)).toThrowError(
@@ -877,7 +878,7 @@ it("doesn't throw when direct children is Screen or empty element", () => {
};
render(
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name="foo" component={jest.fn()} />
{null}
@@ -885,7 +886,7 @@ it("doesn't throw when direct children is Screen or empty element", () => {
{false}
{true}
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
});
@@ -896,13 +897,13 @@ it('throws when multiple screens with same name are defined', () => {
};
const element = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name="foo" component={jest.fn()} />
<Screen name="bar" component={jest.fn()} />
<Screen name="foo" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(() => render(element).update(element)).toThrowError(
@@ -917,20 +918,20 @@ it('switches rendered navigators', () => {
};
const root = render(
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator key="a">
<Screen name="foo" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(() =>
root.update(
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator key="b">
<Screen name="foo" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
)
).not.toThrowError(
'Another navigator is already registered for this container.'
@@ -944,11 +945,11 @@ it('throws if no name is passed to Screen', () => {
};
const element = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name={undefined as any} component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(() => render(element).update(element)).toThrowError(
@@ -963,11 +964,11 @@ it('throws if invalid name is passed to Screen', () => {
};
const element = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name={[] as any} component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(() => render(element).update(element)).toThrowError(
@@ -982,13 +983,13 @@ it('throws if both children and component are passed', () => {
};
const element = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name="foo" component={jest.fn()}>
{jest.fn()}
</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(() => render(element).update(element)).toThrowError(
@@ -1003,11 +1004,11 @@ it('throws descriptive error for undefined screen component', () => {
};
const element = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name="foo" component={undefined as any} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(() => render(element).update(element)).toThrowError(
@@ -1022,11 +1023,11 @@ it('throws descriptive error for invalid screen component', () => {
};
const element = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name="foo" component={{} as any} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(() => render(element).update(element)).toThrowError(
@@ -1041,11 +1042,11 @@ it('throws descriptive error for invalid children', () => {
};
const element = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name="foo">{[] as any}</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(() => render(element).update(element)).toThrowError(
@@ -1060,13 +1061,13 @@ it("doesn't throw if children is null", () => {
};
const element = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name="foo" component={jest.fn()}>
{null as any}
</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(() => render(element).update(element)).not.toThrowError();

View File

@@ -51,20 +51,55 @@ it('returns false for non-serializable object', () => {
});
it('returns false for circular references', () => {
const o = {
index: 0,
key: '7',
routeNames: ['foo', 'bar'],
routes: [
{
key: 'foo',
name: 'foo',
},
],
const x = {
a: 1,
b: { b1: 1 },
};
// @ts-ignore
o.routes[0].state = o;
x.b.b2 = x;
// @ts-ignore
x.c = x.b;
expect(isSerializable(o)).toBe(false);
expect(isSerializable(x)).toBe(false);
const y = [
{
label: 'home',
children: [{ label: 'product' }],
},
{ label: 'about', extend: {} },
];
// @ts-ignore
y[0].children[0].parent = y[0];
// @ts-ignore
y[1].extend.home = y[0].children[0];
expect(isSerializable(y)).toBe(false);
const z = {
name: 'sun',
child: [{ name: 'flower' }],
};
// @ts-ignore
z.child[0].parent = z;
expect(isSerializable(z)).toBe(false);
});
it("doesn't fail if same object used multiple times", () => {
const o = { foo: 'bar' };
expect(
isSerializable({
baz: 'bax',
first: o,
second: o,
stuff: {
b: o,
},
})
).toBe(true);
});

View File

@@ -1,13 +1,17 @@
import * as React from 'react';
import { render, act } from 'react-native-testing-library';
import {
DefaultRouterOptions,
NavigationState,
Router,
} from '@react-navigation/routers';
import useNavigationBuilder from '../useNavigationBuilder';
import NavigationContainer from '../NavigationContainer';
import BaseNavigationContainer from '../BaseNavigationContainer';
import Screen from '../Screen';
import MockRouter, {
MockActions,
MockRouterKey,
} from './__fixtures__/MockRouter';
import { DefaultRouterOptions, NavigationState, Router } from '../types';
jest.useFakeTimers();
@@ -34,7 +38,7 @@ it('sets options with options prop as an object', () => {
const TestScreen = (): any => 'Test screen';
const root = render(
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen
name="foo"
@@ -43,7 +47,7 @@ it('sets options with options prop as an object', () => {
/>
<Screen name="bar" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(root).toMatchInlineSnapshot(`
@@ -79,7 +83,7 @@ it('sets options with options prop as a fuction', () => {
const TestScreen = (): any => 'Test screen';
const root = render(
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen
name="foo"
@@ -89,7 +93,7 @@ it('sets options with options prop as a fuction', () => {
/>
<Screen name="bar" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(root).toMatchInlineSnapshot(`
@@ -134,12 +138,12 @@ it('sets options with screenOptions prop as an object', () => {
const TestScreenB = (): any => 'Test screen B';
const root = render(
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator screenOptions={{ title: 'Hello world' }}>
<Screen name="foo" component={TestScreenA} />
<Screen name="bar" component={TestScreenB} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(root).toMatchInlineSnapshot(`
@@ -194,7 +198,7 @@ it('sets options with screenOptions prop as a fuction', () => {
const TestScreenB = (): any => 'Test screen B';
const root = render(
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator
screenOptions={({ route }: any) => ({
title: `${route.name}: ${route.params.author || route.params.fruit}`,
@@ -211,7 +215,7 @@ it('sets options with screenOptions prop as a fuction', () => {
initialParams={{ fruit: 'Apple' }}
/>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(root).toMatchInlineSnapshot(`
@@ -266,14 +270,14 @@ it('sets initial options with setOptions', () => {
};
const root = render(
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name="foo" options={{ color: 'blue' }}>
{props => <TestScreen {...props} />}
</Screen>
<Screen name="bar" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(root).toMatchInlineSnapshot(`
@@ -331,14 +335,14 @@ it('updates options with setOptions', () => {
};
const element = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name="foo" options={{ color: 'blue' }}>
{props => <TestScreen {...props} />}
</Screen>
<Screen name="bar" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
const root = render(element);
@@ -396,7 +400,7 @@ it("returns correct value for canGoBack when it's not overridden", () => {
};
const root = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen
name="foo"
@@ -405,7 +409,7 @@ it("returns correct value for canGoBack when it's not overridden", () => {
/>
<Screen name="bar" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(root).update(root);
@@ -452,11 +456,11 @@ it(`returns false for canGoBack when current router doesn't handle GO_BACK`, ()
};
const root = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name="baz" component={TestScreen} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(root).update(root);
@@ -513,7 +517,7 @@ it('returns true for canGoBack when current router handles GO_BACK', () => {
};
const root = (
<NavigationContainer>
<BaseNavigationContainer>
<ParentNavigator>
<Screen name="baz">
{() => (
@@ -523,7 +527,7 @@ it('returns true for canGoBack when current router handles GO_BACK', () => {
)}
</Screen>
</ParentNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(root).update(root);
@@ -580,7 +584,7 @@ it('returns true for canGoBack when parent router handles GO_BACK', () => {
};
const root = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name="baz">
{() => (
@@ -597,7 +601,7 @@ it('returns true for canGoBack when parent router handles GO_BACK', () => {
)}
</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(root).update(root);

View File

@@ -1,10 +1,10 @@
import * as React from 'react';
import { render, act } from 'react-native-testing-library';
import { Router, NavigationState } from '@react-navigation/routers';
import useNavigationBuilder from '../useNavigationBuilder';
import NavigationContainer from '../NavigationContainer';
import BaseNavigationContainer from '../BaseNavigationContainer';
import Screen from '../Screen';
import MockRouter from './__fixtures__/MockRouter';
import { Router, NavigationState } from '../types';
it('fires focus and blur events in root navigator', () => {
const TestNavigator = React.forwardRef((props: any, ref: any): any => {
@@ -47,7 +47,7 @@ it('fires focus and blur events in root navigator', () => {
const navigation = React.createRef<any>();
const element = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator ref={navigation}>
<Screen
name="first"
@@ -66,7 +66,7 @@ it('fires focus and blur events in root navigator', () => {
component={createComponent(fourthFocusCallback, fourthBlurCallback)}
/>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element);
@@ -139,7 +139,7 @@ it('fires focus and blur events in nested navigator', () => {
const child = React.createRef<any>();
const element = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator ref={parent}>
<Screen
name="first"
@@ -170,7 +170,7 @@ it('fires focus and blur events in nested navigator', () => {
)}
</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element);
@@ -307,12 +307,12 @@ it('fires blur event when a route is removed with a delay', async () => {
const navigation = React.createRef<any>();
const element = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator ref={navigation}>
<Screen name="first" component={First} />
<Screen name="second" component={Second} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element);
@@ -363,13 +363,13 @@ it('fires custom events', () => {
const ref = React.createRef<any>();
const element = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator ref={ref}>
<Screen name="first" component={createComponent(firstCallback)} />
<Screen name="second" component={createComponent(secondCallback)} />
<Screen name="third" component={createComponent(thirdCallback)} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element);
@@ -444,11 +444,11 @@ it('has option to prevent default', () => {
const ref = React.createRef<any>();
const element = (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator ref={ref}>
<Screen name="first" component={Test} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element);

View File

@@ -2,7 +2,7 @@ import * as React from 'react';
import { render, act } from 'react-native-testing-library';
import useNavigationBuilder from '../useNavigationBuilder';
import useFocusEffect from '../useFocusEffect';
import NavigationContainer from '../NavigationContainer';
import BaseNavigationContainer from '../BaseNavigationContainer';
import Screen from '../Screen';
import MockRouter from './__fixtures__/MockRouter';
@@ -31,13 +31,13 @@ it('runs focus effect on focus change', () => {
const navigation = React.createRef<any>();
const element = (
<NavigationContainer ref={navigation}>
<BaseNavigationContainer ref={navigation}>
<TestNavigator>
<Screen name="first">{() => null}</Screen>
<Screen name="second" component={Test} />
<Screen name="third">{() => null}</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element);
@@ -84,12 +84,12 @@ it('runs focus effect on deps change', () => {
};
const App = ({ count }: { count: number }) => (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name="first">{() => <Test count={count} />}</Screen>
<Screen name="second">{() => null}</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
const root = render(<App count={1} />);
@@ -135,13 +135,13 @@ it('runs focus effect when initial state is given', () => {
const navigation = React.createRef<any>();
const element = (
<NavigationContainer ref={navigation} initialState={initialState}>
<BaseNavigationContainer ref={navigation} initialState={initialState}>
<TestNavigator>
<Screen name="first">{() => null}</Screen>
<Screen name="second">{() => null}</Screen>
<Screen name="third" component={Test} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element);
@@ -178,13 +178,13 @@ it('runs focus effect when only focused route is rendered', () => {
const navigation = React.createRef<any>();
const element = (
<NavigationContainer ref={navigation}>
<BaseNavigationContainer ref={navigation}>
<TestNavigator>
<Screen name="first" component={Test} />
<Screen name="second">{() => null}</Screen>
<Screen name="third">{() => null}</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element);
@@ -221,12 +221,12 @@ it('runs cleanup when component is unmounted', () => {
const TestB = () => null;
const App = ({ mounted }: { mounted: boolean }) => (
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name="first" component={mounted ? TestA : TestB} />
<Screen name="second">{() => null}</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
const root = render(<App mounted />);

View File

@@ -2,7 +2,7 @@ import * as React from 'react';
import { render, act } from 'react-native-testing-library';
import useNavigationBuilder from '../useNavigationBuilder';
import useIsFocused from '../useIsFocused';
import NavigationContainer from '../NavigationContainer';
import BaseNavigationContainer from '../BaseNavigationContainer';
import Screen from '../Screen';
import MockRouter from './__fixtures__/MockRouter';
@@ -24,13 +24,13 @@ it('renders correct focus state', () => {
const navigation = React.createRef<any>();
const root = render(
<NavigationContainer ref={navigation}>
<BaseNavigationContainer ref={navigation}>
<TestNavigator>
<Screen name="first">{() => null}</Screen>
<Screen name="second" component={Test} />
<Screen name="third">{() => null}</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(root).toMatchInlineSnapshot(`"unfocused"`);

View File

@@ -2,7 +2,7 @@ import * as React from 'react';
import { render } from 'react-native-testing-library';
import useNavigationBuilder from '../useNavigationBuilder';
import useNavigation from '../useNavigation';
import NavigationContainer from '../NavigationContainer';
import BaseNavigationContainer from '../BaseNavigationContainer';
import Screen from '../Screen';
import MockRouter from './__fixtures__/MockRouter';
@@ -24,11 +24,11 @@ it('gets navigation prop from context', () => {
};
render(
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name="foo" component={Test} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
});
@@ -50,7 +50,7 @@ it("gets navigation's parent from context", () => {
};
render(
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name="foo">
{() => (
@@ -60,7 +60,7 @@ it("gets navigation's parent from context", () => {
)}
</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
});
@@ -86,7 +86,7 @@ it("gets navigation's parent's parent from context", () => {
};
render(
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name="foo">
{() => (
@@ -102,7 +102,7 @@ it("gets navigation's parent's parent from context", () => {
)}
</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
});
@@ -112,7 +112,7 @@ it('throws if called outside a navigation context', () => {
const Test = () => {
// eslint-disable-next-line react-hooks/rules-of-hooks
expect(() => useNavigation()).toThrow(
"We couldn't find a navigation object. Is your component inside a navigator?"
"We couldn't find a navigation object. Is your component inside a screen in a navigator?"
);
return null;

View File

@@ -1,11 +1,11 @@
import * as React from 'react';
import { render, act } from 'react-native-testing-library';
import { NavigationState } from '@react-navigation/routers';
import useNavigationBuilder from '../useNavigationBuilder';
import useNavigationState from '../useNavigationState';
import NavigationContainer from '../NavigationContainer';
import BaseNavigationContainer from '../BaseNavigationContainer';
import Screen from '../Screen';
import MockRouter from './__fixtures__/MockRouter';
import { NavigationState } from '../types';
it('gets the current navigation state', () => {
const TestNavigator = (props: any): any => {
@@ -27,13 +27,13 @@ it('gets the current navigation state', () => {
const navigation = React.createRef<any>();
const element = (
<NavigationContainer ref={navigation}>
<BaseNavigationContainer ref={navigation}>
<TestNavigator>
<Screen name="first" component={Test} />
<Screen name="second">{() => null}</Screen>
<Screen name="third">{() => null}</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element);
@@ -78,13 +78,13 @@ it('gets the current navigation state with selector', () => {
const navigation = React.createRef<any>();
const element = (
<NavigationContainer ref={navigation}>
<BaseNavigationContainer ref={navigation}>
<TestNavigator>
<Screen name="first" component={Test} />
<Screen name="second">{() => null}</Screen>
<Screen name="third">{() => null}</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element);
@@ -133,13 +133,13 @@ it('gets the correct value if selector changes', () => {
const App = ({ selector }: { selector: (state: NavigationState) => any }) => {
return (
<SelectorContext.Provider value={selector}>
<NavigationContainer ref={navigation}>
<BaseNavigationContainer ref={navigation}>
<TestNavigator>
<Screen name="first" component={Test} />
<Screen name="second">{() => null}</Screen>
<Screen name="third">{() => null}</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
</SelectorContext.Provider>
);
};

View File

@@ -1,13 +1,17 @@
import * as React from 'react';
import { render } from 'react-native-testing-library';
import {
Router,
DefaultRouterOptions,
NavigationState,
} from '@react-navigation/routers';
import useNavigationBuilder from '../useNavigationBuilder';
import NavigationContainer from '../NavigationContainer';
import BaseNavigationContainer from '../BaseNavigationContainer';
import Screen from '../Screen';
import MockRouter, {
MockActions,
MockRouterKey,
} from './__fixtures__/MockRouter';
import { Router, DefaultRouterOptions, NavigationState } from '../types';
beforeEach(() => (MockRouterKey.current = 0));
@@ -58,7 +62,7 @@ it("lets parent handle the action if child didn't", () => {
const onStateChange = jest.fn();
render(
<NavigationContainer onStateChange={onStateChange}>
<BaseNavigationContainer onStateChange={onStateChange}>
<ParentNavigator initialRouteName="baz">
<Screen name="foo">{() => null}</Screen>
<Screen name="bar">{() => null}</Screen>
@@ -70,7 +74,7 @@ it("lets parent handle the action if child didn't", () => {
)}
</Screen>
</ParentNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
expect(onStateChange).toBeCalledTimes(1);
@@ -171,7 +175,7 @@ it("lets children handle the action if parent didn't", () => {
};
const element = (
<NavigationContainer
<BaseNavigationContainer
initialState={initialState}
onStateChange={onStateChange}
>
@@ -187,7 +191,7 @@ it("lets children handle the action if parent didn't", () => {
)}
</Screen>
</ParentNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element).update(element);
@@ -284,7 +288,7 @@ it("action doesn't bubble if target is specified", () => {
const onStateChange = jest.fn();
const element = (
<NavigationContainer onStateChange={onStateChange}>
<BaseNavigationContainer onStateChange={onStateChange}>
<ParentNavigator>
<Screen name="foo">{() => null}</Screen>
<Screen name="bar" component={TestScreen} />
@@ -297,7 +301,7 @@ it("action doesn't bubble if target is specified", () => {
)}
</Screen>
</ParentNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
render(element).update(element);
@@ -349,7 +353,7 @@ it('logs error if no navigator handled the action', () => {
};
const element = (
<NavigationContainer initialState={initialState}>
<BaseNavigationContainer initialState={initialState}>
<TestNavigator>
<Screen name="foo">{() => null}</Screen>
<Screen name="bar" component={TestScreen} />
@@ -362,7 +366,7 @@ it('logs error if no navigator handled the action', () => {
)}
</Screen>
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
const spy = jest.spyOn(console, 'error').mockImplementation();

View File

@@ -2,7 +2,7 @@ import * as React from 'react';
import { render } from 'react-native-testing-library';
import useNavigationBuilder from '../useNavigationBuilder';
import useRoute from '../useRoute';
import NavigationContainer from '../NavigationContainer';
import BaseNavigationContainer from '../BaseNavigationContainer';
import Screen from '../Screen';
import MockRouter from './__fixtures__/MockRouter';
import { RouteProp } from '../types';
@@ -25,10 +25,10 @@ it('gets route prop from context', () => {
};
render(
<NavigationContainer>
<BaseNavigationContainer>
<TestNavigator>
<Screen name="foo" component={Test} initialParams={{ x: 1 }} />
</TestNavigator>
</NavigationContainer>
</BaseNavigationContainer>
);
});

View File

@@ -1,6 +1,7 @@
import * as React from 'react';
import { ParamListBase } from '@react-navigation/routers';
import Screen from './Screen';
import { ParamListBase, TypedNavigator } from './types';
import { TypedNavigator } from './types';
/**
* Higher order component to create a `Navigator` and `Screen` pair.
@@ -20,7 +21,7 @@ export default function createNavigatorFactory<
> {
if (arguments[0] !== undefined) {
throw new Error(
"Creating a navigator doesn't take an argument. Maybe you are trying to use React Navigation 4 API with React Navigation 5?"
"Creating a navigator doesn't take an argument. Maybe you are trying to use React Navigation 4 API with React Navigation 5? See https://reactnavigation.org/docs/en/hello-react-navigation.html for usage guide."
);
}

View File

@@ -1,4 +1,4 @@
import { PartialState, NavigationState } from './types';
import { PartialState, NavigationState } from '@react-navigation/routers';
type NavigateParams = {
screen?: string;

View File

@@ -1,5 +1,9 @@
import queryString from 'query-string';
import { NavigationState, PartialState, Route } from './types';
import {
NavigationState,
PartialState,
Route,
} from '@react-navigation/routers';
type State = NavigationState | Omit<PartialState<NavigationState>, 'stale'>;
@@ -9,7 +13,7 @@ type Options = {
[routeName: string]:
| string
| {
path: string;
path?: string;
stringify?: StringifyConfig;
screens?: Options;
};
@@ -66,73 +70,94 @@ export default function getPathFromState(
pattern = currentOptions[route.name] as string;
break;
} else if (typeof currentOptions[route.name] === 'object') {
if (route.state === undefined) {
// if there is no `screens` property, we return pattern
if (
!(currentOptions[route.name] as {
screens: Options;
}).screens
) {
pattern = (currentOptions[route.name] as { path: string }).path;
break;
} else {
if (!(currentOptions[route.name] as { screens?: Options }).screens) {
throw Error('Wrong Options object passed');
// if it is the end of state, we return pattern
if (route.state === undefined) {
pattern = (currentOptions[route.name] as { path: string }).path;
break;
} else {
index =
typeof route.state.index === 'number' ? route.state.index : 0;
const nextRoute = route.state.routes[index];
const deeperConfig = (currentOptions[route.name] as {
screens: Options;
}).screens;
// if there is config for next route name, we go deeper
if (nextRoute.name in deeperConfig) {
route = nextRoute as Route<string> & { state?: State };
currentOptions = deeperConfig;
} else {
// if not, there is no sense in going deeper in config
pattern = (currentOptions[route.name] as { path: string }).path;
break;
}
}
currentOptions = (currentOptions[route.name] as { screens: Options })
.screens;
index = typeof route.state.index === 'number' ? route.state.index : 0;
route = route.state.routes[index] as Route<string> & {
state?: State;
};
}
}
}
const config =
currentOptions[route.name] !== undefined
? (currentOptions[route.name] as { stringify?: StringifyConfig })
.stringify
// we don't add empty path strings to path
if (pattern !== '') {
const config =
currentOptions[route.name] !== undefined
? (currentOptions[route.name] as { stringify?: StringifyConfig })
.stringify
: undefined;
const params = route.params
? // Stringify all of the param values before we use them
Object.entries(route.params).reduce<{
[key: string]: string;
}>((acc, [key, value]) => {
acc[key] = config?.[key] ? config[key](value) : String(value);
return acc;
}, {})
: undefined;
const params = route.params
? // Stringify all of the param values before we use them
Object.entries(route.params).reduce<{
[key: string]: string;
}>((acc, [key, value]) => {
acc[key] = config?.[key] ? config[key](value) : String(value);
return acc;
}, {})
: undefined;
if (currentOptions[route.name] !== undefined) {
path += pattern
.split('/')
.map(p => {
const name = p.replace(/^:/, '');
if (currentOptions[route.name] !== undefined) {
path += pattern
.split('/')
.map(p => {
const name = p.replace(/^:/, '');
// If the path has a pattern for a param, put the param in the path
if (params && name in params && p.startsWith(':')) {
const value = params[name];
// Remove the used value from the params object since we'll use the rest for query string
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete params[name];
return encodeURIComponent(value);
}
// If the path has a pattern for a param, put the param in the path
if (params && name in params && p.startsWith(':')) {
const value = params[name];
// Remove the used value from the params object since we'll use the rest for query string
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete params[name];
return encodeURIComponent(value);
}
return encodeURIComponent(p);
})
.join('/');
} else {
path += encodeURIComponent(route.name);
}
return encodeURIComponent(p);
})
.join('/');
} else {
path += encodeURIComponent(route.name);
}
if (route.state) {
path += '/';
} else if (params) {
const query = queryString.stringify(params);
if (route.state) {
path += '/';
} else if (params) {
const query = queryString.stringify(params);
if (query) {
path += `?${query}`;
if (query) {
path += `?${query}`;
}
}
}
current = route.state;
}
path = path.slice(path.length - 1) === '/' ? path.slice(0, -1) : path;
return path;
}

View File

@@ -1,6 +1,10 @@
import escape from 'escape-string-regexp';
import queryString from 'query-string';
import { NavigationState, PartialState, InitialState } from './types';
import {
NavigationState,
PartialState,
InitialState,
} from '@react-navigation/routers';
type ParseConfig = Record<string, (value: string) => any>;
@@ -8,9 +12,10 @@ type Options = {
[routeName: string]:
| string
| {
path: string;
path?: string;
parse?: ParseConfig;
screens?: Options;
initialRouteName?: string;
};
};
@@ -21,6 +26,11 @@ type RouteConfig = {
parse: ParseConfig | undefined;
};
type InitialRouteConfig = {
initialRouteName: string;
connectedRoutes: string[];
};
type ResultState = PartialState<NavigationState> & {
state?: ResultState;
};
@@ -51,9 +61,12 @@ export default function getStateFromPath(
if (path === '') {
return undefined;
}
let initialRoutes: InitialRouteConfig[] = [];
// Create a normalized configs array which will be easier to use
const configs = ([] as RouteConfig[]).concat(
...Object.keys(options).map(key => createNormalizedConfigs(key, options))
...Object.keys(options).map(key =>
createNormalizedConfigs(key, options, [], initialRoutes)
)
);
let result: PartialState<NavigationState> | undefined;
@@ -65,8 +78,8 @@ export default function getStateFromPath(
.replace(/\?.*/, ''); // Remove query params which we will handle later
while (remaining) {
let routeNames;
let params;
let routeNames: string[] | undefined;
let params: Record<string, any> | undefined;
// Go through all configs, and see if the next path segment matches our regex
for (const config of configs) {
@@ -111,46 +124,43 @@ export default function getStateFromPath(
}
let state: InitialState;
let routeName = routeNames.shift() as string;
let initialRoute = findInitialRoute(routeName, initialRoutes);
if (routeNames.length === 1) {
state = {
routes: [
{ name: routeNames.shift() as string, ...(params && { params }) },
],
};
} else {
state = {
routes: [{ name: routeNames.shift() as string, state: { routes: [] } }],
};
state = createNestedState(
initialRoute,
routeName,
routeNames.length === 0,
params
);
let helper = state.routes[0].state as InitialState;
let routeName;
if (routeNames.length > 0) {
let nestedState = state;
while ((routeName = routeNames.shift())) {
if (routeNames.length === 0) {
helper.routes.push({
name: routeName,
...(params && { params }),
});
} else {
helper.routes[0] = {
name: routeName,
state: {
routes: [],
},
};
helper = helper.routes[0].state as InitialState;
while ((routeName = routeNames.shift() as string)) {
initialRoute = findInitialRoute(routeName, initialRoutes);
nestedState.routes[nestedState.index || 0].state = createNestedState(
initialRoute,
routeName,
routeNames.length === 0,
params
);
if (routeNames.length > 0) {
nestedState = nestedState.routes[nestedState.index || 0]
.state as InitialState;
}
}
}
if (current) {
// The state should be nested inside the deepest route we parsed before
while (current.routes[0].state) {
current = current.routes[0].state;
while (current?.routes[current.index || 0].state) {
current = current.routes[current.index || 0].state;
}
current.routes[0].state = state;
(current as PartialState<NavigationState>).routes[
current?.index || 0
].state = state;
} else {
result = state;
}
@@ -165,12 +175,14 @@ export default function getStateFromPath(
const query = path.split('?')[1];
if (query) {
while (current.routes[0].state) {
while (current?.routes[current.index || 0].state) {
// The query params apply to the deepest route
current = current.routes[0].state;
current = current.routes[current.index || 0].state;
}
const route = current.routes[0];
const route = (current as PartialState<NavigationState>).routes[
current?.index || 0
];
const params = queryString.parse(query);
const parseFunction = findParseConfigForRoute(route.name, configs);
@@ -192,7 +204,8 @@ export default function getStateFromPath(
function createNormalizedConfigs(
key: string,
routeConfig: Options,
routeNames: string[] = []
routeNames: string[] = [],
initials: InitialRouteConfig[]
): RouteConfig[] {
const configs: RouteConfig[] = [];
@@ -202,18 +215,30 @@ function createNormalizedConfigs(
if (typeof value === 'string') {
// If a string is specified as the value of the key(e.g. Foo: '/path'), use it as the pattern
configs.push(createConfigItem(routeNames, value));
if (value !== '') {
configs.push(createConfigItem(routeNames, value));
}
} else if (typeof value === 'object') {
// if an object is specified as the value (e.g. Foo: { ... }),
// it has `path` property and
// it can have `path` property and
// it could have `screens` prop which has nested configs
configs.push(createConfigItem(routeNames, value.path, value.parse));
if (value.path && value.path !== '') {
configs.push(createConfigItem(routeNames, value.path, value.parse));
}
if (value.screens) {
// property `initialRouteName` without `screens` has no purpose
if (value.initialRouteName) {
initials.push({
initialRouteName: value.initialRouteName,
connectedRoutes: Object.keys(value.screens),
});
}
Object.keys(value.screens).forEach(nestedConfig => {
const result = createNormalizedConfigs(
nestedConfig,
value.screens as Options,
routeNames
routeNames,
initials
);
configs.push(...result);
});
@@ -254,3 +279,55 @@ function findParseConfigForRoute(
}
return undefined;
}
// tries to find an initial route connected with the one passed
function findInitialRoute(
routeName: string,
initialRoutes: InitialRouteConfig[]
): string | undefined {
for (const config of initialRoutes) {
if (config.connectedRoutes.includes(routeName)) {
return config.initialRouteName === routeName
? undefined
: config.initialRouteName;
}
}
return undefined;
}
// returns nested state object with values depending on whether
// it is the end of state and if there is initialRoute for this level
function createNestedState(
initialRoute: string | undefined,
routeName: string,
isEmpty: boolean,
params?: Record<string, any> | undefined
): InitialState {
if (isEmpty) {
if (initialRoute) {
return {
index: 1,
routes: [
{ name: initialRoute },
{ name: routeName as string, ...(params && { params }) },
],
};
} else {
return {
routes: [{ name: routeName as string, ...(params && { params }) }],
};
}
} else {
if (initialRoute) {
return {
index: 1,
routes: [
{ name: initialRoute },
{ name: routeName as string, state: { routes: [] } },
],
};
} else {
return { routes: [{ name: routeName as string, state: { routes: [] } }] };
}
}
}

View File

@@ -1,9 +1,6 @@
import * as CommonActions from './CommonActions';
export * from '@react-navigation/routers';
export { CommonActions };
export { default as BaseRouter } from './BaseRouter';
export { default as NavigationContainer } from './NavigationContainer';
export { default as BaseNavigationContainer } from './BaseNavigationContainer';
export { default as createNavigatorFactory } from './createNavigatorFactory';
export { default as NavigationContext } from './NavigationContext';

View File

@@ -1,6 +1,6 @@
const isSerializableWithoutCircularReference = (
o: { [key: string]: any },
seen = new Set<any>()
seen: Set<any>
): boolean => {
if (
o === undefined ||
@@ -27,13 +27,13 @@ const isSerializableWithoutCircularReference = (
if (Array.isArray(o)) {
for (const it of o) {
if (!isSerializableWithoutCircularReference(it, seen)) {
if (!isSerializableWithoutCircularReference(it, new Set<any>(seen))) {
return false;
}
}
} else {
for (const key in o) {
if (!isSerializableWithoutCircularReference(o[key], seen)) {
if (!isSerializableWithoutCircularReference(o[key], new Set<any>(seen))) {
return false;
}
}
@@ -43,5 +43,5 @@ const isSerializableWithoutCircularReference = (
};
export default function isSerializable(o: { [key: string]: any }) {
return isSerializableWithoutCircularReference(o);
return isSerializableWithoutCircularReference(o, new Set<any>());
}

View File

@@ -1,105 +1,13 @@
import * as CommonActions from './CommonActions';
import * as React from 'react';
export type CommonAction = CommonActions.Action;
export type NavigationState = {
/**
* Unique key for the navigation state.
*/
key: string;
/**
* Index of the currently focused route.
*/
index: number;
/**
* List of valid route names as defined in the screen components.
*/
routeNames: string[];
/**
* Alternative entries for history.
*/
history?: unknown[];
/**
* List of rendered routes.
*/
routes: (Route<string> & {
state?: NavigationState | PartialState<NavigationState>;
})[];
/**
* Custom type for the state, whether it's for tab, stack, drawer etc.
* During rehydration, the state will be discarded if type doesn't match with router type.
* It can also be used to detect the type of the navigator we're dealing with.
*/
type: string;
/**
* Whether the navigation state has been rehydrated.
*/
stale: false;
};
export type InitialState = Partial<
Omit<NavigationState, 'stale' | 'routes'>
> & {
routes: (Omit<Route<string>, 'key'> & { state?: InitialState })[];
};
export type PartialState<State extends NavigationState> = Partial<
Omit<State, 'stale' | 'type' | 'key' | 'routes' | 'routeNames'>
> & {
stale?: true;
type?: string;
routes: (Omit<Route<string>, 'key'> & {
key?: string;
state?: InitialState;
})[];
};
export type Route<RouteName extends string> = {
/**
* Unique key for the route.
*/
key: string;
/**
* User-provided name for the route.
*/
name: RouteName;
/**
* Params for the route.
*/
params?: object;
};
export type NavigationAction = {
/**
* Type of the action (e.g. `NAVIGATE`)
*/
type: string;
/**
* Additional data for the action
*/
payload?: object;
/**
* Key of the route which dispatched this action.
*/
source?: string;
/**
* Key of the navigator which should handle this action.
*/
target?: string;
};
export type ActionCreators<Action extends NavigationAction> = {
[key: string]: (...args: any) => Action;
};
export type DefaultRouterOptions = {
/**
* Name of the route to focus by on initial render.
* If not specified, usually the first route is used.
*/
initialRouteName?: string;
};
import {
DefaultRouterOptions,
NavigationState,
NavigationAction,
InitialState,
PartialState,
Route,
ParamListBase,
} from '@react-navigation/routers';
export type DefaultNavigatorOptions<
ScreenOptions extends object
@@ -120,97 +28,6 @@ export type DefaultNavigatorOptions<
}) => ScreenOptions);
};
export type RouterFactory<
State extends NavigationState,
Action extends NavigationAction,
RouterOptions extends DefaultRouterOptions
> = (options: RouterOptions) => Router<State, Action>;
export type RouterConfigOptions = {
routeNames: string[];
routeParamList: ParamListBase;
};
export type Router<
State extends NavigationState,
Action extends NavigationAction
> = {
/**
* Type of the router. Should match the `type` property in state.
* If the type doesn't match, the state will be discarded during rehydration.
*/
type: State['type'];
/**
* Initialize the navigation state.
*
* @param options.routeNames List of valid route names as defined in the screen components.
* @param options.routeParamsList Object containing params for each route.
*/
getInitialState(options: RouterConfigOptions): State;
/**
* Rehydrate the full navigation state from a given partial state.
*
* @param partialState Navigation state to rehydrate from.
* @param options.routeNames List of valid route names as defined in the screen components.
* @param options.routeParamsList Object containing params for each route.
*/
getRehydratedState(
partialState: PartialState<State> | State,
options: RouterConfigOptions
): State;
/**
* Take the current state and updated list of route names, and return a new state.
*
* @param state State object to update.
* @param options.routeNames New list of route names.
* @param options.routeParamsList Object containing params for each route.
*/
getStateForRouteNamesChange(
state: State,
options: RouterConfigOptions
): State;
/**
* Take the current state and key of a route, and return a new state with the route focused
*
* @param state State object to apply the action on.
* @param key Key of the route to focus.
*/
getStateForRouteFocus(state: State, key: string): State;
/**
* Take the current state and action, and return a new state.
* If the action cannot be handled, return `null`.
*
* @param state State object to apply the action on.
* @param action Action object to apply.
* @param options.routeNames List of valid route names as defined in the screen components.
* @param options.routeParamsList Object containing params for each route.
*/
getStateForAction(
state: State,
action: Action,
options: RouterConfigOptions
): State | PartialState<State> | null;
/**
* Whether the action should also change focus in parent navigator
*
* @param action Action object to check.
*/
shouldActionChangeFocus(action: NavigationAction): boolean;
/**
* Action creators for the router.
*/
actionCreators?: ActionCreators<Action>;
};
export type ParamListBase = Record<string, object | undefined>;
export type EventMapBase = Record<
string,
{ data?: any; canPreventDefault?: boolean }

View File

@@ -1,4 +1,10 @@
import * as React from 'react';
import {
NavigationAction,
NavigationState,
ParamListBase,
Router,
} from '@react-navigation/routers';
import SceneView from './SceneView';
import NavigationBuilderContext, {
ChildActionListener,
@@ -7,16 +13,7 @@ import NavigationBuilderContext, {
} from './NavigationBuilderContext';
import { NavigationEventEmitter } from './useEventEmitter';
import useNavigationCache from './useNavigationCache';
import {
Descriptor,
NavigationAction,
NavigationHelpers,
NavigationState,
ParamListBase,
RouteConfig,
RouteProp,
Router,
} from './types';
import { Descriptor, NavigationHelpers, RouteConfig, RouteProp } from './types';
type Options<State extends NavigationState, ScreenOptions extends object> = {
state: State;

View File

@@ -1,5 +1,9 @@
import * as React from 'react';
import { NavigationState, NavigationAction, PartialState } from './types';
import {
NavigationState,
NavigationAction,
PartialState,
} from '@react-navigation/routers';
type State = NavigationState | PartialState<NavigationState> | undefined;

View File

@@ -1,7 +1,7 @@
import * as React from 'react';
import { NavigationState } from '@react-navigation/routers';
import NavigationContext from './NavigationContext';
import { NavigationEventEmitter } from './useEventEmitter';
import { NavigationState } from './types';
type Options = {
state: NavigationState;

View File

@@ -1,9 +1,10 @@
import * as React from 'react';
import { ParamListBase } from '@react-navigation/routers';
import NavigationBuilderContext, {
FocusedNavigationCallback,
FocusedNavigationListener,
} from './NavigationBuilderContext';
import { NavigationHelpers, ParamListBase } from './types';
import { NavigationHelpers } from './types';
type Options = {
navigation: NavigationHelpers<ParamListBase>;

View File

@@ -1,6 +1,7 @@
import * as React from 'react';
import { ParamListBase } from '@react-navigation/routers';
import NavigationContext from './NavigationContext';
import { NavigationProp, ParamListBase } from './types';
import { NavigationProp } from './types';
/**
* Hook to access the navigation prop of the parent screen anywhere.
@@ -14,7 +15,7 @@ export default function useNavigation<
if (navigation === undefined) {
throw new Error(
"We couldn't find a navigation object. Is your component inside a navigator?"
"We couldn't find a navigation object. Is your component inside a screen in a navigator?"
);
}

View File

@@ -1,9 +1,18 @@
import * as React from 'react';
import { isValidElementType } from 'react-is';
import { NavigationStateContext } from './NavigationContainer';
import {
CommonActions,
DefaultRouterOptions,
NavigationState,
ParamListBase,
Router,
RouterFactory,
PartialState,
NavigationAction,
} from '@react-navigation/routers';
import { NavigationStateContext } from './BaseNavigationContainer';
import NavigationRouteContext from './NavigationRouteContext';
import Screen from './Screen';
import { navigate } from './CommonActions';
import useEventEmitter from './useEventEmitter';
import useRegisterNavigator from './useRegisterNavigator';
import useDescriptors from './useDescriptors';
@@ -15,16 +24,9 @@ import useChildActionListeners from './useChildActionListeners';
import useFocusedListeners from './useFocusedListeners';
import useFocusedListenersChildrenAdapter from './useFocusedListenersChildrenAdapter';
import {
DefaultRouterOptions,
DefaultNavigatorOptions,
NavigationState,
ParamListBase,
RouteConfig,
Router,
RouterFactory,
PartialState,
PrivateValueStore,
NavigationAction,
} from './types';
import useStateGetters from './useStateGetters';
import useOnGetState from './useOnGetState';
@@ -34,6 +36,7 @@ import useOnGetState from './useOnGetState';
PrivateValueStore;
type NavigatorRoute = {
key: string;
params?: {
screen?: string;
params?: object;
@@ -152,7 +155,7 @@ export default function useNavigationBuilder<
createRouter: RouterFactory<State, any, RouterOptions>,
options: DefaultNavigatorOptions<ScreenOptions> & RouterOptions
) {
useRegisterNavigator();
const navigatorKey = useRegisterNavigator();
const route = React.useContext(NavigationRouteContext) as
| NavigatorRoute
@@ -230,7 +233,8 @@ export default function useNavigationBuilder<
state: currentState,
getState: getCurrentState,
setState,
key,
setKey,
getKey,
performTransaction,
} = React.useContext(NavigationStateContext);
@@ -292,7 +296,7 @@ export default function useNavigationBuilder<
// The update should be limited to current navigator only, so we call the router manually
const updatedState = router.getStateForAction(
state,
navigate(route.params.screen, route.params.params),
CommonActions.navigate(route.params.screen, route.params.params),
{
routeNames,
routeParamList,
@@ -323,11 +327,15 @@ export default function useNavigationBuilder<
state = nextState;
React.useEffect(() => {
setKey(navigatorKey);
return () => {
// We need to clean up state for this navigator on unmount
performTransaction(
() => getCurrentState() !== undefined && setState(undefined)
);
performTransaction(() => {
if (getCurrentState() !== undefined && getKey() === navigatorKey) {
setState(undefined);
}
});
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@@ -364,7 +372,7 @@ export default function useNavigationBuilder<
router,
getState,
setState,
key,
key: route?.key,
listeners: actionListeners,
routerConfigOptions: {
routeNames,
@@ -374,7 +382,7 @@ export default function useNavigationBuilder<
const onRouteFocus = useOnRouteFocus({
router,
key,
key: route?.key,
getState,
setState,
});

View File

@@ -1,16 +1,15 @@
import * as React from 'react';
import * as CommonActions from './CommonActions';
import { NavigationEventEmitter } from './useEventEmitter';
import NavigationContext from './NavigationContext';
import {
CommonActions,
NavigationAction,
NavigationHelpers,
NavigationProp,
ParamListBase,
NavigationState,
Router,
} from './types';
} from '@react-navigation/routers';
import { NavigationEventEmitter } from './useEventEmitter';
import NavigationContext from './NavigationContext';
import { NavigationHelpers, NavigationProp } from './types';
type Options<State extends NavigationState> = {
state: State;

View File

@@ -1,17 +1,15 @@
import * as React from 'react';
import * as CommonActions from './CommonActions';
import NavigationContext from './NavigationContext';
import { NavigationStateContext } from './NavigationContainer';
import { NavigationEventEmitter } from './useEventEmitter';
import {
NavigationHelpers,
NavigationProp,
CommonActions,
NavigationAction,
NavigationState,
ParamListBase,
Router,
PrivateValueStore,
} from './types';
} from '@react-navigation/routers';
import NavigationContext from './NavigationContext';
import { NavigationStateContext } from './BaseNavigationContainer';
import { NavigationEventEmitter } from './useEventEmitter';
import { NavigationHelpers, NavigationProp, PrivateValueStore } from './types';
// This is to make TypeScript compiler happy
// eslint-disable-next-line babel/no-unused-expressions
@@ -51,7 +49,7 @@ export default function useNavigationHelpers<
console.error(
`The action '${payload.type}' with payload '${JSON.stringify(
payload.payload
)}' was not handled by any navigator.`
)}' was not handled by any navigator. If you are trying to navigate to a screen, check if the screen exists in your navigator. If you're trying to navigate to a screen in a nested navigator, see https://reactnavigation.org/docs/en/nesting-navigators.html#navigating-to-a-screen-in-a-nested-navigator.`
);
}
});

View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import { NavigationState } from '@react-navigation/routers';
import useNavigation from './useNavigation';
import { NavigationState } from './types';
type Selector<T> = (state: NavigationState) => T;

View File

@@ -1,14 +1,14 @@
import * as React from 'react';
import NavigationBuilderContext, {
ChildActionListener,
} from './NavigationBuilderContext';
import {
NavigationAction,
NavigationState,
PartialState,
Router,
RouterConfigOptions,
} from './types';
} from '@react-navigation/routers';
import NavigationBuilderContext, {
ChildActionListener,
} from './NavigationBuilderContext';
type Options = {
router: Router<NavigationState, NavigationAction>;

View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import { NavigationState } from '@react-navigation/routers';
import NavigationBuilderContext from './NavigationBuilderContext';
import { NavigationState } from './types';
import NavigationRouteContext from './NavigationRouteContext';
export default function useOnGetState({

View File

@@ -1,6 +1,10 @@
import * as React from 'react';
import {
NavigationAction,
NavigationState,
Router,
} from '@react-navigation/routers';
import NavigationBuilderContext from './NavigationBuilderContext';
import { NavigationAction, NavigationState, Router } from './types';
type Options<Action extends NavigationAction> = {
router: Router<NavigationState, Action>;

View File

@@ -23,4 +23,6 @@ export default function useRegisterNavigator() {
return () => unregister(key);
}, [container, key]);
return key;
}

View File

@@ -1,6 +1,7 @@
import * as React from 'react';
import { ParamListBase } from '@react-navigation/routers';
import NavigationRouteContext from './NavigationRouteContext';
import { ParamListBase, RouteProp } from './types';
import { RouteProp } from './types';
/**
* Hook to access the route prop of the parent screen anywhere.
@@ -14,7 +15,7 @@ export default function useRoute<
if (route === undefined) {
throw new Error(
"We couldn't find a route object. Is your component inside a navigator?"
"We couldn't find a route object. Is your component inside a screen in a navigator?"
);
}

View File

@@ -1,5 +1,8 @@
{
"extends": "../../tsconfig",
"references": [
{ "path": "../routers" }
],
"compilerOptions": {
"outDir": "./lib/typescript"
}

View File

@@ -3,6 +3,55 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.0.3](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.0.2...@react-navigation/drawer@5.0.3) (2020-02-12)
**Note:** Version bump only for package @react-navigation/drawer
## [5.0.2](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.0.1...@react-navigation/drawer@5.0.2) (2020-02-11)
### Bug Fixes
* remove unnecessary borderless from drawer item ([031136f](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/031136f7c86eff3c9139d1baa243da9f19bc61d4)), closes [#6801](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/6801)
## [5.0.1](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.0.0-alpha.47...@react-navigation/drawer@5.0.1) (2020-02-10)
### Bug Fixes
* prevent ripple from bleeding out of drawer item ([688d16d](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/688d16de5d9f4c5116b92e3ebf3029b56a659d7a)), closes [#6801](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/6801)
# [5.0.0-alpha.47](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/compare/@react-navigation/drawer@5.0.0-alpha.46...@react-navigation/drawer@5.0.0-alpha.47) (2020-02-04)
**Note:** Version bump only for package @react-navigation/drawer
# [5.0.0-alpha.46](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/compare/@react-navigation/drawer@5.0.0-alpha.45...@react-navigation/drawer@5.0.0-alpha.46) (2020-02-04)
### Features
* disable pan gesture by default in the browser for Apple devices ([b277927](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/commit/b2779279251b1f157ba825cc34e39046b44f00d8)), closes [#287](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/issues/287)
# [5.0.0-alpha.45](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/compare/@react-navigation/drawer@5.0.0-alpha.44...@react-navigation/drawer@5.0.0-alpha.45) (2020-02-03)
**Note:** Version bump only for package @react-navigation/drawer

View File

@@ -2,60 +2,4 @@
Bottom tab navigator for React Navigation following iOS design guidelines.
Documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/en/next/drawer-navigator.html).
## Installation
Open a Terminal in your project's folder and run,
```sh
yarn add @react-navigation/native @react-navigation/drawer
```
Now we need to install [`react-native-gesture-handler`](https://github.com/kmagiera/react-native-gesture-handler), [`react-native-reanimated`](https://github.com/kmagiera/react-native-reanimated) and [`react-native-safe-area-context`](https://github.com/th3rdwave/react-native-safe-area-context).
If you are using Expo, to ensure that you get the compatible versions of the libraries, run:
```sh
expo install react-native-gesture-handler react-native-reanimated react-native-safe-area-context
```
If you are not using Expo, run the following:
```sh
yarn add react-native-reanimated react-native-gesture-handler react-native-safe-area-context
```
If you are using Expo, you are done. Otherwise, continue to the next steps.
To complete the linking on iOS, make sure you have [Cocoapods](https://cocoapods.org/) installed. Then run:
```sh
cd ios
pod install
cd ..
```
**IMPORTANT:** There are additional steps required for `react-native-gesture-handler` on Android after linking (for all React Native versions). Check the [this guide](https://kmagiera.github.io/react-native-gesture-handler/docs/getting-started.html) to complete the installation.
## Usage
```js
import { createDrawerNavigator } from '@react-navigation/drawer';
const Drawer = createDrawerNavigator();
export default function App() {
return (
<Drawer.Navigator>
<Drawer.Screen name="home" component={Home} options={{ title: 'Home' }} />
<Drawer.Screen name="feed" component={Feed} options={{ title: 'Feed' }} />
<Drawer.Screen
name="profile"
component={Profile}
options={{ title: 'Profile' }}
/>
</Drawer.Navigator>
);
}
```
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/drawer-navigator.html).

View File

@@ -1,6 +1,7 @@
{
"name": "@react-navigation/drawer",
"description": "Drawer navigator component with animated transitions and gesturess",
"version": "5.0.3",
"keywords": [
"react-native-component",
"react-component",
@@ -11,9 +12,12 @@
"material",
"drawer"
],
"version": "5.0.0-alpha.45",
"license": "MIT",
"repository": "https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer",
"repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/drawer",
"bugs": {
"url": "https://github.com/react-navigation/react-navigation/issues"
},
"homepage": "https://reactnavigation.org/docs/drawer-navigator.html",
"main": "lib/commonjs/index.js",
"react-native": "src/index.tsx",
"module": "lib/module/index.js",
@@ -31,25 +35,25 @@
"clean": "del lib"
},
"dependencies": {
"@react-navigation/routers": "^5.0.0-alpha.31",
"color": "^3.1.2",
"react-native-iphone-x-helper": "^1.2.1"
},
"devDependencies": {
"@react-native-community/bob": "^0.8.0",
"@types/react": "^16.9.17",
"@react-native-community/bob": "^0.9.3",
"@react-navigation/native": "^5.0.3",
"@types/react": "^16.9.19",
"@types/react-native": "^0.60.30",
"del-cli": "^3.0.0",
"react": "~16.9.0",
"react-native": "~0.61.5",
"react-native-gesture-handler": "^1.5.5",
"react-native-reanimated": "^1.4.0",
"react-native-safe-area-context": "^0.6.2",
"react-native-screens": "^2.0.0-alpha.33",
"typescript": "^3.7.4"
"react-native-gesture-handler": "^1.5.6",
"react-native-reanimated": "^1.7.0",
"react-native-safe-area-context": "^0.7.2",
"react-native-screens": "^2.0.0-beta.2",
"typescript": "^3.7.5"
},
"peerDependencies": {
"@react-navigation/native": "^5.0.0-alpha.0",
"@react-navigation/native": "^5.0.0",
"react": "*",
"react-native": "*",
"react-native-gesture-handler": "^1.0.0",

View File

@@ -3,12 +3,10 @@ import {
createNavigatorFactory,
useNavigationBuilder,
DefaultNavigatorOptions,
} from '@react-navigation/native';
import {
DrawerNavigationState,
DrawerRouterOptions,
DrawerRouter,
} from '@react-navigation/routers';
} from '@react-navigation/native';
import DrawerView from '../views/DrawerView';
import {

View File

@@ -6,8 +6,8 @@ import {
NavigationProp,
Descriptor,
NavigationHelpers,
DrawerNavigationState,
} from '@react-navigation/native';
import { DrawerNavigationState } from '@react-navigation/routers';
import { PanGestureHandler } from 'react-native-gesture-handler';
export type Scene = {

View File

@@ -94,11 +94,30 @@ type Props = {
gestureHandlerProps?: React.ComponentProps<typeof PanGestureHandler>;
};
/**
* Disables the pan gesture by default on Apple devices in the browser.
* https://stackoverflow.com/a/9039885
*/
function shouldEnableGesture(): boolean {
if (
Platform.OS === 'web' &&
typeof navigator !== 'undefined' &&
typeof window !== 'undefined'
) {
const isWebAppleDevice =
/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
return !isWebAppleDevice;
}
return true;
}
export default class DrawerView extends React.PureComponent<Props> {
static defaultProps = {
drawerPostion: I18nManager.isRTL ? 'left' : 'right',
drawerType: 'front',
gestureEnabled: true,
gestureEnabled: shouldEnableGesture(),
swipeEdgeWidth: 32,
swipeVelocityThreshold: 500,
keyboardDismissMode: 'on-drag',
@@ -582,7 +601,10 @@ export default class DrawerView extends React.PureComponent<Props> {
>
{renderSceneContent({ progress: this.progress })}
</View>
<TapGestureHandler onHandlerStateChange={this.handleTapStateChange}>
<TapGestureHandler
enabled={gestureEnabled}
onHandlerStateChange={this.handleTapStateChange}
>
<Overlay progress={this.progress} style={overlayStyle} />
</TapGestureHandler>
</Animated.View>

View File

@@ -101,7 +101,6 @@ export default function DrawerItem(props: Props) {
style={[styles.container, { borderRadius, backgroundColor }, style]}
>
<TouchableItem
borderless
delayPressIn={0}
onPress={onPress}
style={[styles.wrapper, { borderRadius }]}
@@ -145,6 +144,7 @@ const styles = StyleSheet.create({
container: {
marginHorizontal: 10,
marginVertical: 4,
overflow: 'hidden',
},
wrapper: {
flexDirection: 'row',

View File

@@ -1,9 +1,9 @@
import * as React from 'react';
import { CommonActions } from '@react-navigation/native';
import {
CommonActions,
DrawerActions,
DrawerNavigationState,
} from '@react-navigation/routers';
} from '@react-navigation/native';
import DrawerItem from './DrawerItem';
import {
DrawerNavigationHelpers,

View File

@@ -18,8 +18,8 @@ import {
import {
DrawerNavigationState,
DrawerActions,
} from '@react-navigation/routers';
import { useTheme } from '@react-navigation/native';
useTheme,
} from '@react-navigation/native';
import DrawerGestureContext from '../utils/DrawerGestureContext';
import SafeAreaProviderCompat from './SafeAreaProviderCompat';
@@ -205,7 +205,7 @@ export default function DrawerView({
<DrawerGestureContext.Provider value={drawerGestureRef}>
<Drawer
open={isDrawerOpen}
gestureEnabled={gestureEnabled !== false}
gestureEnabled={gestureEnabled}
onOpen={handleDrawerOpen}
onClose={handleDrawerClose}
onGestureRef={ref => {

View File

@@ -2,17 +2,25 @@ import * as React from 'react';
import {
SafeAreaProvider,
SafeAreaConsumer,
initialWindowSafeAreaInsets,
} from 'react-native-safe-area-context';
import {
getStatusBarHeight,
getBottomSpace,
} from 'react-native-iphone-x-helper';
// The provider component for safe area initializes asynchornously
// Until the insets are available, there'll be blank screen
// To avoid the blank screen, we specify some initial values
const initialSafeAreaInsets = {
// Approximate values which are good enough for most cases
top: getStatusBarHeight(true),
bottom: getBottomSpace(),
right: 0,
left: 0,
// If we are on a newer version of the library, we can get the correct window insets
// The component might not be filling the window, but this is good enough for most cases
...initialWindowSafeAreaInsets,
};
type Props = {
@@ -26,7 +34,7 @@ export default function SafeAreaProviderCompat({ children }: Props) {
if (insets) {
// If we already have insets, don't wrap the stack in another safe area provider
// This avoids an issue with updates at the cost of potentially incorrect values
// https://github.com/react-navigation/navigation-ex/issues/174
// https://github.com/react-navigation/react-navigation/issues/174
return children;
}

View File

@@ -2,8 +2,8 @@
"extends": "../../tsconfig",
"references": [
{ "path": "../core" },
{ "path": "../native" },
{ "path": "../routers" }
{ "path": "../routers" },
{ "path": "../native" }
],
"compilerOptions": {
"outDir": "./lib/typescript"

View File

@@ -3,6 +3,46 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.0.3](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.0.2...@react-navigation/material-bottom-tabs@5.0.3) (2020-02-12)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
## [5.0.2](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.0.1...@react-navigation/material-bottom-tabs@5.0.2) (2020-02-11)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
## [5.0.1](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.42...@react-navigation/material-bottom-tabs@5.0.1) (2020-02-10)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
# [5.0.0-alpha.42](https://github.com/react-navigation/navigation-ex/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.41...@react-navigation/material-bottom-tabs@5.0.0-alpha.42) (2020-02-04)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
# [5.0.0-alpha.41](https://github.com/react-navigation/navigation-ex/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.40...@react-navigation/material-bottom-tabs@5.0.0-alpha.41) (2020-02-04)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
# [5.0.0-alpha.40](https://github.com/react-navigation/navigation-ex/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.39...@react-navigation/material-bottom-tabs@5.0.0-alpha.40) (2020-02-03)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs

View File

@@ -2,61 +2,4 @@
React Navigation integration for [bottom navigation](https://material.io/design/components/bottom-navigation.html) component from [`react-native-paper`](https://callstack.github.io/react-native-paper/bottom-navigation.html).
Documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/en/next/material-bottom-tab-navigator.html).
## Installation
Open a Terminal in your project's folder and run,
```sh
yarn add @react-navigation/native @react-navigation/material-bottom-tabs
```
Setup `react-native-paper` following the [Getting Started guide](https://callstack.github.io/react-native-paper/getting-started.html).
## Usage
```js
import { createMaterialBottomTabNavigator } from '@react-navigation/material-bottom-tabs';
const MaterialBottomTabs = createMaterialBottomTabNavigator();
export default function App() {
return (
<MaterialBottomTabs.Navigator>
<MaterialBottomTabs.Screen
name="article"
component={Article}
options={{
tabBarLabel: 'Article',
tabBarIcon: 'file-document-box',
}}
/>
<MaterialBottomTabs.Screen
name="chat"
component={Chat}
options={{
tabBarLabel: 'Chat',
tabBarIcon: 'message-reply',
}}
/>
<MaterialBottomTabs.Screen
name="contacts"
component={Contacts}
options={{
tabBarLabel: 'Contacts',
tabBarIcon: 'contacts',
}}
/>
<MaterialBottomTabs.Screen
name="albums"
component={Albums}
options={{
tabBarLabel: 'Albums',
tabBarIcon: 'image-album',
}}
/>
</MaterialBottomTabs.Navigator>
);
}
```
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/material-bottom-tab-navigator.html).

View File

@@ -1,6 +1,7 @@
{
"name": "@react-navigation/material-bottom-tabs",
"description": "Integration for bottom navigation component from react-native-paper",
"version": "5.0.3",
"keywords": [
"react-native-component",
"react-component",
@@ -11,9 +12,12 @@
"material",
"tab"
],
"version": "5.0.0-alpha.40",
"license": "MIT",
"repository": "https://github.com/react-navigation/navigation-ex/tree/master/packages/material-bottom-tabs",
"repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs",
"bugs": {
"url": "https://github.com/react-navigation/react-navigation/issues"
},
"homepage": "https://reactnavigation.org/docs/material-bottom-tab-navigator.html",
"main": "lib/commonjs/index.js",
"react-native": "src/index.tsx",
"module": "lib/module/index.js",
@@ -30,23 +34,21 @@
"prepare": "bob build",
"clean": "del lib"
},
"dependencies": {
"@react-navigation/routers": "^5.0.0-alpha.31"
},
"devDependencies": {
"@react-native-community/bob": "^0.8.0",
"@types/react": "^16.9.17",
"@react-native-community/bob": "^0.9.3",
"@react-navigation/native": "^5.0.3",
"@types/react": "^16.9.19",
"@types/react-native": "^0.60.30",
"@types/react-native-vector-icons": "^6.4.5",
"del-cli": "^3.0.0",
"react": "~16.9.0",
"react-native": "~0.61.5",
"react-native-paper": "^3.5.0",
"react-native-paper": "^3.6.0",
"react-native-vector-icons": "^6.6.0",
"typescript": "^3.7.4"
"typescript": "^3.7.5"
},
"peerDependencies": {
"@react-navigation/native": "^5.0.0-alpha.0",
"@react-navigation/native": "^5.0.0",
"react": "*",
"react-native": "*",
"react-native-paper": "^3.5.0",

View File

@@ -3,12 +3,10 @@ import {
useNavigationBuilder,
createNavigatorFactory,
DefaultNavigatorOptions,
} from '@react-navigation/native';
import {
TabRouter,
TabRouterOptions,
TabNavigationState,
} from '@react-navigation/routers';
} from '@react-navigation/native';
import MaterialBottomTabView from '../views/MaterialBottomTabView';
import {

View File

@@ -4,8 +4,8 @@ import {
Descriptor,
NavigationProp,
NavigationHelpers,
TabNavigationState,
} from '@react-navigation/native';
import { TabNavigationState } from '@react-navigation/routers';
export type MaterialBottomTabNavigationEventMap = {
/**

Some files were not shown because too many files have changed in this diff Show More