├── .babelrc ├── .circleci └── config.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmignore ├── .npmrc ├── LICENSE.md ├── README.md ├── docs-www ├── .gitignore ├── LICENSE ├── README.md ├── gatsby-config.js ├── gatsby-docs-kit.yml ├── package.json ├── src │ ├── globalReferences.js │ ├── layouts │ │ └── .gitkeep │ ├── pages │ │ ├── 404.js │ │ ├── landing.js │ │ ├── landing.module.scss │ │ └── variables.scss │ ├── static │ │ └── favicon.png │ └── templates │ │ └── .gitkeep ├── tools │ ├── deploy-github.sh │ ├── seed.sh │ └── seed │ │ └── example.md └── yarn.lock ├── docs ├── examples │ ├── callback.md │ ├── oneMatch.md │ ├── permissionsMatch.md │ └── renderOtherwise.md ├── getting-started │ ├── installation.md │ └── overview.md └── usage │ ├── basic.md │ └── hoc.md ├── lib └── react-permissible.js ├── package.json ├── renovate.json ├── src ├── components │ ├── permissible.js │ └── permissibleRender.js └── index.js ├── test ├── .eslintrc ├── accessible.component.js ├── permissible.test.js └── permissibleRender.test.js ├── tools ├── build.js └── testSetup.js ├── types ├── .eslintrc ├── react-permissible.d.ts └── tests │ ├── .gitignore │ ├── .npmrc │ ├── Permissible.test.tsx │ ├── PermissibleRender.test.tsx │ ├── package-lock.json │ ├── package.json │ └── tsconfig.json ├── webpack.config.prod.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env", 4 | "react", 5 | "stage-1" 6 | ], 7 | "plugins": [ 8 | "transform-decorators-legacy" 9 | ], 10 | "env": { 11 | "production": { 12 | "plugins": [ 13 | "transform-react-constant-elements", 14 | "transform-react-remove-prop-types" 15 | ] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | executors: 3 | node-executor: 4 | docker: 5 | - image: circleci/node:14.4.0 6 | working_directory: ~/app 7 | 8 | jobs: 9 | check_branch_name: 10 | docker: 11 | - image: circleci/node:14.4.0 12 | working_directory: ~/app 13 | steps: 14 | - run: 15 | name: fail if the branch name does not start with a valid prefix 16 | command: | 17 | branch=$CIRCLE_BRANCH 18 | if [[ "$branch" =~ ^(dependabot|fix|feature|breaking)/ || "$branch" == 'master' ]] 19 | then 20 | echo $branch is a valid name 21 | else 22 | echo $branch is not valid because the branch name must match '^(dependabot|fix|feature|breaking)/' or be master 23 | exit 1 24 | fi 25 | build: 26 | working_directory: ~/app 27 | docker: 28 | - image: circleci/node:14.4.0 29 | steps: 30 | - checkout 31 | - restore_cache: 32 | key: dependency-cache-{{ checksum "package.json" }} 33 | - run: 34 | name: Install npm dependencies 35 | command: npm install --production=false 36 | - save_cache: 37 | key: dependency-cache-{{ checksum "package.json" }} 38 | paths: 39 | - node_modules 40 | test: 41 | executor: node-executor 42 | steps: 43 | - checkout 44 | - restore_cache: 45 | key: dependency-cache-{{ checksum "package.json" }} 46 | - run: 47 | name: Install npm dependencies 48 | command: npm install --production=false 49 | - save_cache: 50 | key: dependency-cache-{{ checksum "package.json" }} 51 | paths: 52 | - node_modules 53 | - run: 54 | name: Test 55 | command: npm run test:coverage:ci 56 | - persist_to_workspace: 57 | root: ~/app 58 | paths: 59 | - .git 60 | - node_modules 61 | - coverage 62 | lint: 63 | docker: 64 | - image: circleci/node:14.4.0 65 | steps: 66 | - checkout 67 | - restore_cache: 68 | key: dependency-cache-{{ checksum "package.json" }} 69 | - run: 70 | name: Install npm dependencies 71 | command: npm install --production=false 72 | - save_cache: 73 | key: dependency-cache-{{ checksum "package.json" }} 74 | paths: 75 | - node_modules 76 | - run: 77 | name: Lint 78 | command: npm run lint 79 | compile: 80 | docker: 81 | - image: circleci/node:14.4.0 82 | steps: 83 | - checkout 84 | - restore_cache: 85 | key: dependency-cache-{{ checksum "package.json" }} 86 | - run: 87 | name: Install npm dependencies 88 | command: npm install --production=false 89 | - save_cache: 90 | key: dependency-cache-{{ checksum "package.json" }} 91 | paths: 92 | - node_modules 93 | - run: 94 | name: Compile 95 | command: npm run build 96 | publish_package: 97 | executor: node-executor 98 | steps: 99 | - attach_workspace: 100 | at: ~/app 101 | - run: git checkout . 102 | - run: 103 | name: Generate coveralls config 104 | command: "echo repo_token: $REPO_TOKEN > ./.coveralls.yml" 105 | - run: 106 | name: Upload coverage to coveralls 107 | command: cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js 108 | - run: 109 | name: configure GitHub user 110 | command: | 111 | git config --global user.email "devops@brainhub.pl" 112 | git config --global user.name "DevOps Brainhub" 113 | git remote -v 114 | git remote remove origin 115 | git remote add origin https://$GIT_TOKEN@github.com/brainhubeu/react-permissible 116 | git remote -v 117 | - run: 118 | name: bump NPM version 119 | command: | 120 | branch=`git log --oneline | grep '[0-9a-f]\{6,40\} Merge pull request #[0-9]\+ from brainhubeu/' | head -1 | sed 's@.* from brainhubeu/@@g' || true` 121 | echo branch=$branch 122 | if [[ "$branch" =~ ^(dependabot)/ ]]; then 123 | npm version patch -m "%s [ci skip]" 124 | elif [[ "$branch" =~ ^(fix)/ ]]; then 125 | npm version patch -m "%s [ci skip]" 126 | elif [[ "$branch" =~ ^(feature)/ ]]; then 127 | npm version minor -m "%s [ci skip]" 128 | elif [[ "$branch" =~ ^(breaking)/ ]]; then 129 | npm version major -m "%s [ci skip]" 130 | else 131 | echo $branch is not valid because the branch name must match '^(dependabot|fix|feature|breaking)/' 132 | exit 1 133 | fi 134 | - run: git pull --no-edit origin $CIRCLE_BRANCH 135 | - run: git push origin $CIRCLE_BRANCH 136 | - run: 137 | name: npm publish 138 | command: | 139 | echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc 140 | npm publish 141 | workflows: 142 | version: 2 143 | build_and_test: 144 | jobs: 145 | - check_branch_name 146 | - build: 147 | requires: 148 | - check_branch_name 149 | - test: 150 | requires: 151 | - build 152 | - lint: 153 | requires: 154 | - build 155 | - compile: 156 | requires: 157 | - build 158 | - publish_package: 159 | requires: 160 | - test 161 | - lint 162 | - compile 163 | filters: 164 | branches: 165 | only: 166 | - master 167 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /lib/**/*.js 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "brainhub" 5 | ], 6 | "settings": { 7 | "import/resolve": { 8 | "moduleDirectory": ["node_modules", "src"] 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | 3 | # Logs 4 | logs 5 | *.log* 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | reports 18 | 19 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 20 | .grunt 21 | 22 | # node-waf configuration 23 | .lock-wscript 24 | 25 | # Compiled binary addons (http://nodejs.org/api/addons.html) 26 | build/Release 27 | 28 | # Dependency directory 29 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 30 | node_modules 31 | 32 | # IDEA/Webstorm project files 33 | .idea 34 | *.iml 35 | 36 | #VSCode metadata 37 | .vscode 38 | 39 | # Mac files 40 | .DS_Store 41 | 42 | # Debug files 43 | selenium-debug.log 44 | .npm.debug 45 | 46 | # Merge conflicts 47 | *.orig 48 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .babelrc 2 | .circleci/ 3 | .coveralls.yml 4 | .dockerignore 5 | .editorconfig 6 | .eslintignore 7 | .eslintrc 8 | .git/ 9 | .github/ 10 | .gitignore 11 | .istanbul.yml 12 | .npmignore 13 | .npmrc 14 | .nvmrc 15 | Dockerfile.e2e 16 | coverage/ 17 | cypress.json 18 | docker-compose.e2e.yml 19 | docs-www/ 20 | docs/ 21 | jest.config.js 22 | jest.setup.js 23 | renovate.json 24 | test/ 25 | tools/ 26 | types/ 27 | webpack.config.prod.js 28 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017-2020 [Brainhub](https://brainhub.eu/?utm_source=github) 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

3 | react-permissible 4 |

5 | 6 |

7 | Making the permission management for React components easier. 8 |

