├── .babelrc
├── .eslintrc
├── .github
└── workflows
│ └── npm-publish.yml
├── .gitignore
├── .prettierrc
├── LICENSE
├── README.md
├── package.json
├── src
├── hoc.tsx
├── react-ua.spec.tsx
└── react-ua.tsx
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-env",
4 | "@babel/preset-react",
5 | "@babel/preset-typescript"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["eslint:recommended", "plugin:react/recommended"],
3 | "plugins": ["react", "jest", "prettier"],
4 | "parser": "babel-eslint",
5 | "env": {
6 | "browser": true,
7 | "jest": true
8 | },
9 | "rules": {
10 | "prettier/prettier": "error",
11 | "no-useless-catch": "off",
12 | "react/prop-types": "off"
13 | },
14 | "parserOptions": {
15 | "sourceType": "module",
16 | "ecmaFeatures": {
17 | "jsx": true,
18 | "modules": true
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/.github/workflows/npm-publish.yml:
--------------------------------------------------------------------------------
1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
3 |
4 | name: Node.js Package
5 |
6 | on:
7 | release:
8 | types: [created]
9 |
10 | jobs:
11 | publish-npm:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v3
15 | - uses: actions/setup-node@v3
16 | with:
17 | node-version: 14
18 | registry-url: https://registry.npmjs.org/
19 | # - run: npm ci
20 | - run: yarn
21 | - run: npm t
22 | - run: npm run build
23 | - run: npm whoami && npm publish
24 | env:
25 | NODE_AUTH_TOKEN: ${{secrets.npm_token}}
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | yarn-error.log
4 | coverage
5 | .idea
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2,
3 | "singleQuote": true
4 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Antony Budianto
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-ua
2 |
3 | [](https://badge.fury.io/js/react-ua)
4 | [](https://travis-ci.org/antonybudianto/react-ua)
5 |
6 |
7 |
8 |
9 |
10 | React User Agent Component and Provider, SSR-ready, using new React Context API
11 |
12 | ## Requirement
13 |
14 | - React 16.8.0
15 |
16 | ## Features
17 |
18 | - Wrapper for [ua-parser-js](https://github.com/faisalman/ua-parser-js)
19 | - Using new React [Hooks API](https://reactjs.org/docs/hooks-faq.html)
20 | - Using new React [Context API](https://reactjs.org/docs/context.html)
21 | - SSR-ready
22 | - Unit-tested
23 |
24 | > Try it [live at StackBlitz](https://stackblitz.com/edit/demo-react-ua)
25 |
26 | ```js
27 | import React from 'react';
28 | import { UserAgentProvider, useUserAgent } from 'react-ua';
29 |
30 | const Comp = () => {
31 | const ua = useUserAgent();
32 | return OS: {ua.os.name}
;
33 | };
34 |
35 | const App = () => (
36 |
37 |
38 |
39 | );
40 |
41 | ReactDOM.render(, document.getElementById('#root'));
42 |
43 | // SSR
44 | const el = (
45 |
46 |
47 |
48 | );
49 |
50 | ReactDOMServer.renderToString(el);
51 | ```
52 |
53 | ## HOC (deprecated)
54 |
55 | ```tsx
56 | import { withUserAgent } from 'react-ua/hoc';
57 |
58 | const CompWithHoc = withUserAgent(({ ua }) => OS: {ua.os.name}
);
59 |
60 | const App = () => (
61 |
62 |
63 |
64 | );
65 | ```
66 |
67 | ## License
68 |
69 | MIT
70 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-ua",
3 | "version": "4.0.0",
4 | "description": "React User Agent component and provider with new React Context API",
5 | "main": "dist/react-ua.js",
6 | "source": "src/react-ua.js",
7 | "files": [
8 | "dist"
9 | ],
10 | "scripts": {
11 | "build": "babel --extensions=.ts,.tsx --config-file=./.babelrc src/. -d dist/. --ignore=**/*.spec.tsx",
12 | "dev": "babel --watch --extensions=.ts,.tsx --config-file=./.babelrc src/. -d dist/. --ignore=**/*.spec.tsx",
13 | "old-prerelease": "npm run build",
14 | "old-release": "npm publish --access public",
15 | "lint": "eslint src",
16 | "test": "jest",
17 | "test:ci": "jest --coverage",
18 | "prt": "prettier src/**/*.js --write"
19 | },
20 | "jest": {
21 | "testEnvironment": "jsdom"
22 | },
23 | "repository": {
24 | "type": "git",
25 | "url": "git+https://github.com/antonybudianto/react-ua.git"
26 | },
27 | "keywords": [
28 | "react",
29 | "user agent",
30 | "ua-parser-js"
31 | ],
32 | "author": "Antony Budianto ",
33 | "license": "MIT",
34 | "bugs": {
35 | "url": "https://github.com/antonybudianto/react-ua/issues"
36 | },
37 | "homepage": "https://github.com/antonybudianto/react-ua#readme",
38 | "peerDependencies": {
39 | "react": "^16.9.0 || ^17.0.0 || ^18.0.0"
40 | },
41 | "devDependencies": {
42 | "@babel/cli": "^7.20.7",
43 | "@babel/core": "^7.20.7",
44 | "@babel/preset-env": "^7.20.2",
45 | "@babel/preset-react": "^7.18.6",
46 | "@babel/preset-typescript": "^7.18.6",
47 | "@types/jest": "^29.2.5",
48 | "@testing-library/react": "13.0.0",
49 | "babel-eslint": "10.0.3",
50 | "babel-jest": "24.9.0",
51 | "eslint": "6.3.0",
52 | "eslint-plugin-jest": "22.17.0",
53 | "eslint-plugin-prettier": "^3.1.0",
54 | "eslint-plugin-react": "7.14.3",
55 | "jest": "24.9.0",
56 | "prettier": "2.6.2",
57 | "react": "18.2.0",
58 | "react-dom": "18.2.0"
59 | },
60 | "dependencies": {
61 | "ua-parser-js": "^0.7.24"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/hoc.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { UserAgent } from './react-ua';
3 |
4 | /**
5 | * @legacy Please migrate to use hooks
6 | * Legacy HOC
7 | */
8 | export const withUserAgent = (Comp) =>
9 | class UserAgentHoc extends React.PureComponent {
10 | // This method works with Next.js
11 | static async getInitialProps(ctx) {
12 | let initialProps = {};
13 |
14 | if (Comp.getInitialProps) {
15 | initialProps = await Comp.getInitialProps(ctx);
16 | }
17 |
18 | return initialProps;
19 | }
20 |
21 | render() {
22 | return {(ua) => };
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/src/react-ua.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { UserAgentProvider, UserAgent, useUserAgent } from './react-ua';
4 | import { withUserAgent } from './hoc';
5 | import PropTypes from 'prop-types';
6 |
7 | const mockUa =
8 | 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36';
9 |
10 | Object.defineProperty(
11 | window.navigator,
12 | 'userAgent',
13 | (function (_value) {
14 | return {
15 | get: function _get() {
16 | return _value;
17 | },
18 | set: function _set(v) {
19 | _value = v;
20 | },
21 | };
22 | })(mockUa)
23 | );
24 |
25 | afterEach(cleanup);
26 |
27 | describe('react-ua', () => {
28 | it('should render with default provider', () => {
29 | const el = (
30 |
31 | {(v) => Browser: {v.browser.name}
}
32 |
33 | );
34 | const { getByText } = render(el);
35 | getByText('Browser: Chrome');
36 | });
37 |
38 | it('should render with custom provider value', () => {
39 | const customUa =
40 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/13.2b11866 Mobile/16A366 Safari/605.1.15';
41 | const el = (
42 |
43 | {(v) => OS: {v.os.name}
}
44 |
45 | );
46 | const { getByText } = render(el);
47 | getByText('OS: iOS');
48 | });
49 |
50 | it('should render HOC component with default provider', () => {
51 | const Comp = ({ ua }) => Browser: {ua.browser.name}
;
52 | Comp.propTypes = {
53 | ua: PropTypes.object,
54 | };
55 |
56 | const CompWithHoc = withUserAgent(Comp);
57 | const el = (
58 |
59 |
60 |
61 | );
62 | const { getByText } = render(el);
63 | getByText('Browser: Chrome');
64 | });
65 |
66 | it('should render HOC component with custom provider value', () => {
67 | const customUa =
68 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/13.2b11866 Mobile/16A366 Safari/605.1.15';
69 |
70 | const Comp = ({ ua }) => OS: {ua.os.name}
;
71 | Comp.propTypes = {
72 | ua: PropTypes.object,
73 | };
74 |
75 | const CompWithHoc = withUserAgent(Comp);
76 | const el = (
77 |
78 |
79 |
80 | );
81 | const { getByText } = render(el);
82 | getByText('OS: iOS');
83 | });
84 |
85 | it('should render hook useUserAgent', () => {
86 | const customUa =
87 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/13.2b11866 Mobile/16A366 Safari/605.1.15';
88 |
89 | const CompWithHooks = () => {
90 | const ua = useUserAgent();
91 | return OS: {ua.os.name}
;
92 | };
93 | const el = (
94 |
95 |
96 |
97 | );
98 | const { getByText } = render(el);
99 | getByText('OS: iOS');
100 | });
101 | });
102 |
--------------------------------------------------------------------------------
/src/react-ua.tsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo } from 'react';
2 | import type { FC, ReactNode } from 'react';
3 | import UAParser from 'ua-parser-js';
4 |
5 | interface UserAgentProviderProps {
6 | value?: string;
7 | children: ReactNode;
8 | }
9 |
10 | const UserAgentContext = React.createContext({});
11 |
12 | export const UserAgent = UserAgentContext.Consumer;
13 |
14 | export const UserAgentProvider: FC = ({
15 | value,
16 | children,
17 | }) => {
18 | const initUA = useMemo(() => {
19 | return new UAParser(value).getResult();
20 | }, [value]);
21 |
22 | return (
23 |
24 | {children}
25 |
26 | );
27 | };
28 |
29 | export const useUserAgent = () => React.useContext(UserAgentContext);
30 |
--------------------------------------------------------------------------------