├── .editorconfig
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ ├── main.yml
│ └── size.yml
├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── example
├── .env
├── .gitignore
├── .prettierrc
├── README.md
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── src
│ ├── App.tsx
│ ├── UserForm.tsx
│ ├── firebaseConfig.ts
│ ├── index.tsx
│ ├── react-app-env.d.ts
│ ├── reportWebVitals.ts
│ └── setupTests.ts
├── tsconfig.json
└── yarn.lock
├── package.json
├── rollup.config.js
├── src
├── getErrorMessageForProvider.ts
├── index.tsx
├── setupTests.ts
└── test.tsx
├── tsconfig.json
├── tsconfig.test.json
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on: [push]
3 | jobs:
4 | build:
5 | name: Build, lint, test and release on Node 16
6 | runs-on: ubuntu-latest
7 | steps:
8 | - name: Checkout repo
9 | uses: actions/checkout@v2
10 |
11 | - name: Use Node 16
12 | uses: actions/setup-node@v1
13 | with:
14 | node-version: 16
15 |
16 | - name: Install deps and build (with cache)
17 | uses: bahmutov/npm-install@v1
18 |
19 | - name: Lint
20 | run: yarn lint
21 |
22 | - name: Test
23 | run: yarn test --ci --coverage --maxWorkers=2
24 |
25 | - name: Coverage
26 | run: npx codecov -f coverage/*.json
27 |
28 | - name: Pack Inspect
29 | run: yarn pack:inspect
30 |
31 | - name: Release
32 | env:
33 | GITHUB_TOKEN: ${{ secrets.GH_PAT }}
34 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
35 | GH_AUTHOR_NAME: github-actions
36 | GH_AUTHOR_EMAIL: actions@github.com
37 | run: yarn semantic-release
38 |
39 | - name: Build Example
40 | if: ${{ github.ref == 'refs/heads/master' }}
41 | env:
42 | PUBLIC_URL: https://armand1m.github.io/react-with-firebase-auth/
43 | run: yarn build:example
44 |
45 | - name: Deploy Example
46 | uses: peaceiris/actions-gh-pages@v3
47 | if: ${{ github.ref == 'refs/heads/master' }}
48 | with:
49 | github_token: ${{ secrets.GITHUB_TOKEN }}
50 | publish_dir: ./example/build
51 |
--------------------------------------------------------------------------------
/.github/workflows/size.yml:
--------------------------------------------------------------------------------
1 | name: size
2 | on: [pull_request]
3 | jobs:
4 | size:
5 | runs-on: ubuntu-latest
6 | env:
7 | CI_JOB_NUMBER: 1
8 | steps:
9 | - uses: actions/checkout@v1
10 | - uses: andresz1/size-limit-action@v1
11 | with:
12 | github_token: ${{ secrets.GITHUB_TOKEN }}
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # See https://help.github.com/ignore-files/ for more about ignoring files.
3 |
4 | # dependencies
5 | node_modules
6 |
7 | # builds
8 | build
9 | dist
10 |
11 | # misc
12 | .DS_Store
13 | ./.env
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
23 | .rpt*
24 | coverage
25 |
26 | *.tgz
27 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [1.4.1](https://github.com/armand1m/react-with-firebase-auth/compare/v1.4.0...v1.4.1) (2022-04-05)
2 |
3 |
4 | ### Bug Fixes
5 |
6 | * **google-auth:** fix google auth provider instance type ([a2a3ca8](https://github.com/armand1m/react-with-firebase-auth/commit/a2a3ca8effcd72d100062ce81ffc8195fbfa33b9))
7 |
8 | # [1.4.0](https://github.com/armand1m/react-with-firebase-auth/compare/v1.3.11...v1.4.0) (2022-02-20)
9 |
10 |
11 | ### Features
12 |
13 | * 🎸 include signInWithApple provider ([#131](https://github.com/armand1m/react-with-firebase-auth/issues/131)) ([bac510f](https://github.com/armand1m/react-with-firebase-auth/commit/bac510f6f2cac58aabb1b85f7e73a5355b9185a2))
14 |
15 | ## [1.3.11](https://github.com/armand1m/react-with-firebase-auth/compare/v1.3.10...v1.3.11) (2021-07-04)
16 |
17 |
18 | ### Bug Fixes
19 |
20 | * semantic-release plugins order ([285b02c](https://github.com/armand1m/react-with-firebase-auth/commit/285b02ce4702fade5ad377d69e07067bde9edd1b))
21 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at armando.mag95@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Armando Magalhães
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-with-firebase-auth
2 |
3 | [](https://www.npmjs.com/package/react-with-firebase-auth) [](https://standardjs.com)
4 | [](https://travis-ci.org/armand1m/react-with-firebase-auth)
5 | [](https://codecov.io/gh/armand1m/react-with-firebase-auth)
6 | [](https://bundlephobia.com/result?p=react-with-firebase-auth)
7 | [](https://bundlephobia.com/result?p=react-with-firebase-auth)
8 | [](https://david-dm.org/armand1m/react-with-firebase-auth?type=dev)
9 | [](https://david-dm.org/armand1m/react-with-firebase-auth?type=peer) [](https://greenkeeper.io/)
10 |
11 | > Higher Order Component for integrating Firebase with a React Component
12 |
13 | This library makes a `withFirebaseAuth()` function available to you.
14 |
15 | - [Signature](#signature)
16 | - [Usage](#usage)
17 | - [Examples](#examples)
18 | - [Articles](#articles)
19 | - [License](#license)
20 |
21 | ## Signature
22 |
23 | ```ts
24 | type FirebaseAuthProps = {
25 | firebaseAppAuth: firebase.auth.Auth,
26 | providers?: {
27 | googleProvider?: firebase.auth.GoogleAuthProvider_Instance;
28 | facebookProvider?: firebase.auth.FacebookAuthProvider_Instance;
29 | twitterProvider?: firebase.auth.TwitterAuthProvider_Instance;
30 | githubProvider?: firebase.auth.GithubAuthProvider_Instance;
31 | }
32 | };
33 |
34 | withFirebaseAuth
(authProps: FirebaseAuthProps) =>
35 | createComponentWithAuth(WrappedComponent: React.ComponentType
) =>
36 | React.ComponentType
37 | ```
38 |
39 | ## Props Provided
40 |
41 | ```ts
42 | type WrappedComponentProps = {
43 | signInWithEmailAndPassword: (email: string, password: string) => void;
44 | createUserWithEmailAndPassword: (email: string, password: string) => void;
45 | signInWithGoogle: () => void;
46 | signInWithFacebook: () => void;
47 | signInWithGithub: () => void;
48 | signInWithTwitter: () => void;
49 | signInWithPhoneNumber: (
50 | phoneNumber: string,
51 | applicationVerifier: firebase.auth.ApplicationVerifier,
52 | ) => void;
53 | signInAnonymously: () => void;
54 | signOut: () => void;
55 | setError: (error: string) => void;
56 | user?: firebase.User | null;
57 | error?: string;
58 | loading: boolean;
59 | };
60 | ```
61 |
62 | ## Usage
63 |
64 | Install it:
65 |
66 | ```bash
67 | npm install --save react-with-firebase-auth
68 | ```
69 |
70 | Then use it in your components:
71 |
72 | ```tsx
73 | import * as React from 'react';
74 | import * as firebase from 'firebase/app';
75 | import 'firebase/auth';
76 |
77 | import withFirebaseAuth, { WrappedComponentProps } from 'react-with-firebase-auth';
78 |
79 | import firebaseConfig from './firebaseConfig';
80 |
81 | const firebaseApp = firebase.initializeApp(firebaseConfig);
82 |
83 | const firebaseAppAuth = firebaseApp.auth();
84 |
85 | /** See the signature above to find out the available providers */
86 | const providers = {
87 | googleProvider: new firebase.auth.GoogleAuthProvider(),
88 | };
89 |
90 | /** providers can be customised as per the Firebase documentation on auth providers **/
91 | providers.googleProvider.setCustomParameters({
92 | hd: 'mycompany.com',
93 | });
94 |
95 | /** Create the FirebaseAuth component wrapper */
96 | const createComponentWithAuth = withFirebaseAuth({
97 | providers,
98 | firebaseAppAuth,
99 | });
100 |
101 | const App = ({
102 | /** These props are provided by withFirebaseAuth HOC */
103 | signInWithEmailAndPassword,
104 | createUserWithEmailAndPassword,
105 | signInWithGoogle,
106 | signInWithFacebook,
107 | signInWithGithub,
108 | signInWithTwitter,
109 | signInAnonymously,
110 | signOut,
111 | setError,
112 | user,
113 | error,
114 | loading,
115 | }: WrappedComponentProps) => (
116 |
117 | {
118 | user
119 | ? Hello, {user.displayName}
120 | : Log in
121 | }
122 |
123 | {
124 | user
125 | ? Sign out
126 | : Sign in with Google
127 | }
128 |
129 | {
130 | loading && Loading..
131 | }
132 |
133 | );
134 |
135 | /** Wrap it */
136 | export default createComponentWithAuth(App);
137 | ```
138 |
139 | ## Examples
140 |
141 | There are a few source code examples available:
142 |
143 | - Create React App Javascript Example: [armand1m/react-with-firebase-auth/tree/master/example](https://github.com/armand1m/react-with-firebase-auth/tree/master/example)
144 | - Create React App Medium Example: [armand1m/react-firebase-authentication-medium](https://github.com/armand1m/react-firebase-authentication-medium)
145 |
146 | You can also check a live demo example here:
147 |
148 | - https://armand1m.github.io/react-with-firebase-auth/
149 |
150 | ## Articles
151 |
152 | - ["How to setup Firebase Authentication with React in 5 minutes (maybe 10)"](https://medium.com/firebase-developers/how-to-setup-firebase-authentication-with-react-in-5-minutes-maybe-10-bb8bb53e8834)
153 |
154 | ## Stargazers
155 |
156 | [](https://starchart.cc/armand1m/react-with-firebase-auth)
157 |
158 | ## License
159 |
160 | MIT © [Armando Magalhaes](https://github.com/armand1m)
161 |
--------------------------------------------------------------------------------
/example/.env:
--------------------------------------------------------------------------------
1 | SKIP_PREFLIGHT_CHECK=true
2 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/example/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "jsxBracketSameLine": true,
3 | "printWidth": 70,
4 | "singleQuote": true,
5 | "trailingComma": "es5"
6 | }
7 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `yarn start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
13 |
14 | The page will reload if you make edits.\
15 | You will also see any lint errors in the console.
16 |
17 | ### `yarn test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `yarn build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `yarn eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
35 |
36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
39 |
40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-with-firebase-auth-example-ts",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "firebase": "^8.7.0",
14 | "react": "^17.0.2",
15 | "react-dom": "^17.0.2",
16 | "react-scripts": "4.0.3",
17 | "react-with-firebase-auth": "file:..",
18 | "typescript": "^4.1.2",
19 | "web-vitals": "^1.0.1"
20 | },
21 | "scripts": {
22 | "start": "react-scripts start",
23 | "build": "react-scripts build",
24 | "test": "react-scripts test",
25 | "eject": "react-scripts eject",
26 | "lint": "prettier --check './src/**/*.{tsx,ts}'",
27 | "lint:fix": "prettier --write './src/**/*.{tsx,ts}'"
28 | },
29 | "eslintConfig": {
30 | "extends": [
31 | "react-app",
32 | "react-app/jest"
33 | ]
34 | },
35 | "browserslist": {
36 | "production": [
37 | ">0.2%",
38 | "not dead",
39 | "not op_mini all"
40 | ],
41 | "development": [
42 | "last 1 chrome version",
43 | "last 1 firefox version",
44 | "last 1 safari version"
45 | ]
46 | },
47 | "devDependencies": {
48 | "@types/firebase": "^3.2.1",
49 | "prettier": "^2.3.2"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/example/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/armand1m/react-with-firebase-auth/bd3692139f1e136653b3bd5a6008de7a7e969b9f/example/public/favicon.ico
--------------------------------------------------------------------------------
/example/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | react-with-firebase-auth
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/example/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/armand1m/react-with-firebase-auth/bd3692139f1e136653b3bd5a6008de7a7e969b9f/example/public/logo192.png
--------------------------------------------------------------------------------
/example/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/armand1m/react-with-firebase-auth/bd3692139f1e136653b3bd5a6008de7a7e969b9f/example/public/logo512.png
--------------------------------------------------------------------------------
/example/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "react-with-firebase-auth",
3 | "name": "react-with-firebase-auth",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/example/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/example/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import firebase from 'firebase';
3 | import withFirebaseAuth, {
4 | WrappedComponentProps,
5 | } from 'react-with-firebase-auth';
6 | import firebaseConfig from './firebaseConfig';
7 | import UserForm from './UserForm';
8 |
9 | const firebaseApp = firebase.initializeApp(firebaseConfig);
10 |
11 | const FormWrapper: React.FC = ({ children }) => (
12 | <>
13 | {children}
14 |
15 | >
16 | );
17 |
18 | const Loading = () => (
19 |
31 | Loading..
32 |
33 | );
34 |
35 | const App: React.FC = ({
36 | user,
37 | error,
38 | loading,
39 | setError,
40 | signOut,
41 | signInAnonymously,
42 | signInWithEmailAndPassword,
43 | signInWithGoogle,
44 | signInWithGithub,
45 | signInWithApple,
46 | createUserWithEmailAndPassword,
47 | }) => (
48 |
49 | {loading && }
50 |
51 |
52 | react-with-firebase-auth
53 | a very simple demo showing it in action
54 | see user data and errors in the end of this page
55 |
56 |
57 |
58 | create user
59 |
60 |
61 |
62 |
63 | sign in
64 |
65 |
66 |
67 |
68 | sign in with google
69 | sign in with google
70 |
71 |
72 |
73 | sign in with apple
74 | sign in with apple
75 |
76 |
77 |
78 | sign in with github
79 | (no provider setup, good to see error message)
80 | sign in with github
81 |
82 |
83 |
84 | sign in anonymously
85 | (failing due to permissions, good to see error message)
86 | sign in anonymously
87 |
88 |
89 |
90 | sign out
91 | sign out
92 |
93 |
94 |
95 | clear error
96 | setError(null)}>clear error
97 |
98 |
99 |
100 | user data
101 |
105 |
106 |
107 |
108 | error data
109 |
110 |
111 |
112 | );
113 |
114 | const firebaseAppAuth = firebaseApp.auth();
115 |
116 | const providers = {
117 | googleProvider: new firebase.auth.GoogleAuthProvider(),
118 | appleProvider: new firebase.auth.OAuthProvider('apple.com'),
119 | };
120 |
121 | export default withFirebaseAuth({
122 | providers,
123 | firebaseAppAuth,
124 | })(App);
125 |
--------------------------------------------------------------------------------
/example/src/UserForm.tsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | ButtonHTMLAttributes,
3 | DetailedHTMLProps,
4 | InputHTMLAttributes,
5 | useState,
6 | } from 'react';
7 |
8 | const Field: React.FC = ({ children }) => (
9 | <>
10 | {children}
11 |
12 | >
13 | );
14 |
15 | type HTMLInputProps = DetailedHTMLProps<
16 | InputHTMLAttributes,
17 | HTMLInputElement
18 | >;
19 |
20 | type InputProps = Omit & {
21 | onChange: (value: string) => void;
22 | };
23 |
24 | const Input = ({ value, onChange, ...props }: InputProps) => (
25 | onChange(event.target.value)}
29 | />
30 | );
31 |
32 | type ButtonProps = DetailedHTMLProps<
33 | ButtonHTMLAttributes,
34 | HTMLButtonElement
35 | >;
36 |
37 | const SubmitButton = (props: ButtonProps) => (
38 | submit
39 | );
40 |
41 | type UserFormProps = {
42 | onSubmit: (email: string, password: string) => void;
43 | };
44 |
45 | const UserForm = ({ onSubmit }: UserFormProps) => {
46 | const [email, setEmail] = useState('');
47 | const [password, setPassword] = useState('');
48 |
49 | return (
50 |
51 |
52 | email:
53 |
54 |
55 | password:{' '}
56 |
61 |
62 | onSubmit(email, password)} />
63 |
64 | );
65 | };
66 |
67 | export default UserForm;
68 |
--------------------------------------------------------------------------------
/example/src/firebaseConfig.ts:
--------------------------------------------------------------------------------
1 | const firebaseConfig = {
2 | apiKey: 'AIzaSyC0hOjv4hqAvG-g7UxItugLuaj-6E2FOjI',
3 | authDomain: 'react-firebase-auth-9d938.firebaseapp.com',
4 | databaseURL: 'https://react-firebase-auth-9d938.firebaseio.com',
5 | projectId: 'react-firebase-auth-9d938',
6 | storageBucket: 'react-firebase-auth-9d938.appspot.com',
7 | messagingSenderId: '975967064412',
8 | };
9 |
10 | export default firebaseConfig;
11 |
--------------------------------------------------------------------------------
/example/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 | import reportWebVitals from './reportWebVitals';
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById('root')
11 | );
12 |
13 | // If you want to start measuring performance in your app, pass a function
14 | // to log results (for example: reportWebVitals(console.log))
15 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
16 | reportWebVitals();
17 |
--------------------------------------------------------------------------------
/example/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/example/src/reportWebVitals.ts:
--------------------------------------------------------------------------------
1 | import { ReportHandler } from 'web-vitals';
2 |
3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => {
4 | if (onPerfEntry && onPerfEntry instanceof Function) {
5 | import('web-vitals').then(
6 | ({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
7 | getCLS(onPerfEntry);
8 | getFID(onPerfEntry);
9 | getFCP(onPerfEntry);
10 | getLCP(onPerfEntry);
11 | getTTFB(onPerfEntry);
12 | }
13 | );
14 | }
15 | };
16 |
17 | export default reportWebVitals;
18 |
--------------------------------------------------------------------------------
/example/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-with-firebase-auth",
3 | "version": "1.4.1",
4 | "description": "Higher Order Component for integrating Firebase with a React Component",
5 | "author": "armand1m",
6 | "license": "MIT",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/armand1m/react-with-firebase-auth.git"
10 | },
11 | "main": "dist/index.js",
12 | "module": "dist/index.es.js",
13 | "typings": "dist/types/index.d.ts",
14 | "jsnext:main": "dist/index.es.js",
15 | "keywords": [
16 | "react",
17 | "firebase",
18 | "firebase-auth",
19 | "authentication",
20 | "higher-order-component",
21 | "composable",
22 | "hoc",
23 | "google",
24 | "facebook",
25 | "twitter",
26 | "email"
27 | ],
28 | "engines": {
29 | "node": ">=8",
30 | "npm": ">=5"
31 | },
32 | "prettier": {
33 | "printWidth": 80,
34 | "semi": true,
35 | "singleQuote": true,
36 | "trailingComma": "es5"
37 | },
38 | "size-limit": [
39 | {
40 | "path": "dist/index.js",
41 | "limit": "10 KB"
42 | },
43 | {
44 | "path": "dist/index.es.js",
45 | "limit": "10 KB"
46 | }
47 | ],
48 | "scripts": {
49 | "test": "jest --coverage",
50 | "build": "rollup -c",
51 | "start": "rollup -c -w",
52 | "lint": "prettier --check 'src/**/*.{ts,tsx}'",
53 | "lint:fix": "prettier --write 'src/**/*.{ts,tsx}'",
54 | "prepare": "npm run build",
55 | "semantic-release": "semantic-release",
56 | "pack:inspect": "yarn pack && tar -ztvf *.tgz",
57 | "build:example": "cd example && yarn && yarn build"
58 | },
59 | "peerDependencies": {
60 | "firebase": "^8.6.8",
61 | "react": "^17.0.2",
62 | "react-dom": "^17.0.2"
63 | },
64 | "devDependencies": {
65 | "@babel/core": "^7.14.6",
66 | "@babel/runtime": "^7.14.6",
67 | "@rollup/plugin-commonjs": "^19.0.0",
68 | "@rollup/plugin-node-resolve": "^13.0.0",
69 | "@rollup/plugin-typescript": "^8.2.1",
70 | "@rollup/plugin-url": "^6.0.0",
71 | "@semantic-release/changelog": "^5.0.1",
72 | "@semantic-release/git": "^9.0.0",
73 | "@semantic-release/github": "^7.2.3",
74 | "@semantic-release/npm": "^7.1.3",
75 | "@size-limit/preset-small-lib": "^4.9.1",
76 | "@types/enzyme": "^3.10.8",
77 | "@types/enzyme-adapter-react-16": "^1.0.6",
78 | "@types/firebase": "^3.2.1",
79 | "@types/hoist-non-react-statics": "^3.3.1",
80 | "@types/jest": "^26.0.23",
81 | "@types/react": "^17.0.13",
82 | "@types/react-dom": "^17.0.8",
83 | "codecov": "^3.8.2",
84 | "core-js": "3",
85 | "cross-env": "^7.0.3",
86 | "enzyme": "^3.11.0",
87 | "enzyme-adapter-react-16": "^1.15.6",
88 | "gh-pages": "^3.2.3",
89 | "jest": "^27.0.6",
90 | "prettier": "^2.3.2",
91 | "react": "^16.13.1",
92 | "react-dom": "^16.13.1",
93 | "rollup": "^2.52.6",
94 | "rollup-plugin-peer-deps-external": "^2.2.4",
95 | "semantic-release": "^17.4.4",
96 | "size-limit": "^4.9.1",
97 | "ts-jest": "^27.0.3",
98 | "tslib": "^2.3.0",
99 | "typescript": "^4.3.5"
100 | },
101 | "files": [
102 | "dist"
103 | ],
104 | "jest": {
105 | "preset": "ts-jest",
106 | "testEnvironment": "jsdom",
107 | "setupFiles": [
108 | "./src/setupTests.ts"
109 | ]
110 | },
111 | "dependencies": {},
112 | "release": {
113 | "branches": [
114 | "master"
115 | ],
116 | "plugins": [
117 | "@semantic-release/commit-analyzer",
118 | "@semantic-release/release-notes-generator",
119 | [
120 | "@semantic-release/changelog",
121 | {
122 | "changelogFile": "CHANGELOG.md"
123 | }
124 | ],
125 | "@semantic-release/npm",
126 | [
127 | "@semantic-release/git",
128 | {
129 | "assets": [
130 | "package.json",
131 | "CHANGELOG.md"
132 | ],
133 | "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
134 | }
135 | ],
136 | [
137 | "@semantic-release/github",
138 | {
139 | "assets": [
140 | {
141 | "path": "dist/index.js",
142 | "label": "JS distribution"
143 | },
144 | {
145 | "path": "dist/index.es.js",
146 | "label": "ES distribution"
147 | }
148 | ]
149 | }
150 | ]
151 | ]
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import url from '@rollup/plugin-url'
2 | import commonjs from '@rollup/plugin-commonjs'
3 | import resolve from '@rollup/plugin-node-resolve'
4 | import typescript from '@rollup/plugin-typescript'
5 | import external from 'rollup-plugin-peer-deps-external'
6 |
7 | import pkg from './package.json'
8 |
9 | export default {
10 | input: 'src/index.tsx',
11 | output: [
12 | {
13 | file: pkg.main,
14 | format: 'cjs',
15 | exports: 'named',
16 | sourcemap: true
17 | },
18 | {
19 | file: pkg.module,
20 | format: 'es',
21 | exports: 'named',
22 | sourcemap: true
23 | },
24 | ],
25 | plugins: [
26 | external(),
27 | url(),
28 | resolve(),
29 | typescript({
30 | tsconfig: './tsconfig.json',
31 | }),
32 | commonjs()
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/src/getErrorMessageForProvider.ts:
--------------------------------------------------------------------------------
1 | import { PossibleProviders } from './';
2 |
3 | const providerToTypeMapper = {
4 | googleProvider: 'firebase.auth.GoogleAuthProvider' as const,
5 | facebookProvider: 'firebase.auth.FacebookAuthProvider' as const,
6 | twitterProvider: 'firebase.auth.TwitterAuthProvider' as const,
7 | githubProvider: 'firebase.auth.GithubAuthProvider' as const,
8 | appleProvider: 'firebase.auth.OAuthProvider' as const,
9 | };
10 |
11 | const providerToFirebaseDocs = {
12 | googleProvider:
13 | 'https://firebase.google.com/docs/auth/web/google-signin' as const,
14 | facebookProvider:
15 | 'https://firebase.google.com/docs/auth/web/facebook-signin' as const,
16 | twitterProvider:
17 | 'https://firebase.google.com/docs/auth/web/twitter-signin' as const,
18 | githubProvider:
19 | 'https://firebase.google.com/docs/auth/web/github-auth' as const,
20 | appleProvider: 'https://firebase.google.com/docs/auth/web/apple' as const,
21 | };
22 |
23 | const getErrorMessageForProvider = (provider: PossibleProviders) =>
24 | `Please provide an instance of ${providerToTypeMapper[provider]} in the withFirebaseAuth HOC providers parameter under the ${provider} key. Check ${providerToFirebaseDocs[provider]} to learn more.`;
25 |
26 | export default getErrorMessageForProvider;
27 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import firebase from 'firebase';
3 |
4 | import getErrorMessageForProvider from './getErrorMessageForProvider';
5 |
6 | export type WrappedComponentProps = {
7 | signInWithEmailAndPassword: (email: string, password: string) => void;
8 | createUserWithEmailAndPassword: (email: string, password: string) => void;
9 | signInWithGoogle: () => void;
10 | signInWithFacebook: () => void;
11 | signInWithGithub: () => void;
12 | signInWithTwitter: () => void;
13 | signInWithApple: () => void;
14 | signInWithPhoneNumber: (
15 | phoneNumber: string,
16 | applicationVerifier: firebase.auth.ApplicationVerifier
17 | ) => void;
18 | signInAnonymously: () => void;
19 | signOut: () => void;
20 | setError: (error: string | null) => void;
21 | user?: firebase.User | null;
22 | error?: string;
23 | loading: boolean;
24 | };
25 |
26 | export type PossibleProviders = keyof ProvidersMapper;
27 |
28 | export type ProvidersMapper = {
29 | googleProvider?: firebase.auth.GoogleAuthProvider_Instance;
30 | facebookProvider?: firebase.auth.FacebookAuthProvider_Instance;
31 | twitterProvider?: firebase.auth.TwitterAuthProvider_Instance;
32 | githubProvider?: firebase.auth.GithubAuthProvider_Instance;
33 | appleProvider?: firebase.auth.OAuthProvider;
34 | };
35 |
36 | export type HocParameters = {
37 | firebaseAppAuth: firebase.auth.Auth;
38 | providers?: ProvidersMapper;
39 | };
40 |
41 | export type FirebaseAuthProviderState = {
42 | loading: boolean;
43 | user?: firebase.User | null;
44 | error?: string | null;
45 | };
46 |
47 | const withFirebaseAuth = ({
48 | firebaseAppAuth,
49 | providers = {},
50 | }: HocParameters) => {
51 | return function createComponentWithAuth(
52 | WrappedComponent: React.ComponentType
53 | ) {
54 | return class FirebaseAuthProvider extends React.PureComponent<
55 | P,
56 | FirebaseAuthProviderState
57 | > {
58 | static displayName = `withFirebaseAuth(${
59 | WrappedComponent.displayName || WrappedComponent.name
60 | })`;
61 |
62 | state = {
63 | loading: false,
64 | user: undefined,
65 | error: undefined,
66 | };
67 |
68 | unsubscribeAuthStateListener: firebase.Unsubscribe | undefined;
69 |
70 | componentDidMount() {
71 | this.unsubscribeAuthStateListener = firebaseAppAuth.onAuthStateChanged(
72 | (user) => this.setState({ user })
73 | );
74 | }
75 |
76 | componentWillUnmount() {
77 | if (this.unsubscribeAuthStateListener) {
78 | this.unsubscribeAuthStateListener();
79 | }
80 | }
81 |
82 | setError = (error: string | null) => this.setState({ error });
83 |
84 | toggleLoading = () => {
85 | this.setState((currState) => ({ loading: !currState.loading }));
86 | };
87 |
88 | async tryTo(operation: () => Promise) {
89 | try {
90 | this.toggleLoading();
91 | const result = await operation();
92 | return result;
93 | } catch (error) {
94 | this.setError(error.message);
95 | return error as firebase.auth.Error;
96 | } finally {
97 | this.toggleLoading();
98 | }
99 | }
100 |
101 | tryToSignInWithProvider = (provider: PossibleProviders) =>
102 | this.tryTo(() => {
103 | const providerInstance = providers[provider];
104 |
105 | if (!providerInstance) {
106 | throw new Error(getErrorMessageForProvider(provider));
107 | }
108 |
109 | return firebaseAppAuth.signInWithPopup(providerInstance);
110 | });
111 |
112 | signOut = () => {
113 | return this.tryTo(() => firebaseAppAuth.signOut());
114 | };
115 |
116 | signInAnonymously = () => {
117 | return this.tryTo(() =>
118 | firebaseAppAuth.signInAnonymously()
119 | );
120 | };
121 |
122 | signInWithGithub = () => this.tryToSignInWithProvider('githubProvider');
123 |
124 | signInWithTwitter = () => this.tryToSignInWithProvider('twitterProvider');
125 |
126 | signInWithGoogle = () => this.tryToSignInWithProvider('googleProvider');
127 |
128 | signInWithFacebook = () =>
129 | this.tryToSignInWithProvider('facebookProvider');
130 |
131 | signInWithApple = () => this.tryToSignInWithProvider('appleProvider');
132 |
133 | signInWithEmailAndPassword = (email: string, password: string) => {
134 | return this.tryTo(() =>
135 | firebaseAppAuth.signInWithEmailAndPassword(email, password)
136 | );
137 | };
138 |
139 | signInWithPhoneNumber = (
140 | phoneNumber: string,
141 | applicationVerifier: firebase.auth.ApplicationVerifier
142 | ) => {
143 | return this.tryTo(() =>
144 | firebaseAppAuth.signInWithPhoneNumber(
145 | phoneNumber,
146 | applicationVerifier
147 | )
148 | );
149 | };
150 |
151 | createUserWithEmailAndPassword = (email: string, password: string) => {
152 | return this.tryTo(() =>
153 | firebaseAppAuth.createUserWithEmailAndPassword(email, password)
154 | );
155 | };
156 |
157 | sharedHandlers = {
158 | createUserWithEmailAndPassword: this.createUserWithEmailAndPassword,
159 | signInWithEmailAndPassword: this.signInWithEmailAndPassword,
160 | signInWithGithub: this.signInWithGithub,
161 | signInWithTwitter: this.signInWithTwitter,
162 | signInWithGoogle: this.signInWithGoogle,
163 | signInWithFacebook: this.signInWithFacebook,
164 | signInWithApple: this.signInWithApple,
165 | signInWithPhoneNumber: this.signInWithPhoneNumber,
166 | signInAnonymously: this.signInAnonymously,
167 | setError: this.setError,
168 | signOut: this.signOut,
169 | };
170 |
171 | render() {
172 | const props = {
173 | ...this.props,
174 | ...this.sharedHandlers,
175 | loading: this.state.loading,
176 | user: this.state.user,
177 | error: this.state.error,
178 | };
179 |
180 | return ;
181 | }
182 | };
183 | };
184 | };
185 |
186 | export default withFirebaseAuth;
187 |
--------------------------------------------------------------------------------
/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | import Enzyme from 'enzyme';
2 | import Adapter from 'enzyme-adapter-react-16';
3 |
4 | Enzyme.configure({ adapter: new Adapter() });
5 |
--------------------------------------------------------------------------------
/src/test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import firebase from 'firebase';
3 | import { shallow, mount } from 'enzyme';
4 |
5 | import withFirebaseAuth, { WrappedComponentProps } from './';
6 |
7 | const testAppAuth = {} as firebase.auth.Auth;
8 |
9 | const providers = {
10 | googleProvider: {} as firebase.auth.GoogleAuthProvider_Instance,
11 | twitterProvider: {} as firebase.auth.TwitterAuthProvider_Instance,
12 | githubProvider: {} as firebase.auth.GithubAuthProvider_Instance,
13 | facebookProvider: {} as firebase.auth.FacebookAuthProvider_Instance,
14 | appleProvider: {} as firebase.auth.OAuthProvider,
15 | };
16 |
17 | const fakeUser = {
18 | displayName: 'fake',
19 | } as firebase.User;
20 |
21 | type AuthStateObserver =
22 | | firebase.Observer
23 | | ((user: firebase.User | null) => any);
24 |
25 | describe('withFirebaseAuth', () => {
26 | let currentAuthStateObserver: AuthStateObserver = (
27 | _user: firebase.User | null
28 | ) => {};
29 | let unsubcribeAuthStateChangeMock = jest.fn();
30 |
31 | beforeEach(() => {
32 | unsubcribeAuthStateChangeMock = jest.fn();
33 |
34 | testAppAuth.signInWithEmailAndPassword = jest.fn();
35 | testAppAuth.createUserWithEmailAndPassword = jest.fn();
36 | testAppAuth.signInAnonymously = jest.fn();
37 | testAppAuth.signOut = jest.fn();
38 | testAppAuth.signInWithPopup = jest.fn();
39 | testAppAuth.signInWithPhoneNumber = jest.fn();
40 | testAppAuth.onAuthStateChanged = jest.fn((observer) => {
41 | currentAuthStateObserver = observer;
42 | return unsubcribeAuthStateChangeMock;
43 | });
44 | });
45 |
46 | it('should be a function', () => {
47 | expect(typeof withFirebaseAuth).toBe('function');
48 | });
49 |
50 | it('should render WrappedComponent', () => {
51 | const WrappedComponent = () => ama wrapped
;
52 |
53 | const EnhancedComponent = withFirebaseAuth({
54 | firebaseAppAuth: testAppAuth,
55 | })(WrappedComponent);
56 |
57 | const wrapped = shallow( );
58 |
59 | expect(wrapped.find(WrappedComponent).exists()).toBeTruthy();
60 | });
61 |
62 | it('should start with loading as false', () => {
63 | const WrappedComponent = ({ loading }: WrappedComponentProps) => (
64 | {String(loading)}
65 | );
66 |
67 | const EnhancedComponent = withFirebaseAuth({
68 | firebaseAppAuth: testAppAuth,
69 | })(WrappedComponent);
70 |
71 | const wrapped = mount( );
72 |
73 | expect(wrapped.text()).toBe('false');
74 | });
75 |
76 | it('should setup onAuthStateChange listener when mounting the component', () => {
77 | const WrappedComponent = () => ama wrapped
;
78 |
79 | const EnhancedComponent = withFirebaseAuth({
80 | firebaseAppAuth: testAppAuth,
81 | })(WrappedComponent);
82 |
83 | mount( );
84 |
85 | expect(testAppAuth.onAuthStateChanged).toHaveBeenCalledTimes(1);
86 | });
87 |
88 | it('should call onAuthStateChange unsubscriber when unmounting the component', () => {
89 | const WrappedComponent = () => ama wrapped
;
90 |
91 | const EnhancedComponent = withFirebaseAuth({
92 | firebaseAppAuth: testAppAuth,
93 | })(WrappedComponent);
94 |
95 | const wrapped = mount( );
96 |
97 | wrapped.unmount();
98 |
99 | expect(unsubcribeAuthStateChangeMock).toHaveBeenCalledTimes(1);
100 | });
101 |
102 | it('should update user in state when currentAuthStateObserver is invoked', () => {
103 | const WrappedComponent = () => ama wrapped
;
104 |
105 | const EnhancedComponent = withFirebaseAuth({
106 | firebaseAppAuth: testAppAuth,
107 | })(WrappedComponent);
108 |
109 | const wrapped = mount( );
110 |
111 | expect(wrapped.state('user')).toBeUndefined();
112 |
113 | if (typeof currentAuthStateObserver === 'function') {
114 | currentAuthStateObserver(fakeUser);
115 | }
116 |
117 | expect(wrapped.state('user')).toBe(fakeUser);
118 | });
119 |
120 | it('should call signInAnonymously when prop is invoked', () => {
121 | const WrappedComponent = ({ signInAnonymously }: WrappedComponentProps) => (
122 | signInAnonymously
123 | );
124 |
125 | const EnhancedComponent = withFirebaseAuth({
126 | firebaseAppAuth: testAppAuth,
127 | })(WrappedComponent);
128 |
129 | const wrapped = mount( );
130 |
131 | wrapped.find('button').simulate('click');
132 |
133 | expect(testAppAuth.signInAnonymously).toHaveBeenCalled();
134 | });
135 |
136 | it('should call signOut when prop is invoked', () => {
137 | const WrappedComponent = ({ signOut }: WrappedComponentProps) => (
138 | signOut
139 | );
140 |
141 | const EnhancedComponent = withFirebaseAuth({
142 | firebaseAppAuth: testAppAuth,
143 | })(WrappedComponent);
144 |
145 | const wrapped = mount( );
146 |
147 | wrapped.find('button').simulate('click');
148 |
149 | expect(testAppAuth.signOut).toHaveBeenCalled();
150 | });
151 |
152 | it('should call signInWithEmailAndPassword when prop is invoked', () => {
153 | const WrappedComponent = ({
154 | signInWithEmailAndPassword,
155 | }: WrappedComponentProps) => (
156 | signInWithEmailAndPassword('test', 'test')}>
157 | signInWithEmailAndPassword
158 |
159 | );
160 |
161 | const EnhancedComponent = withFirebaseAuth({
162 | firebaseAppAuth: testAppAuth,
163 | })(WrappedComponent);
164 |
165 | const wrapped = mount( );
166 |
167 | wrapped.find('button').simulate('click');
168 |
169 | expect(testAppAuth.signInWithEmailAndPassword).toHaveBeenCalled();
170 | });
171 |
172 | it('should call signInWithPopup with googleProvider instance when signInWithGoogle prop is invoked', () => {
173 | const WrappedComponent = ({ signInWithGoogle }: WrappedComponentProps) => (
174 | signInWithGoogle()}>signInWithGoogle
175 | );
176 |
177 | const EnhancedComponent = withFirebaseAuth({
178 | firebaseAppAuth: testAppAuth,
179 | providers,
180 | })(WrappedComponent);
181 |
182 | const wrapped = mount( );
183 |
184 | wrapped.find('button').simulate('click');
185 |
186 | expect(testAppAuth.signInWithPopup).toHaveBeenCalledWith(
187 | providers.googleProvider
188 | );
189 | });
190 |
191 | it('should call signInWithPopup with twitterProvider instance when signInWithTwitter prop is invoked', () => {
192 | const WrappedComponent = ({ signInWithTwitter }: WrappedComponentProps) => (
193 | signInWithTwitter()}>signInWithTwitter
194 | );
195 |
196 | const EnhancedComponent = withFirebaseAuth({
197 | firebaseAppAuth: testAppAuth,
198 | providers,
199 | })(WrappedComponent);
200 |
201 | const wrapped = mount( );
202 |
203 | wrapped.find('button').simulate('click');
204 |
205 | expect(testAppAuth.signInWithPopup).toHaveBeenCalledWith(
206 | providers.twitterProvider
207 | );
208 | });
209 |
210 | it('should call signInWithPopup with facebookProvider instance when signInWithFacebook prop is invoked', () => {
211 | const WrappedComponent = ({
212 | signInWithFacebook,
213 | }: WrappedComponentProps) => (
214 | signInWithFacebook()}>signInWithFacebook
215 | );
216 |
217 | const EnhancedComponent = withFirebaseAuth({
218 | firebaseAppAuth: testAppAuth,
219 | providers,
220 | })(WrappedComponent);
221 |
222 | const wrapped = mount( );
223 |
224 | wrapped.find('button').simulate('click');
225 |
226 | expect(testAppAuth.signInWithPopup).toHaveBeenCalledWith(
227 | providers.facebookProvider
228 | );
229 | });
230 |
231 | it('should call signInWithPopup with appleProvider instance when signInWithApple prop is invoked', () => {
232 | const WrappedComponent = ({ signInWithApple }: WrappedComponentProps) => (
233 | signInWithApple()}>signInWithApple
234 | );
235 |
236 | const EnhancedComponent = withFirebaseAuth({
237 | firebaseAppAuth: testAppAuth,
238 | providers,
239 | })(WrappedComponent);
240 |
241 | const wrapped = mount( );
242 |
243 | wrapped.find('button').simulate('click');
244 |
245 | expect(testAppAuth.signInWithPopup).toHaveBeenCalledWith(
246 | providers.facebookProvider
247 | );
248 | });
249 |
250 | it('should call signInWithPopup with githubProvider instance when signInWithGithub prop is invoked', () => {
251 | const WrappedComponent = ({ signInWithGithub }: WrappedComponentProps) => (
252 | signInWithGithub()}>signInWithGithub
253 | );
254 |
255 | const EnhancedComponent = withFirebaseAuth({
256 | firebaseAppAuth: testAppAuth,
257 | providers,
258 | })(WrappedComponent);
259 |
260 | const wrapped = mount( );
261 |
262 | wrapped.find('button').simulate('click');
263 |
264 | expect(testAppAuth.signInWithPopup).toHaveBeenCalledWith(
265 | providers.githubProvider
266 | );
267 | });
268 |
269 | it('should call createUserWithEmailAndPassword when prop is invoked', () => {
270 | const email = 'test';
271 | const password = 'test';
272 |
273 | const WrappedComponent = ({
274 | createUserWithEmailAndPassword,
275 | }: WrappedComponentProps) => (
276 | createUserWithEmailAndPassword(email, password)}>
277 | createUserWithEmailAndPassword
278 |
279 | );
280 |
281 | const EnhancedComponent = withFirebaseAuth({
282 | firebaseAppAuth: testAppAuth,
283 | })(WrappedComponent);
284 |
285 | const wrapped = mount( );
286 |
287 | wrapped.find('button').simulate('click');
288 |
289 | expect(testAppAuth.createUserWithEmailAndPassword).toHaveBeenCalledWith(
290 | email,
291 | password
292 | );
293 | });
294 |
295 | it('should call signInWithPhoneNumber when prop is invoked', () => {
296 | const phoneNumber = '666999666';
297 | const applicationVerifier = { type: '', verify: () => Promise.resolve('') };
298 |
299 | const WrappedComponent = ({
300 | signInWithPhoneNumber,
301 | }: WrappedComponentProps) => (
302 | signInWithPhoneNumber(phoneNumber, applicationVerifier)}
304 | >
305 | signInWithPhoneNumber
306 |
307 | );
308 |
309 | const EnhancedComponent = withFirebaseAuth({
310 | firebaseAppAuth: testAppAuth,
311 | })(WrappedComponent);
312 |
313 | const wrapped = mount( );
314 |
315 | wrapped.find('button').simulate('click');
316 |
317 | expect(testAppAuth.signInWithPhoneNumber).toHaveBeenCalledWith(
318 | phoneNumber,
319 | applicationVerifier
320 | );
321 | });
322 |
323 | it('should set an error when setError is invoked', () => {
324 | const WrappedComponent = ({ setError, error }: WrappedComponentProps) => (
325 | <>
326 | setError('test-error')}>setError
327 | {error}
328 | >
329 | );
330 |
331 | const EnhancedComponent = withFirebaseAuth({
332 | firebaseAppAuth: testAppAuth,
333 | })(WrappedComponent);
334 |
335 | const wrapped = mount( );
336 |
337 | expect(wrapped.find('span').text()).toBe('');
338 | wrapped.find('button').simulate('click');
339 | expect(wrapped.find('span').text()).toBe('test-error');
340 | });
341 |
342 | it('should set an error when trying to call a login provider without providing its instance', () => {
343 | const WrappedComponent = ({ signInWithGithub }: WrappedComponentProps) => (
344 | signInWithGithub()}>signInWithGithub
345 | );
346 |
347 | const EnhancedComponent = withFirebaseAuth({
348 | firebaseAppAuth: testAppAuth,
349 | })(WrappedComponent);
350 |
351 | const wrapped = mount( );
352 |
353 | wrapped.find('button').simulate('click');
354 |
355 | expect(wrapped.state('error')).toContain(
356 | 'Please provide an instance of firebase.auth.GithubAuthProvider'
357 | );
358 | });
359 | });
360 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "pretty": true,
4 | "strict": true,
5 | "removeComments": true,
6 | "noImplicitReturns": true,
7 | "noImplicitAny": true,
8 | "noImplicitThis": true,
9 | "alwaysStrict": false,
10 | "strictFunctionTypes": true,
11 | "strictPropertyInitialization": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "diagnostics": true,
14 | "listEmittedFiles": true,
15 | "importHelpers": true,
16 | "noUnusedLocals": true,
17 | "esModuleInterop": true,
18 | "strictNullChecks": true,
19 | "experimentalDecorators": true,
20 | "isolatedModules": true,
21 | "skipLibCheck": true,
22 | "sourceMap": true,
23 | "resolveJsonModule": true,
24 | "declaration": true,
25 | "module": "esnext",
26 | "target": "es5",
27 | "lib": ["dom", "esnext"],
28 | "allowJs": false,
29 | "jsx": "react",
30 | "moduleResolution": "node",
31 | "suppressImplicitAnyIndexErrors": true,
32 | "noUnusedParameters": true,
33 | "rootDir": "./src",
34 | "outDir": "build",
35 | "declarationDir": "types"
36 | },
37 | "include": ["src"],
38 | "exclude": ["node_modules", "build", "dist", "example", "rollup.config.js"]
39 | }
40 |
--------------------------------------------------------------------------------
/tsconfig.test.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "module": "commonjs"
5 | }
6 | }
--------------------------------------------------------------------------------