9 | 10 |

11 | 12 | Live code demo | 13 | Hire us 14 | 15 |

16 | 17 |
18 | 19 | [![CircleCI](https://circleci.com/gh/brainhubeu/react-permissible.svg?style=svg)](https://circleci.com/gh/brainhubeu/react-permissible) 20 | [![Last commit](https://img.shields.io/github/last-commit/brainhubeu/react-permissible.svg)](https://github.com/brainhubeu/react-permissible/commits/master) 21 | [![license](https://img.shields.io/npm/l/@brainhubeu/react-permissible.svg)](https://github.com/brainhubeu/react-permissible/blob/master/LICENSE.md) 22 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com) 23 | [![Renovate enabled](https://img.shields.io/badge/renovate-enabled-brightgreen.svg)](https://renovatebot.com/) 24 | 25 | [![Coveralls github](https://img.shields.io/coveralls/github/brainhubeu/react-permissible.svg)](https://coveralls.io/github/brainhubeu/react-permissible?branch=master) 26 | [![Downloads](https://img.shields.io/npm/dm/@brainhubeu/react-permissible?color=blue)](https://www.npmjs.com/package/@brainhubeu/react-permissible) 27 | [![Minified](https://img.shields.io/bundlephobia/min/@brainhubeu/react-permissible?label=minified)](https://www.npmjs.com/package/@brainhubeu/react-permissible) 28 | [![npm](https://img.shields.io/npm/v/@brainhubeu/react-permissible.svg)](https://www.npmjs.com/package/@brainhubeu/react-permissible) 29 | [![Contributors](https://img.shields.io/github/contributors/brainhubeu/react-permissible?color=blue)](https://github.com/brainhubeu/react-permissible/graphs/contributors) 30 |
31 | 32 | 33 | `react-permissible` is a React Component allowing to: 34 | * manage visibility of particular components depending on users permissions 35 | * replace particular component when the user isn't permitted to see it 36 | * manage accessability to particular view depending on users permissions 37 | * fire a callback when the user isn't allowed to go to access the component/route 38 | 39 | ## Why? 40 | Currently there's no permission management in React. The existing components are either over-engineered (full ACL support etc.), or limited to role-based management. `react-permissible` is simple at its core and solves only one problem. Access the Component if the permissions match, do something otherwise. 41 | 42 | ## Live demo/ docs 43 | You can access live demo/ docs at https://brainhubeu.github.io/react-permissible/. 44 | 45 | ## Installation 46 | ``` 47 | npm i @brainhubeu/react-permissible 48 | ``` 49 | 50 | ## Usage 51 | ```javascript 52 | import { PermissibleRender } from '@brainhubeu/react-permissible'; 53 | 54 | ... 55 | 56 | render() { 57 | return ( 58 | 62 | 63 | 64 | ); 65 | } 66 | ``` 67 | 68 | Where: 69 | * `userPermissions` is an **array** of permissions set for current user 70 | * `requiredPermissions` is an **array** of required permissions 71 | 72 | More detailed documentation with several use cases covered is available [here](http://brainhubeu.github.io/react-permissible). 73 | 74 | ## Running tests 75 | ``` 76 | npm test 77 | ``` 78 | 79 | ## Roadmap 80 | * Passing a callback function as a prop for `PermissibleRender` component 81 | 82 | ## License 83 | 84 | React-permissible is copyright © 2017-2020 [Brainhub](https://brainhub.eu/?utm_source=github) It is free software, and may be redistributed under the terms specified in the [license](LICENSE.md). 85 | 86 | ## About 87 | 88 | `react-permissible` is maintained by [@kkoscielniak](https://github.com/kkoscielniak), [@adam-golab](https://github.com/adam-golab), [@Lukasz-pluszczewski](https://github.com/Lukasz-pluszczewski/) and the Brainhub development team. It is funded by Brainhub and the names and logos for Brainhub are trademarks of Brainhub Sp. z o.o.. You can check other open-source projects supported/developed by our teammates [here](https://brainhub.eu/?utm_source=github). 89 | 90 | [![Brainhub](https://brainhub.eu/brainhub.svg)](https://brainhub.eu/?utm_source=github) 91 | 92 | We love open-source JavaScript software! See our other projects or hire us to build your next web, desktop and mobile application with JavaScript. 93 | -------------------------------------------------------------------------------- /docs-www/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log* 4 | 5 | # node-waf configuration 6 | .lock-wscript 7 | 8 | # Dependency directory 9 | node_modules 10 | 11 | # IDEA/Webstorm project files 12 | .idea 13 | *.iml 14 | 15 | #VSCode metadata 16 | .vscode 17 | 18 | # Mac files 19 | .DS_Store 20 | 21 | # Gatsby 22 | .cache 23 | public 24 | -------------------------------------------------------------------------------- /docs-www/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017-2020 Brainhub 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 | 23 | -------------------------------------------------------------------------------- /docs-www/README.md: -------------------------------------------------------------------------------- 1 | # Gatsby Docs Kit Starter 2 | The default Brainhub static docs starter. 3 | 4 | ## Try it out 5 | 6 | Ensure you have the latest version of [Node](https://nodejs.org/en/download/) installed. We also recommend you install [Yarn](https://yarnpkg.com/en/docs/install) as well. 7 | Then run: 8 | 9 | ```bash 10 | yarn install 11 | ``` 12 | 13 | Seed documentation (if you don't have any yet): 14 | 15 | ```bash 16 | yarn seed 17 | ``` 18 | 19 | Run the local webserver via `yarn develop`; 20 | 21 | The example site is available at http://localhost:8000. You should see the example site loaded in your web browser. 22 | Also visit http://localhost:8000/___graphql to explore your site's GraphiQL data and schema. 23 | 24 | Then go to `../docs` to edit and write awesome docs!. 25 | 26 | ## Deploy 27 | 28 | Ready to go? Want to deploy documentation to github pages? Run: 29 | 30 | ```bash 31 | yarn deploy:gh 32 | ``` -------------------------------------------------------------------------------- /docs-www/gatsby-config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const pluginConfigFactory = require('@brainhubeu/gatsby-docs-kit/plugins'); 5 | 6 | module.exports = { 7 | siteMetadata: { 8 | title: 'react-permissible Docs', 9 | description: 'Making the permission management for React components easier', 10 | image: 'https://cdn-images-1.medium.com/max/1200/1*CLUFZFaXF6NG27NA3d_JkQ.jpeg', 11 | url: 'https://brainhubeu.github.io/react-permissible', 12 | type: 'article', 13 | siteName: 'react-permissible Docs', 14 | githubUrl: 'https://github.com/brainhubeu', 15 | }, 16 | 17 | // URL prefix on production environment. For more info see https://www.gatsbyjs.org/docs/path-prefix/ 18 | pathPrefix: process.env.PATH_PREFIX || ' ', 19 | 20 | plugins: [ 21 | ...pluginConfigFactory({ 22 | config: `${__dirname}/gatsby-docs-kit.yml`, 23 | resources: path.resolve(__dirname, '../docs'), 24 | }), 25 | { 26 | resolve: `gatsby-plugin-google-analytics`, 27 | options: { 28 | trackingId: 'UA-62818184-6', 29 | head: false, 30 | anonymize: true, 31 | respectDNT: true, 32 | pageTransitionDelay: 0, 33 | cookieDomain: 'brainhubeu.github.io', 34 | }, 35 | ], 36 | }; 37 | -------------------------------------------------------------------------------- /docs-www/gatsby-docs-kit.yml: -------------------------------------------------------------------------------- 1 | - title: Home 2 | dir: ./src 3 | url: / 4 | file: pages/landing.js 5 | 6 | - title: Docs 7 | dir: ../docs 8 | url: docs 9 | sidemenu: 10 | - title: Getting Started 11 | dir: getting-started 12 | items: 13 | - title: Overview 14 | file: overview.md 15 | - title: Installation 16 | file: installation.md 17 | - title: Usage 18 | dir: usage 19 | items: 20 | - title: As an ordinary component 21 | file: basic.md 22 | - title: As Higher Order Component 23 | file: hoc.md 24 | - title: Examples 25 | dir: examples 26 | items: 27 | - title: Render component when all the permissions match 28 | file: permissionsMatch.md 29 | - title: Render component when one of the permissions match 30 | file: oneMatch.md 31 | - title: Render another component if the permissions do not match 32 | file: renderOtherwise.md 33 | - title: Callback function 34 | file: callback.md 35 | -------------------------------------------------------------------------------- /docs-www/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@brainhubeu/gatsby-docs-kit-start", 3 | "description": "Gatsby-Docs-Kit starter repository", 4 | "version": "0.0.32", 5 | "author": "Brainhub", 6 | "license": "MIT", 7 | "scripts": { 8 | "build": "rm -rf .cache && rm -rf public && gatsby build --prefix-paths", 9 | "deploy:gh": "bash tools/deploy-github.sh", 10 | "develop": "rm -rf .cache && gatsby develop", 11 | "seed": "bash tools/seed.sh", 12 | "serve": "gatsby serve", 13 | "lint": "eslint --ext .jsx,.js .", 14 | "lint:autofix": "eslint --ext .jsx,.js . --fix" 15 | }, 16 | "dependencies": { 17 | "@brainhubeu/gatsby-docs-kit": "1.0.10", 18 | "@brainhubeu/react-permissible": "^1.9.13", 19 | "gatsby": "1.9.279" 20 | }, 21 | "devDependencies": { 22 | "eslint": "^7.3.1", 23 | "eslint-config-brainhub": "^1.13.0", 24 | "gh-pages": "^3.1.0", 25 | "react": "^16.13.1", 26 | "react-dom": "^16.13.1" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /docs-www/src/globalReferences.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Permissible, PermissibleRender } from '@brainhubeu/react-permissible'; 3 | 4 | const boxStyle = { 5 | height: 130, 6 | width: 130, 7 | display: 'flex', 8 | margin: '0 auto', 9 | textAlign: 'center', 10 | backgroundColor: '#b5ffb5', 11 | justifyContent: 'center', 12 | flexDirection: 'column', 13 | }; 14 | 15 | const notAllowedBoxStyle = { 16 | ...boxStyle, 17 | backgroundColor: '#ffb5b5', 18 | }; 19 | 20 | const AccessGranted = () => ( 21 |
22 | Access Granted 23 |
24 | ); 25 | 26 | const AccessDenied = () => ( 27 |
28 | Access Denied 29 |
30 | ); 31 | 32 | const callbackFunction = ({ userPermissions, requiredPermissions }) => { 33 | console.log(` 34 | react-permissible: Access Denied; 35 | userPermissions: ${userPermissions} 36 | requiredPermissions: ${requiredPermissions} 37 | `); 38 | }; 39 | 40 | const CallbackComponent = Permissible( 41 | AccessGranted, 42 | ['ACCESS_DASHBOARD'], // userPermissions 43 | ['ACCESS_ADMIN'], // requiredPermissions 44 | callbackFunction, // no callback 45 | false, // all permissions have to match 46 | ); 47 | 48 | export { 49 | Permissible, 50 | PermissibleRender, 51 | AccessGranted, 52 | AccessDenied, 53 | CallbackComponent, 54 | }; 55 | -------------------------------------------------------------------------------- /docs-www/src/layouts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brainhubeu/react-permissible/f9122aa4c94555c84f71617980b7ccf709fbb618/docs-www/src/layouts/.gitkeep -------------------------------------------------------------------------------- /docs-www/src/pages/404.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const NotFoundPage = () => ( 4 |
5 |

NOT FOUND

6 |

You just hit a route that doesn't exist... the sadness.

7 |
8 | ); 9 | 10 | export default NotFoundPage; 11 | -------------------------------------------------------------------------------- /docs-www/src/pages/landing.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'gatsby-link'; 3 | 4 | import styles from './landing.module.scss'; 5 | 6 | const LandingPage = () => ( 7 |
8 |
9 |

react-permissible

10 |

Making the permission management for components easier.

11 |
12 | 13 | Get started! 14 | 15 |
16 |
17 |
18 | ); 19 | 20 | export default LandingPage; 21 | -------------------------------------------------------------------------------- /docs-www/src/pages/landing.module.scss: -------------------------------------------------------------------------------- 1 | @import './variables.scss'; 2 | 3 | .landing { 4 | text-align: center; 5 | display: flex; 6 | align-items: center; 7 | justify-content: center; 8 | min-height: calc(100vh - 2*#{$mainNavHeight} - 64px); 9 | } 10 | 11 | .landing__header { 12 | text-transform: uppercase; 13 | font-size: 3rem; 14 | letter-spacing: 2px; 15 | margin-bottom: 1rem; 16 | margin-top: 0; 17 | } 18 | 19 | .btn__wrapper { 20 | display: flex; 21 | justify-content: center; 22 | } 23 | 24 | .landing__btn { 25 | display: inline-block; 26 | background-color: $colorBHBg; 27 | text-decoration: none; 28 | color: $white; 29 | padding: 1rem 2rem; 30 | margin: 2rem; 31 | position: relative; 32 | overflow: hidden; 33 | 34 | span { 35 | position: relative; 36 | z-index: 1; 37 | } 38 | 39 | &:after, 40 | &:before { 41 | content: ''; 42 | position: absolute; 43 | top: 0; 44 | bottom: 0; 45 | width: 50%; 46 | background-color: $colorBHMain; 47 | transition: width .3s ease-out; 48 | } 49 | 50 | &:after { 51 | left: 0; 52 | } 53 | 54 | &:before { 55 | right: 0; 56 | } 57 | 58 | &:hover { 59 | color: $white; 60 | 61 | &:after, 62 | &:before { 63 | width: 0; 64 | } 65 | } 66 | } 67 | 68 | .landing__btn--alt { 69 | composes: landing__btn; 70 | background-color: transparent; 71 | color: rgba($colorBHBg, .7); 72 | transition: color .3s; 73 | 74 | &:after, 75 | &:before { 76 | background-color: transparent; 77 | border-style: solid; 78 | border-color: $colorBHMain; 79 | } 80 | 81 | &:after { 82 | border-width: 1px 0 1px 1px; 83 | } 84 | 85 | &:before { 86 | border-width: 1px 1px 1px 0; 87 | } 88 | 89 | &:hover { 90 | color: $colorBHMain; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /docs-www/src/pages/variables.scss: -------------------------------------------------------------------------------- 1 | $colorBHBg: #150940; 2 | $colorBHMain: #7b59ff; 3 | $white: #ffffff; 4 | 5 | $mainNavHeight: 80px; 6 | -------------------------------------------------------------------------------- /docs-www/src/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brainhubeu/react-permissible/f9122aa4c94555c84f71617980b7ccf709fbb618/docs-www/src/static/favicon.png -------------------------------------------------------------------------------- /docs-www/src/templates/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brainhubeu/react-permissible/f9122aa4c94555c84f71617980b7ccf709fbb618/docs-www/src/templates/.gitkeep -------------------------------------------------------------------------------- /docs-www/tools/deploy-github.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | GITHUB_REPO_NAME=$(basename -s .git `git config --get remote.origin.url`); 4 | 5 | PATH_PREFIX="/$GITHUB_REPO_NAME" npm run build 6 | 7 | # deploy to github pags 8 | node ./node_modules/.bin/gh-pages -d public 9 | -------------------------------------------------------------------------------- /docs-www/tools/seed.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CONFIG_FILE="gatsby-docs-kit.yml" 4 | DIR_NAME="docs" 5 | 6 | # Create docs directory 7 | if [ -d "../${DIR_NAME}" ]; then 8 | echo '"docs" directory already exists. Can not seed it. Remove it and try again.' 9 | exit 1; 10 | fi 11 | 12 | mkdir -p "../${DIR_NAME}" 13 | 14 | # Create gastsby docs kit config file if do not exists 15 | if [ ! -f "./${CONFIG_FILE}" ]; then 16 | echo "" > $CONFIG_FILE; 17 | fi 18 | 19 | # Create example Mk 20 | cp ./tools/seed/example.md ../${DIR_NAME}/example.md 21 | 22 | # Seeding gastsby docs kit config file 23 | echo "- title: Home 24 | dir: ../docs 25 | url: docs 26 | file: example.md" >> $CONFIG_FILE; 27 | 28 | # Done 29 | echo "Done!" -------------------------------------------------------------------------------- /docs-www/tools/seed/example.md: -------------------------------------------------------------------------------- 1 | # CLI Commands 2 | 3 | ### Running an app 4 | 1. running an application on locally 5 | ```bash 6 | npm run develop # or yarn develop 7 | ``` 8 | 9 | Gatsby will start a hot-reloading development environment accessible at [http://localhost:8000](http://localhost:8000) 10 | 11 | 2. preparing optimized production build 12 | ```bash 13 | npm run build 14 | ``` 15 | 16 | 3. serving locally production build 17 | ```bash 18 | npm run serve 19 | ``` 20 | 21 | > Note that your site by default will be available on [http://localhost:9000](http://localhost:9000) 22 | 23 | 4. deploying to github pages 24 | ```bash 25 | npm run deploy:gh 26 | ``` 27 | 28 | > See more [HERE](../getting-started/publishing.md) 29 | 30 | -------------------------------------------------------------------------------- /docs/examples/callback.md: -------------------------------------------------------------------------------- 1 | # Callback function 2 | 3 | Callback function passed to a `Permissible` Higher Order Component will be called whenever the permissions do not match. This might be combined with the `oneperm` parameter. **Open the Console** to see a callback message. 4 | 5 | ```javascript 6 | import { Permissible } from '@brainhubeu/react-permissible'; 7 | 8 | const callbackFunction = ({ userPermissions, requiredPermissions }) => { 9 | console.log(` 10 | react-permissible: Access Denied 11 | userPermissions: ${userPermissions} 12 | requiredPermissions: ${requiredPermissions} 13 | `); 14 | }; 15 | 16 | const CallbackComponent = Permissible( 17 | AccessGranted, 18 | ['ACCESS_DASHBOARD'], // userPermissions 19 | ['ACCESS_ADMIN'], // requiredPermissions 20 | callbackFunction, 21 | ); 22 | ``` 23 | 24 | ```jsx render 25 | 26 | ``` 27 | -------------------------------------------------------------------------------- /docs/examples/oneMatch.md: -------------------------------------------------------------------------------- 1 | # Render a component when one of the permissions matches 2 | 3 | If at least one permission from the `userPermissions` array exists in the `requiredPermissions` array, the component is rendered. 4 | 5 | ```jsx render 6 | 11 | 12 | 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/examples/permissionsMatch.md: -------------------------------------------------------------------------------- 1 | # Render a component when all the permissions match 2 | 3 | If all the permissions in the `userPermissions` array match the `requiredPermissions`, the component is rendered. 4 | 5 | ```jsx render 6 | 10 | 11 | 12 | ``` 13 | -------------------------------------------------------------------------------- /docs/examples/renderOtherwise.md: -------------------------------------------------------------------------------- 1 | # Render another component if the permissions do not match 2 | 3 | If permissions in the `userPermissions` do not match the `requiredPermissions`, the component is not rendered, and another one is rendered instead. 4 | 5 | ```jsx render 6 | } 10 | > 11 | 12 | 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/getting-started/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | **react-permissible** is designed as an npm package. Installation is as simple as with every package out there: 4 | 5 | `npm i @brainhubeu/react-permissible` 6 | 7 | or if you use Yarn: 8 | 9 | `yarn add @brainhubeu/react-permissible`. 10 | -------------------------------------------------------------------------------- /docs/getting-started/overview.md: -------------------------------------------------------------------------------- 1 | # react-permissible 2 | 3 | #### Making the permission management for components easier. 4 | 5 | **react-permissible** is a React Component allowing you to: 6 | * manage visibility of particular components depending on user's permissions. 7 | * replacing a particular component when the user isn't permitted to see it. 8 | * manage access to particular view depending on user's permissions. 9 | * firing a callback when the user isn't allowed to access the component/route. 10 | 11 | ## Why 12 | Currently there's a lack of simple permission management components for React. The existing components are either over-engineered (full ACL support etc.), or limited to role-based management. **react-permissible** is simple at it's core and solves only one problem - accessing the particular component if the necessary permissions are met, hide or render another component otherwise. 13 | -------------------------------------------------------------------------------- /docs/usage/basic.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | You can use react-permissible in two ways: as an ordinary component or as a Higher Order Component. Each approach allows you to solve the permission-based rendering a little bit differently. 4 | 5 | ## Use as an ordinary component with props: 6 | ```javascript 7 | import { PermissibleRender } from '@brainhubeu/react-permissible'; 8 | 9 | ... 10 | 11 | render() { 12 | return ( 13 | 19 | 20 | 21 | ); 22 | } 23 | ``` 24 | 25 | Where: 26 | 27 | * `userPermissions`: array of permissions set for current user 28 | * `requiredPermissions`: array of required permissions 29 | * `RestrictedComponent`: component to render 30 | 31 | There are also optional props available: 32 | 33 | * `oneperm`: boolean determining that only one of required permissions will be necessary (boolean) 34 | * `renderOtherwise`: another component to be rendered if the permissions do not match (the user isn't permitted). 35 | -------------------------------------------------------------------------------- /docs/usage/hoc.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | You can use react-permissible in two ways: as an ordinary component or as a Higher Order Component. Each approach allows you to solve the permission-based rendering a little bit differently. 4 | 5 | 6 | ## Usage as a Higher Order Component: 7 | ```javascript 8 | import { Permissible } from '@brainhubeu/react-permissible'; 9 | 10 | ... 11 | 12 | function callbackFunction({ userPermissions, requiredPermissions }) { 13 | // do something 14 | } 15 | 16 | const RestrictedComponent = ( 17 |

Restricted component

18 | ); 19 | 20 | const PermissibleComponent = Permissible( 21 | RestrictedComponent, 22 | userPermissions, 23 | requiredPermissions, 24 | callbackFunction, 25 | oneperm, 26 | ); 27 | 28 | render() { 29 | 30 | } 31 | ``` 32 | 33 | Where: 34 | 35 | * `RestrictedComponent`: a component to render 36 | * `userPermissions`: an array of permissions set for current user 37 | * `requiredPermissions`: an array of required permissions 38 | * `oneperm`: boolean determining that only one of required permissions will be necessary instead of requiring all passed permissions (optional) 39 | -------------------------------------------------------------------------------- /lib/react-permissible.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("react")):"function"==typeof define&&define.amd?define("react-permissible",["react"],e):"object"==typeof exports?exports["react-permissible"]=e(require("react")):t["react-permissible"]=e(t.React)}("undefined"!=typeof self?self:this,function(t){return function(t){function e(n){if(r[n])return r[n].exports;var o=r[n]={i:n,l:!1,exports:{}};return t[n].call(o.exports,o,o.exports,e),o.l=!0,o.exports}var r={};return e.m=t,e.c=r,e.d=function(t,r,n){e.o(t,r)||Object.defineProperty(t,r,{configurable:!1,enumerable:!0,get:n})},e.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(r,"a",r),r},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=8)}([function(t,e){function r(){throw new Error("setTimeout has not been defined")}function n(){throw new Error("clearTimeout has not been defined")}function o(t){if(s===setTimeout)return setTimeout(t,0);if((s===r||!s)&&setTimeout)return s=setTimeout,setTimeout(t,0);try{return s(t,0)}catch(e){try{return s.call(null,t,0)}catch(e){return s.call(this,t,0)}}}function i(t){if(l===clearTimeout)return clearTimeout(t);if((l===n||!l)&&clearTimeout)return l=clearTimeout,clearTimeout(t);try{return l(t)}catch(e){try{return l.call(null,t)}catch(e){return l.call(this,t)}}}function u(){h&&y&&(h=!1,y.length?d=y.concat(d):v=-1,d.length&&a())}function a(){if(!h){var t=o(u);h=!0;for(var e=d.length;e;){for(y=d,d=[];++v1)for(var r=1;r-1}function o(t,e,r){for(var n=-1,o=t?t.length:0;++n-1}function P(t,e){var r=this.__data__,n=M(r,t);return n<0?r.push([t,e]):r[n][1]=e,this}function E(t){var e=-1,r=t?t.length:0;for(this.clear();++e=120&&h.length>=120)?new C(l&&h):void 0}h=t[0];var v=-1,b=p[0];t:for(;++v-1&&t%1==0&&t<=Q}function G(t){var e=typeof t;return!!t&&("object"==e||"function"==e)}function H(t){return!!t&&"object"==typeof t}var K="__lodash_hash_undefined__",Q=9007199254740991,X="[object Function]",Z="[object GeneratorFunction]",tt=/[\\^$.*+?()[\]{}|]/g,et=/^\[object .+?Constructor\]$/,rt="object"==typeof e&&e&&e.Object===Object&&e,nt="object"==typeof self&&self&&self.Object===Object&&self,ot=rt||nt||Function("return this")(),it=Array.prototype,ut=Function.prototype,at=Object.prototype,ct=ot["__core-js_shared__"],ft=function(){var t=/[^.]+$/.exec(ct&&ct.keys&&ct.keys.IE_PROTO||"");return t?"Symbol(src)_1."+t:""}(),st=ut.toString,lt=at.hasOwnProperty,pt=at.toString,yt=RegExp("^"+st.call(lt).replace(tt,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),dt=it.splice,ht=Math.max,vt=Math.min,bt=D(ot,"Map"),_t=D(Object,"create");y.prototype.clear=d,y.prototype.delete=h,y.prototype.get=v,y.prototype.has=b,y.prototype.set=_,m.prototype.clear=g,m.prototype.delete=w,m.prototype.get=O,m.prototype.has=j,m.prototype.set=P,E.prototype.clear=S,E.prototype.delete=x,E.prototype.get=T,E.prototype.has=$,E.prototype.set=k,C.prototype.add=C.prototype.push=A,C.prototype.has=R;var mt=function(t,e){return e=ht(void 0===e?t.length-1:e,0),function(){for(var n=arguments,o=-1,i=ht(n.length-e,0),u=Array(i);++o-1}function o(t,e,r){for(var n=-1,o=t?t.length:0;++n-1}function E(t,e){var r=this.__data__,n=I(r,t);return n<0?r.push([t,e]):r[n][1]=e,this}function S(t){var e=-1,r=t?t.length:0;for(this.clear();++e=Z&&(c=l,f=!1,e=new A(e));t:for(;++a0&&r(c)?e>1?F(c,e-1,r,n,o):u(o,c):n||(o[o.length]=c)}return o}function V(t){return!(!Q(t)||z(t))&&(H(t)||y(t)?_t:ut).test(U(t))}function D(t,e){var r=t.__data__;return W(e)?r["string"==typeof e?"string":"hash"]:r.map}function q(t,e){var r=p(t,e);return V(r)?r:void 0}function L(t){return xt(t)||J(t)||!!(Ot&&t&&t[Ot])}function W(t){var e=typeof t;return"string"==e||"number"==e||"symbol"==e||"boolean"==e?"__proto__"!==t:null===t}function z(t){return!!dt&&dt in t}function U(t){if(null!=t){try{return ht.call(t)}catch(t){}try{return t+""}catch(t){}}return""}function Y(t,e){return t===e||t!==t&&e!==e}function J(t){return G(t)&&vt.call(t,"callee")&&(!gt.call(t,"callee")||bt.call(t)==rt)}function B(t){return null!=t&&K(t.length)&&!H(t)}function G(t){return X(t)&&B(t)}function H(t){var e=Q(t)?bt.call(t):"";return e==nt||e==ot}function K(t){return"number"==typeof t&&t>-1&&t%1==0&&t<=et}function Q(t){var e=typeof t;return!!t&&("object"==e||"function"==e)}function X(t){return!!t&&"object"==typeof t}var Z=200,tt="__lodash_hash_undefined__",et=9007199254740991,rt="[object Arguments]",nt="[object Function]",ot="[object GeneratorFunction]",it=/[\\^$.*+?()[\]{}|]/g,ut=/^\[object .+?Constructor\]$/,at="object"==typeof e&&e&&e.Object===Object&&e,ct="object"==typeof self&&self&&self.Object===Object&&self,ft=at||ct||Function("return this")(),st=Array.prototype,lt=Function.prototype,pt=Object.prototype,yt=ft["__core-js_shared__"],dt=function(){var t=/[^.]+$/.exec(yt&&yt.keys&&yt.keys.IE_PROTO||"");return t?"Symbol(src)_1."+t:""}(),ht=lt.toString,vt=pt.hasOwnProperty,bt=pt.toString,_t=RegExp("^"+ht.call(vt).replace(it,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),mt=ft.Symbol,gt=pt.propertyIsEnumerable,wt=st.splice,Ot=mt?mt.isConcatSpreadable:void 0,jt=Math.max,Pt=q(ft,"Map"),Et=q(Object,"create");d.prototype.clear=h,d.prototype.delete=v,d.prototype.get=b,d.prototype.has=_,d.prototype.set=m,g.prototype.clear=w,g.prototype.delete=O,g.prototype.get=j,g.prototype.has=P,g.prototype.set=E,S.prototype.clear=x,S.prototype.delete=T,S.prototype.get=$,S.prototype.has=k,S.prototype.set=C,A.prototype.add=A.prototype.push=R,A.prototype.has=M;var St=function(t,e){return e=jt(void 0===e?t.length-1:e,0),function(){for(var n=arguments,o=-1,i=jt(n.length-e,0),u=Array(i);++o1?e-1:0),n=1;n2?r-2:0),o=2;o1?"Invalid arguments supplied to oneOf, expected an array, got "+arguments.length+" arguments. A common mistake is to write oneOf(x, y, z) instead of oneOf([x, y, z]).":"Invalid argument supplied to oneOf, expected an array."),n)}function _(t){function e(e,r,n,o,i){if("function"!=typeof t)return new p("Property `"+i+"` of component `"+n+"` has invalid PropType notation inside objectOf.");var a=e[r],f=P(a);if("object"!==f)return new p("Invalid "+o+" `"+i+"` of type `"+f+"` supplied to `"+n+"`, expected an object.");for(var s in a)if(c(a,s)){var l=t(a,s,n,o,i+"."+s,u);if(l instanceof Error)return l}return null}return y(e)}function m(t){function r(e,r,n,o,i){for(var a=0;a=6.14.5" 7 | }, 8 | "scripts": { 9 | "lint": "esw webpack.config.* src tools test --color", 10 | "lint:types": "esw 'types/*.{ts,tsx}' 'types/tests/*.{ts,tsx}' --ext ts,tsx --color --format codeframe --config ./types/.eslintrc", 11 | "lint:types:watch": "npm run --silent lint:types -- --watch", 12 | "lint:types:fix": "npm run --silent lint:types -- --fix", 13 | "clean-dist": "rm -rf ./lib && mkdir lib", 14 | "prebuild": "npm run clean-dist", 15 | "build": "node tools/build.js", 16 | "test": "mocha tools/testSetup.js \"./test/**/*.js\"", 17 | "test:coverage": "NODE_PATH=example babel-node ./node_modules/.bin/isparta cover _mocha -- --require ./tools/testSetup.js \"./test/**/*.js\" && open coverage/lcov-report/index.html", 18 | "test:coverage:ci": "NODE_PATH=example babel-node ./node_modules/.bin/isparta cover _mocha --report lcovonly -- --require ./tools/testSetup.js \"./test/**/*.js\"", 19 | "test:typescript": "cd ./types/tests && npm install-test --no-audit", 20 | "precommit": "npm run lint && npm run lint:types" 21 | }, 22 | "author": "Krystian Kościelniak", 23 | "license": "MIT", 24 | "dependencies": { 25 | "gatsby-plugin-google-analytics": "2.3.2", 26 | "lodash.difference": "^4.5.0", 27 | "lodash.intersection": "^4.4.0" 28 | }, 29 | "devDependencies": { 30 | "autoprefixer": "^9.8.4", 31 | "babel-cli": "^6.26.0", 32 | "babel-core": "^6.26.3", 33 | "babel-eslint": "^10.1.0", 34 | "babel-loader": "^8.1.0", 35 | "babel-plugin-react-display-name": "^2.0.0", 36 | "babel-plugin-transform-decorators-legacy": "^1.3.5", 37 | "babel-plugin-transform-react-constant-elements": "^6.23.0", 38 | "babel-plugin-transform-react-remove-prop-types": "^0.4.24", 39 | "babel-polyfill": "^6.26.0", 40 | "babel-preset-env": "^1.7.0", 41 | "babel-preset-es2015": "^6.24.1", 42 | "babel-preset-latest": "^6.24.1", 43 | "babel-preset-react": "^6.24.1", 44 | "babel-preset-react-hmre": "^1.1.1", 45 | "babel-preset-stage-1": "^6.24.1", 46 | "babel-register": "^6.26.0", 47 | "chai": "^4.2.0", 48 | "chai-as-promised": "^7.1.1", 49 | "chai-enzyme": "^1.0.0-beta.1", 50 | "chalk": "^4.1.0", 51 | "coveralls": "^3.1.0", 52 | "enzyme": "^3.11.0", 53 | "enzyme-adapter-react-16": "^1.15.2", 54 | "eslint": "^7.3.1", 55 | "eslint-config-brainhub": "^1.13.0", 56 | "eslint-plugin-typescript": "^0.14.0", 57 | "eslint-watch": "^7.0.0", 58 | "html-webpack-plugin": "^4.3.0", 59 | "husky": "^4.2.5", 60 | "isparta": "^4.1.1", 61 | "jsdom": "^16.2.2", 62 | "mocha": "^8.0.1", 63 | "prop-types": "^15.7.2", 64 | "react": "^16.13.1", 65 | "react-dom": "^16.13.1", 66 | "react-test-renderer": "^16.13.1", 67 | "sinon": "^9.0.2", 68 | "sinon-chai": "^3.5.0", 69 | "typescript": "^3.9.6", 70 | "typescript-eslint-parser": "^21.0.2", 71 | "uglifyjs-webpack-plugin": "^2.2.0", 72 | "webpack": "^4.43.0" 73 | }, 74 | "peerDependencies": { 75 | "react": ">0.14.0 || >15.0.0", 76 | "react-dom": ">0.14.0 || >15.0.0" 77 | }, 78 | "keywords": [ 79 | "react", 80 | "react-permissions", 81 | "permissions", 82 | "permission-manager", 83 | "props", 84 | "callback", 85 | "users", 86 | "authentication", 87 | "optional-props", 88 | "authorization", 89 | "access-control" 90 | ], 91 | "repository": { 92 | "type": "git", 93 | "url": "https://github.com/brainhubeu/react-permissible" 94 | }, 95 | "main": "./lib/react-permissible.js", 96 | "types": "./types/react-permissible.d.ts" 97 | } 98 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "bumpVersion": "patch", 3 | "commitMessagePrefix": "[renovate] ", 4 | "groupName": "NPM dependencies", 5 | "labels": ["renovate"], 6 | "rangeStrategy": "bump", 7 | "branchPrefix": "fix/renovate/", 8 | "docker": { 9 | "major": { 10 | "enabled": true 11 | } 12 | }, 13 | "packageRules": [ 14 | { 15 | "groupName": "Docker dependencies", 16 | "managers": [ 17 | "circleci" 18 | ] 19 | }, 20 | { 21 | "groupName": "gatsby", 22 | "managerBranchPrefix": "gatsby", 23 | "packagePatterns": [ 24 | "gatsby" 25 | ], 26 | "rangeStrategy": "pin" 27 | } 28 | ], 29 | "schedule": [ 30 | "before 3am on the first day of the month" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /src/components/permissible.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import intersection from 'lodash.intersection'; 4 | import difference from 'lodash.difference'; 5 | 6 | export function Permissible( 7 | RestrictedComponent, 8 | userPermissions, 9 | requiredPermissions, 10 | callbackFunction, 11 | oneperm, 12 | ) { 13 | const permissionsStatus = oneperm 14 | ? intersection(userPermissions, requiredPermissions).length 15 | : difference(requiredPermissions, userPermissions).length === 0; 16 | 17 | class PermissibleHOC extends Component { 18 | static propTypes = { 19 | oneperm: PropTypes.bool, 20 | history: PropTypes.object, // eslint-disable-line react/forbid-prop-types 21 | }; 22 | 23 | constructor(props) { 24 | super(props); 25 | 26 | if (!permissionsStatus) { 27 | this.runCallback(); 28 | } 29 | } 30 | 31 | runCallback() { 32 | if (callbackFunction) { 33 | return callbackFunction({ 34 | userPermissions, 35 | requiredPermissions, 36 | }, 37 | this.props.history); 38 | } 39 | return; 40 | } 41 | 42 | render() { 43 | if (permissionsStatus) { 44 | return ; 45 | } 46 | return null; 47 | } 48 | } 49 | return PermissibleHOC; 50 | } 51 | -------------------------------------------------------------------------------- /src/components/permissibleRender.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import intersection from 'lodash.intersection'; 4 | import difference from 'lodash.difference'; 5 | 6 | export class PermissibleRender extends Component { 7 | static propTypes = { 8 | oneperm: PropTypes.bool, 9 | userPermissions: PropTypes.arrayOf(PropTypes.string).isRequired, 10 | requiredPermissions: PropTypes.arrayOf(PropTypes.string).isRequired, 11 | children: PropTypes.element.isRequired, 12 | renderOtherwise: PropTypes.element, 13 | }; 14 | 15 | checkPermissions() { 16 | const { userPermissions, requiredPermissions, oneperm } = this.props; 17 | 18 | if (oneperm) { 19 | return intersection(userPermissions, requiredPermissions).length; 20 | } 21 | 22 | return difference(requiredPermissions, userPermissions).length === 0; 23 | } 24 | 25 | render() { 26 | const { children, userPermissions, requiredPermissions, renderOtherwise } = this.props; 27 | 28 | if (!children || !userPermissions || !requiredPermissions) { 29 | return null; 30 | } 31 | 32 | if (this.checkPermissions()) { 33 | return children; 34 | } else if (renderOtherwise) { 35 | return renderOtherwise; 36 | } 37 | return null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { Permissible } from './components/permissible'; 2 | import { PermissibleRender } from './components/permissibleRender'; 3 | 4 | export { 5 | Permissible, 6 | PermissibleRender, 7 | }; 8 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | env: { 3 | es6: true, 4 | browser: true, 5 | node: true, 6 | mocha: true, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /test/accessible.component.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const Accessible = ({ permission, oneperm }) => ( 5 |
6 |

7 | {oneperm ? 'One of' : 'Whole set of'} {permission} is necessary to see this component. 8 |

9 |
10 | ); 11 | 12 | Accessible.propTypes = { 13 | permission: PropTypes.string, 14 | oneperm: PropTypes.bool, 15 | }; 16 | 17 | export default Accessible; 18 | -------------------------------------------------------------------------------- /test/permissible.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import chai from 'chai'; 4 | import chaiEnzyme from 'chai-enzyme'; 5 | import { JSDOM } from 'jsdom'; 6 | 7 | import { Permissible } from '../src/components/permissible'; 8 | 9 | import AccessedComponent from './accessible.component'; 10 | 11 | const { document } = (new JSDOM('')).window; 12 | global.document = document; 13 | global.window = document.defaultView; 14 | 15 | chai.use(chaiEnzyme()); 16 | chai.should(); 17 | 18 | describe('Permissible HOC', () => { 19 | it('doesn\'t run a callback function if the permissions are right', done => { 20 | let err = null; 21 | 22 | const AccessibleRoute = Permissible( 23 | () => , 24 | ['MATCHING_PERMISSIONS'], 25 | ['MATCHING_PERMISSIONS'], 26 | () => { 27 | err = new Error('Callback function called.'); 28 | }, 29 | ); 30 | 31 | shallow( 32 | , 33 | ); 34 | 35 | done(err); 36 | }); 37 | 38 | it('doesn\'t run a callback function if `requiredPermissions` and `userPermissions` are both empty', done => { 39 | let err = null; 40 | 41 | const AccessibleRoute = Permissible( 42 | () => , 43 | [], 44 | [], 45 | () => { 46 | err = new Error('Callback function called.'); 47 | }, 48 | ); 49 | 50 | shallow( 51 | , 52 | ); 53 | 54 | done(err); 55 | }); 56 | 57 | it('doesn\'t run a callback function if only `requiredPermissions` are empty', done => { 58 | let err = null; 59 | 60 | const AccessibleRoute = Permissible( 61 | () => , 62 | ['SOME_PERMISSION'], 63 | [], 64 | () => { 65 | err = new Error('Callback function called.'); 66 | }, 67 | ); 68 | 69 | shallow( 70 | , 71 | ); 72 | 73 | done(err); 74 | }); 75 | 76 | it('runs a callback function if the permissions don\'t match', done => { 77 | const AccessibleRoute = Permissible( 78 | () => , 79 | ['MATCHING_PERMISSIONS'], 80 | ['UNMATCHING_PERMISSIONS'], 81 | () => { 82 | done(); 83 | }, 84 | ); 85 | 86 | shallow( 87 | , 88 | ); 89 | }); 90 | 91 | it('doesn\'t run a callback function if the user has one of necessary permissions and `oneperm` is `true`', done => { 92 | let err = null; 93 | 94 | const AccessibleRoute = Permissible( 95 | () => , 96 | ['REQUIRED_PERMISSION'], 97 | ['REQUIRED_PERMISSION', 'ANOTHER_PERMISSION'], 98 | () => { 99 | err = new Error('Callback function called.'); 100 | }, 101 | true, 102 | ); 103 | 104 | shallow( 105 | , 106 | ); 107 | 108 | done(err); 109 | }); 110 | 111 | it('runs a callback function if the user has one of necessary permissions and `oneperm` is `false`', done => { 112 | const AccessibleRoute = Permissible( 113 | () => , 114 | ['REQUIRED_PERMISSION'], 115 | ['REQUIRED_PERMISSION', 'ANOTHER_PERMISSION'], 116 | () => { 117 | done(); 118 | }, 119 | false, 120 | ); 121 | 122 | shallow( 123 | , 124 | ); 125 | }); 126 | 127 | it('doesn\'t run a callback function if it is not defined', done => { 128 | const AccessibleRoute = Permissible( 129 | () => , 130 | ['REQUIRED_PERMISSION'], 131 | ['REQUIRED_PERMISSION', 'ANOTHER_PERMISSION'], 132 | null, 133 | false, 134 | ); 135 | 136 | shallow( 137 | , 138 | ); 139 | 140 | done(); 141 | }); 142 | }); 143 | -------------------------------------------------------------------------------- /test/permissibleRender.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { mount } from 'enzyme'; 3 | import chai from 'chai'; 4 | import chaiEnzyme from 'chai-enzyme'; 5 | import { JSDOM } from 'jsdom'; 6 | 7 | import { PermissibleRender } from '../src/components/permissibleRender'; 8 | 9 | const should = chai.should(); 10 | 11 | const { document } = (new JSDOM('')).window; 12 | global.document = document; 13 | global.window = document.defaultView; 14 | 15 | chai.use(chaiEnzyme()); 16 | chai.should(); 17 | 18 | describe('PermissibleRender', () => { 19 | const ChildComponent = () => ( 20 |
21 | Child component 22 |
23 | ); 24 | 25 | const NotAllowedComponent = () => ( 26 |
27 | Not allowed component 28 |
29 | ); 30 | 31 | it('doesn\'t render a if `userPermissions` prop is not set', () => { 32 | const props = { 33 | requiredPermissions: [], 34 | }; 35 | 36 | const mountedComponent = mount( 37 | 38 | 39 | , 40 | ); 41 | 42 | const searchedElement = mountedComponent.find('ChildComponent'); 43 | searchedElement.length.should.be.equal(0); 44 | }); 45 | 46 | it('doesn\'t render a if `requiredPermissions` prop is not set', () => { 47 | const props = { 48 | userPermissions: [], 49 | }; 50 | 51 | const mountedComponent = mount( 52 | 53 | 54 | , 55 | ); 56 | 57 | const searchedElement = mountedComponent.find('ChildComponent'); 58 | searchedElement.length.should.be.equal(0); 59 | }); 60 | 61 | it('renders nothing if `children` prop is not set', () => { 62 | const props = { 63 | userPermissions: [], 64 | }; 65 | 66 | const mountedComponent = mount( 67 | , 68 | ); 69 | 70 | should.not.exist(mountedComponent.find('PermissibleRender').html()); 71 | }); 72 | 73 | it('renders a if user permissions and required permissions are both empty', () => { 74 | const props = { 75 | userPermissions: [], 76 | requiredPermissions: [], 77 | }; 78 | 79 | const mountedComponent = mount( 80 | 81 | 82 | , 83 | ); 84 | 85 | const searchedElement = mountedComponent.find('ChildComponent'); 86 | searchedElement.length.should.be.equal(1); 87 | }); 88 | 89 | it('renders a if only required permissions are empty', () => { 90 | const props = { 91 | userPermissions: ['SOME_PERMISSION'], 92 | requiredPermissions: [], 93 | }; 94 | 95 | const mountedComponent = mount( 96 | 97 | 98 | , 99 | ); 100 | 101 | const searchedElement = mountedComponent.find('ChildComponent'); 102 | searchedElement.length.should.be.equal(1); 103 | }); 104 | 105 | it('doesn\'t render a if there is a permission mismatch', () => { 106 | const props = { 107 | userPermissions: ['REQUIRED_PERMISSION'], 108 | requiredPermissions: ['ANOTHER_PERMISSION'], 109 | }; 110 | 111 | const mountedComponent = mount( 112 | 113 | 114 | , 115 | ); 116 | 117 | const searchedElement = mountedComponent.find('ChildComponent'); 118 | searchedElement.length.should.be.equal(0); 119 | }); 120 | 121 | it('renders a if the user has required permission', () => { 122 | const props = { 123 | userPermissions: ['REQUIRED_PERMISSION'], 124 | requiredPermissions: ['REQUIRED_PERMISSION'], 125 | }; 126 | 127 | const mountedComponent = mount( 128 | 129 | 130 | , 131 | ); 132 | 133 | const searchedElement = mountedComponent.find('ChildComponent'); 134 | searchedElement.length.should.be.greaterThan(0); 135 | }); 136 | 137 | it('renders a if the user doesn\'t have required permissions and renderOtherwise is given', () => { 138 | const props = { 139 | userPermissions: ['REQUIRED_PERMISSION'], 140 | requiredPermissions: ['NOT_REQUIRED_PERMISSION'], 141 | renderOtherwise: , 142 | }; 143 | 144 | const mountedComponent = mount( 145 | 146 | 147 | , 148 | ); 149 | 150 | const searchedElement = mountedComponent.find('NotAllowedComponent'); 151 | searchedElement.length.should.be.greaterThan(0); 152 | }); 153 | 154 | it('renders a if the user has one of necessary conditions when `oneperm` prop is defined', () => { 155 | const props = { 156 | userPermissions: ['ANOTHER_PERMISSION'], 157 | requiredPermissions: ['REQUIRED_PERMISSION', 'ANOTHER_PERMISSION'], 158 | oneperm: true, 159 | }; 160 | 161 | const mountedComponent = mount( 162 | 163 | 164 | , 165 | ); 166 | 167 | const searchedElement = mountedComponent.find('ChildComponent'); 168 | searchedElement.length.should.be.greaterThan(0); 169 | }); 170 | 171 | it('doesn\'t render a if the user doesn\'t have all of necessary permissions when `oneperm` prop is explicitly set to false', () => { 172 | const props = { 173 | userPermissions: ['REQUIRED_PERMISSION'], 174 | requiredPermissions: ['REQUIRED_PERMISSION', 'ANOTHER_PERMISSION'], 175 | oneperm: false, 176 | }; 177 | 178 | const mountedComponent = mount( 179 | 180 | 181 | , 182 | ); 183 | 184 | const searchedElement = mountedComponent.find('ChildComponent'); 185 | searchedElement.length.should.be.equal(0); 186 | }); 187 | }); 188 | -------------------------------------------------------------------------------- /tools/build.js: -------------------------------------------------------------------------------- 1 | // More info on Webpack's Node API here: https://webpack.github.io/docs/node.js-api.html 2 | // Allowing console calls below since this is a build file. 3 | /* eslint-disable no-console */ 4 | const chalk = require('chalk'); 5 | const webpack = require('webpack'); 6 | 7 | const config = require('../webpack.config.prod'); 8 | 9 | const chalkConfig = { 10 | chalkError: chalk.red, 11 | chalkSuccess: chalk.green, 12 | chalkWarning: chalk.yellow, 13 | chalkProcessing: chalk.blue, 14 | }; 15 | 16 | const { chalkError, chalkSuccess, chalkWarning, chalkProcessing } = chalkConfig; 17 | 18 | process.env.NODE_ENV = 'production'; // this assures React is built in prod mode and that the Babel dev config doesn't apply. 19 | 20 | console.log(chalkProcessing('Generating minified bundle. This will take a moment...')); 21 | 22 | webpack(config).run((error, stats) => { 23 | if (error) { // so a fatal error occurred. Stop here. 24 | console.log(chalkError(error)); 25 | return 1; 26 | } 27 | 28 | const jsonStats = stats.toJson(); 29 | 30 | if (jsonStats.hasErrors) { 31 | return jsonStats.errors.map(error => console.log(chalkError(error))); 32 | } 33 | 34 | if (jsonStats.hasWarnings) { 35 | console.log(chalkWarning('Webpack generated the following warnings: ')); 36 | jsonStats.warnings.map(warning => console.log(chalkWarning(warning))); 37 | } 38 | 39 | console.log(`Webpack stats: ${stats}`); 40 | 41 | // if we got this far, the build succeeded. 42 | console.log(chalkSuccess('Your app is compiled in production mode in /dist. It\'s ready to roll!')); 43 | 44 | return 0; 45 | }); 46 | -------------------------------------------------------------------------------- /tools/testSetup.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'test'; 2 | 3 | // Disable webpack-specific features for tests since 4 | // Mocha doesn't know what to do with them. 5 | /* istanbul ignore next */ 6 | ['.css', '.scss', '.png', '.jpg'].forEach(ext => { 7 | require.extensions[ext] = () => null; 8 | }); 9 | 10 | // add required globals 11 | /* eslint-disable no-empty-function */ 12 | /* istanbul ignore next */ 13 | global.logger = function() {}; 14 | /* istanbul ignore next */ 15 | global.logger.info = function() {}; 16 | /* istanbul ignore next */ 17 | global.logger.apiSuccess = function() {}; 18 | /* istanbul ignore next */ 19 | global.logger.apiError = function() {}; 20 | /* istanbul ignore next */ 21 | global.logger.warn = function() {}; 22 | /* eslint-enable */ 23 | 24 | // Register babel so that it will transpile ES6 to ES5 25 | // before our tests run. 26 | require('babel-register')(); 27 | require('babel-polyfill'); 28 | 29 | const chai = require('chai'); 30 | const sinonChai = require('sinon-chai'); 31 | const chaiAsPromised = require('chai-as-promised'); 32 | const enzyme = require('enzyme'); 33 | const Adapter = require('enzyme-adapter-react-16'); 34 | 35 | enzyme.configure({ adapter: new Adapter() }); 36 | 37 | chai.use(sinonChai); 38 | chai.use(chaiAsPromised); 39 | -------------------------------------------------------------------------------- /types/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "typescript-eslint-parser", 3 | "plugins": ["eslint-plugin-typescript"], 4 | "extends": "../.eslintrc", 5 | "rules": { 6 | "comma-dangle":"off", 7 | "no-unused-vars": "off", 8 | "import/no-namespace": "off", 9 | "typescript/no-unused-vars": "error" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /types/react-permissible.d.ts: -------------------------------------------------------------------------------- 1 | import { Component, ComponentState, ComponentType, ReactNode, ReactNodeArray, StaticLifecycle, ValidationMap, } from 'react'; 2 | 3 | declare module '@brainhubeu/react-permissible' { 4 | type Permissions = string[]; 5 | 6 | type Children = ReactNode | ReactNodeArray; 7 | 8 | export interface UserAndRequiredPermissions { 9 | userPermissions: Permissions 10 | requiredPermissions: Permissions 11 | } 12 | 13 | export interface PermissibleRenderProps extends UserAndRequiredPermissions { 14 | oneperm?: boolean 15 | children: Children 16 | renderOtherwise?: Children 17 | } 18 | 19 | export class PermissibleRender extends Component { 20 | checkPermissions(): boolean 21 | } 22 | 23 | export function Permissible( 24 | RestrictedComponent: ComponentType, 25 | userPermissions: Permissions, 26 | requiredPermissions: Permissions, 27 | callbackFunction?: ({ userPermissions, requiredPermissions, }: UserAndRequiredPermissions) => void, 28 | oneperm?: boolean, 29 | ): PermissibleHOC 30 | 31 | interface PermissibleHOC extends StaticLifecycle { 32 | new (props: Props, context?: any): Component & TE 33 | propTypes?: ValidationMap 34 | contextTypes?: ValidationMap 35 | childContextTypes?: ValidationMap 36 | defaultProps?: Partial 37 | displayName?: string 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /types/tests/.gitignore: -------------------------------------------------------------------------------- 1 | brainhubeu-react-permissible-*.tgz 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /types/tests/.npmrc: -------------------------------------------------------------------------------- 1 | update-notifier=false 2 | audit=false 3 | prefer-offline=true 4 | -------------------------------------------------------------------------------- /types/tests/Permissible.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from 'react-dom'; 3 | // eslint-disable-next-line import/no-unresolved, import/no-extraneous-dependencies, /* this is what we're testing */ 4 | import { Permissible, UserAndRequiredPermissions } from '@brainhubeu/react-permissible'; 5 | 6 | function callbackFunction({ userPermissions, requiredPermissions }: UserAndRequiredPermissions) { 7 | // eslint-disable-next-line no-console 8 | console.info(` 9 | react-permissible: Access Denied 10 | userPermissions: ${userPermissions} 11 | requiredPermissions: ${requiredPermissions} 12 | `); 13 | } 14 | 15 | const AccessGranted = ({ message }: { message: string, }) => <>AccessGranted {message}; 16 | 17 | const RestrictedComponentWithCallback = Permissible( 18 | AccessGranted, 19 | ['ACCESS_DASHBOARD'], // userPermissions 20 | ['ACCESS_ADMIN'], // requiredPermissions 21 | callbackFunction, 22 | ); 23 | 24 | render(, document.createElement('div')); 25 | -------------------------------------------------------------------------------- /types/tests/PermissibleRender.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from 'react-dom'; 3 | // eslint-disable-next-line import/no-unresolved, import/no-extraneous-dependencies, /* this is what we're testing */ 4 | import { PermissibleRender, PermissibleRenderProps } from '@brainhubeu/react-permissible'; 5 | 6 | const VIEW_PERMISSION = 'VIEW'; 7 | 8 | const permissibleRenderTestProps: PermissibleRenderProps = { 9 | oneperm: false, 10 | children: 'restricted content', 11 | userPermissions: [VIEW_PERMISSION], 12 | requiredPermissions: [VIEW_PERMISSION], 13 | renderOtherwise: 'ACCESS DENIED', 14 | }; 15 | 16 | render(, document.createElement('div')); 17 | -------------------------------------------------------------------------------- /types/tests/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-permissible-typescript-tests", 3 | "version": "1.0.27", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/prop-types": { 8 | "version": "15.7.3", 9 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", 10 | "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" 11 | }, 12 | "@types/react": { 13 | "version": "16.9.41", 14 | "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.41.tgz", 15 | "integrity": "sha512-6cFei7F7L4wwuM+IND/Q2cV1koQUvJ8iSV+Gwn0c3kvABZ691g7sp3hfEQHOUBJtccl1gPi+EyNjMIl9nGA0ug==", 16 | "requires": { 17 | "@types/prop-types": "*", 18 | "csstype": "^2.2.0" 19 | } 20 | }, 21 | "@types/react-dom": { 22 | "version": "16.9.8", 23 | "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.8.tgz", 24 | "integrity": "sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA==", 25 | "requires": { 26 | "@types/react": "*" 27 | } 28 | }, 29 | "csstype": { 30 | "version": "2.6.10", 31 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.10.tgz", 32 | "integrity": "sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w==" 33 | }, 34 | "js-tokens": { 35 | "version": "4.0.0", 36 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 37 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 38 | }, 39 | "loose-envify": { 40 | "version": "1.4.0", 41 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 42 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 43 | "requires": { 44 | "js-tokens": "^3.0.0 || ^4.0.0" 45 | } 46 | }, 47 | "object-assign": { 48 | "version": "4.1.1", 49 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 50 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 51 | }, 52 | "prop-types": { 53 | "version": "15.7.2", 54 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", 55 | "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", 56 | "requires": { 57 | "loose-envify": "^1.4.0", 58 | "object-assign": "^4.1.1", 59 | "react-is": "^16.8.1" 60 | } 61 | }, 62 | "react": { 63 | "version": "16.13.1", 64 | "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", 65 | "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", 66 | "requires": { 67 | "loose-envify": "^1.1.0", 68 | "object-assign": "^4.1.1", 69 | "prop-types": "^15.6.2" 70 | } 71 | }, 72 | "react-dom": { 73 | "version": "16.13.1", 74 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz", 75 | "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==", 76 | "requires": { 77 | "loose-envify": "^1.1.0", 78 | "object-assign": "^4.1.1", 79 | "prop-types": "^15.6.2", 80 | "scheduler": "^0.19.1" 81 | } 82 | }, 83 | "react-is": { 84 | "version": "16.13.1", 85 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", 86 | "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" 87 | }, 88 | "scheduler": { 89 | "version": "0.19.1", 90 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", 91 | "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", 92 | "requires": { 93 | "loose-envify": "^1.1.0", 94 | "object-assign": "^4.1.1" 95 | } 96 | }, 97 | "typescript": { 98 | "version": "3.9.6", 99 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.6.tgz", 100 | "integrity": "sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==" 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /types/tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-permissible-typescript-tests", 3 | "version": "1.0.27", 4 | "description": "Test publishing the typings of react-permissible package", 5 | "scripts": { 6 | "clean:packed-package": "rm ./brainhubeu-react-permissible-*.tgz", 7 | "clean:node-modules": "rm -rf ./node_modules/", 8 | "preinstall": "npm run clean:packed-package && npm run clean:node-modules", 9 | "pack-parent-package-get-filename": "npm pack ../.. | grep \\.tgz", 10 | "postinstall": "npm install --no-save --no-audit `npm run --silent pack-parent-package-get-filename`", 11 | "test": "tsc --project .", 12 | "posttest": "echo 'TypeScript typings successfuly compiled!'" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/brainhubeu/react-permissible" 17 | }, 18 | "author": "Nick Ribal (https://about.me/nickribal)", 19 | "license": "MIT", 20 | "dependencies": { 21 | "@types/react": "^16.9.41", 22 | "@types/react-dom": "^16.9.8", 23 | "react": "^16.13.1", 24 | "react-dom": "^16.13.1", 25 | "typescript": "^3.9.6" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /types/tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es2015", 5 | "noEmit": true, 6 | "jsx": "react", 7 | "strict": true, 8 | "noUnusedLocals": true, 9 | "noUnusedParameters": true, 10 | "noImplicitReturns": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "moduleResolution": "node" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /webpack.config.prod.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 4 | 5 | module.exports = { 6 | externals: [ 7 | { 8 | 'react-dom': { 9 | root: 'ReactDOM', 10 | commonjs2: 'react-dom', 11 | commonjs: 'react-dom', 12 | amd: 'react-dom', 13 | }, 14 | }, 15 | { 16 | react: { 17 | root: 'React', 18 | commonjs2: 'react', 19 | commonjs: 'react', 20 | amd: 'react', 21 | }, 22 | }, 23 | ], 24 | resolve: { 25 | extensions: ['.js', '.jsx', '.json'], 26 | modules: [ 27 | 'node_modules', 28 | path.join(__dirname, 'src'), 29 | ], 30 | }, 31 | devtool: 'source-map', 32 | entry: './src/index.js', 33 | output: { 34 | filename: 'react-permissible.js', 35 | library: 'react-permissible', 36 | libraryTarget: 'umd', 37 | path: path.resolve(__dirname, 'lib'), 38 | umdNamedDefine: true, 39 | }, 40 | optimization: { 41 | minimizer: [ 42 | new UglifyJsPlugin({ 43 | sourceMap: false, 44 | }), 45 | ], 46 | }, 47 | module: { 48 | rules: [ 49 | { 50 | test: /\.jsx?$/, 51 | exclude: /node_modules/, 52 | use: ['babel-loader'], 53 | }, 54 | ], 55 | }, 56 | }; 57 | --------------------------------------------------------------------------------