├── .prettierrc
├── src
├── lib
│ ├── index.js
│ ├── index.d.ts
│ ├── ReactWhatsapp.js
│ └── ReactWhatsapp.spec.js
└── dev
│ ├── index.js
│ ├── index.html
│ ├── App.js
│ └── index.css
├── .github
└── workflows
│ ├── npmpublish.yml
│ └── nodejs.yml
├── LICENSE
├── .gitignore
├── package.json
└── README.md
/.prettierrc:
--------------------------------------------------------------------------------
1 | { "singleQuote": true }
2 |
--------------------------------------------------------------------------------
/src/lib/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './ReactWhatsapp';
2 |
--------------------------------------------------------------------------------
/src/dev/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 | import './index.css';
5 |
6 | ReactDOM.render(, document.getElementById('root'));
7 |
--------------------------------------------------------------------------------
/src/dev/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 | React Whatsapp
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/lib/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | type Values = T[keyof T];
4 |
5 | type ObtainHTMLProps> =
6 | T extends React.DetailedHTMLProps ? Props : never;
7 |
8 | type ReactWhatsappProps = Values<{
9 | [Tag in keyof JSX.IntrinsicElements]: {
10 | element: Tag;
11 | number: string;
12 | message?: string;
13 | } & ObtainHTMLProps;
14 | }>;
15 |
16 | export default class ReactWhatsapp extends React.Component {}
17 |
--------------------------------------------------------------------------------
/.github/workflows/npmpublish.yml:
--------------------------------------------------------------------------------
1 | name: Node.js Package
2 |
3 | on:
4 | release:
5 | types: [created]
6 |
7 | jobs:
8 | npm-publish:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v2
12 | - uses: actions/setup-node@v1
13 | with:
14 | node-version: 12
15 | registry-url: https://registry.npmjs.org/
16 | - name: Install dependencies
17 | run: yarn install --frozen-lockfile
18 | - name: Build
19 | run: yarn build
20 | - name: Npm publish
21 | run: npm publish --access public
22 | env:
23 | NODE_AUTH_TOKEN: ${{secrets.npm_token}}
24 |
--------------------------------------------------------------------------------
/.github/workflows/nodejs.yml:
--------------------------------------------------------------------------------
1 | name: Node.js CI
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build_test:
7 | runs-on: ${{ matrix.os }}
8 | strategy:
9 | matrix:
10 | node-version: [10, 12, 14]
11 | os: [ubuntu-latest, windows-latest, macOS-latest]
12 | steps:
13 | - uses: actions/checkout@v2
14 | - name: Use Node.js ${{ matrix.node-version }}
15 | uses: actions/setup-node@v1
16 | with:
17 | node-version: ${{ matrix.node-version }}
18 | - name: Install dependencies
19 | run: yarn install --frozen-lockfile
20 | - name: Tests and coverage
21 | run: yarn coverage
22 | - name: Build
23 | run: yarn build
24 |
--------------------------------------------------------------------------------
/src/dev/App.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import ReactWhatsapp from '../lib';
3 |
4 | const App = () => {
5 | const [number, setNumber] = useState('');
6 | const [message, setMessage] = useState('');
7 |
8 | return (
9 |
10 |
React Whatsapp
11 |
28 |
29 | );
30 | };
31 |
32 | export default App;
33 |
--------------------------------------------------------------------------------
/src/lib/ReactWhatsapp.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | const URL = 'https://wa.me';
5 |
6 | const ReactWhatsapp = ({ number, message, element, onClick, ...props }) => {
7 | const Element = element;
8 |
9 | number = number.replace(/[^\w\s]/gi, '').replace(/ /g, '');
10 |
11 | let url = `${URL}/${number}`;
12 |
13 | if (message) {
14 | url += `?text=${encodeURI(message)}`;
15 | }
16 |
17 | return (
18 | {
20 | window.open(url);
21 |
22 | if (onClick) {
23 | onClick(e);
24 | }
25 | }}
26 | {...props}
27 | />
28 | );
29 | };
30 |
31 | ReactWhatsapp.propTypes = {
32 | number: PropTypes.string.isRequired,
33 | message: PropTypes.string,
34 | element: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
35 | };
36 |
37 | ReactWhatsapp.defaultProps = {
38 | element: 'button',
39 | };
40 |
41 | export default ReactWhatsapp;
42 |
--------------------------------------------------------------------------------
/src/dev/index.css:
--------------------------------------------------------------------------------
1 | body,
2 | #root {
3 | margin: 0;
4 | padding: 0;
5 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
6 | }
7 |
8 | html,
9 | body,
10 | #root {
11 | width: 100%;
12 | height: 100%;
13 | }
14 |
15 | .content {
16 | background-color: #1ebea5;
17 | width: 100%;
18 | height: 100%;
19 | display: flex;
20 | align-items: center;
21 | justify-content: center;
22 | flex-direction: column;
23 | }
24 |
25 | .title {
26 | color: white;
27 | margin-bottom: 10px;
28 | margin-top: 0px;
29 | font-size: 40pt;
30 | }
31 |
32 | .library {
33 | width: 600px;
34 | max-width: 100%;
35 | height: 300px;
36 | display: flex;
37 | flex-direction: column;
38 | align-items: center;
39 | justify-content: center;
40 | border-radius: 2px;
41 | padding: 16px;
42 | }
43 |
44 | input,
45 | button {
46 | width: 100%;
47 | font-family: inherit;
48 | font-size: 16pt;
49 | padding: 0.4em;
50 | margin: 0 0 20px 0;
51 | box-sizing: border-box;
52 | border: 1px solid #ccc;
53 | border-radius: 4px;
54 | }
55 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 N.A.D.A.
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 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /bower_componets
6 | /.pnp
7 | .pnp.js
8 |
9 | # TypeScript v1 declaration files
10 | typings/
11 |
12 | # TypeScript cache
13 | *.tsbuildinfo
14 |
15 | # Optional npm cache directory
16 | .npm
17 |
18 | # Optional eslint cache
19 | .eslintcache
20 |
21 | # Optional REPL history
22 | .node_repl_history
23 |
24 | # Output of 'npm pack'
25 | *.tgz
26 |
27 | # Yarn Integrity file
28 | .yarn-integrity
29 |
30 | # testing
31 | /coverage
32 |
33 | # production
34 | /dist
35 |
36 | # next.js build output
37 | .next
38 |
39 | # dotenv environment variables file
40 | .env
41 | .env.test
42 |
43 | # misc
44 | .DS_*
45 | **/*.backup.*
46 | **/*.back.*
47 | .env.local
48 | .env.development.local
49 | .env.test.local
50 | .env.production.local
51 |
52 | # Logs
53 | logs
54 | *.log
55 | npm-debug.log*
56 | yarn-debug.log*
57 | yarn-error.log*
58 | lerna-debug.log*
59 |
60 | # Runtime data
61 | pids
62 | *.pid
63 | *.seed
64 | *.pid.lock
65 |
66 | # DynamoDB Local files
67 | .dynamodb/
68 |
69 | # FuseBox cache
70 | .fusebox/
71 |
72 | # parcel-bundler cache (https://parceljs.org/)
73 | .cache
74 |
75 | # Other
76 | .idea/
77 | *.sublime*
78 | psd
79 | thumb
80 | sketch
81 | Thumbs.db
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-whatsapp",
3 | "description": "React component for whatsapp click to chat",
4 | "version": "0.3.0",
5 | "main": "dist/index.js",
6 | "module": "dist/index.js",
7 | "types": "dist/index.d.ts",
8 | "homepage": "https://react-whatsapp.netlify.com/",
9 | "author": "André Lins (https://andrelmlins.github.io/)",
10 | "license": "MIT",
11 | "files": [
12 | "dist"
13 | ],
14 | "scripts": {
15 | "start": "react-dependency-scripts start",
16 | "build": "react-dependency-scripts build",
17 | "build:app": "react-dependency-scripts build-app",
18 | "test": "react-dependency-scripts test",
19 | "coverage": "react-dependency-scripts test --coverage"
20 | },
21 | "devDependencies": {
22 | "@testing-library/react": "^11.2.5",
23 | "prop-types": "^15.7.2",
24 | "react": "^17.0.1",
25 | "react-dependency-scripts": "^1.0.6",
26 | "react-dom": "^17.0.1"
27 | },
28 | "repository": {
29 | "type": "git",
30 | "url": "git+https://github.com/andrelmlins/react-whatsapp.git"
31 | },
32 | "keywords": [
33 | "react",
34 | "whatsapp",
35 | "click-to-chat"
36 | ],
37 | "bugs": {
38 | "url": "https://github.com/andrelmlins/react-whatsapp/issues"
39 | },
40 | "browserslist": {
41 | "production": [
42 | ">0.2%",
43 | "not dead",
44 | "not op_mini all"
45 | ],
46 | "development": [
47 | "last 1 chrome version",
48 | "last 1 firefox version",
49 | "last 1 safari version"
50 | ]
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/lib/ReactWhatsapp.spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | render,
4 | cleanup,
5 | getByLabelText,
6 | fireEvent
7 | } from '@testing-library/react';
8 |
9 | import ReactWhatsapp from './index';
10 |
11 | afterEach(cleanup);
12 |
13 | describe('ReactWhatsapp Component', () => {
14 | it('Should render the component', () => {
15 | const { container } = createComponent({
16 | number: '+1-202-555-0107',
17 | message: 'MESSAGE'
18 | });
19 | expect(container).toBeDefined();
20 | });
21 |
22 | it('Should render the component without message', () => {
23 | const { container } = createComponent({ number: '+1-202-555-0107' });
24 | expect(container).toBeDefined();
25 | });
26 |
27 | it('Call button with onClick', () => {
28 | const onClick = jest.fn();
29 | window.open = jest.fn();
30 |
31 | const { getByLabelText } = createComponent({
32 | number: '+1-202-555-0107',
33 | onClick,
34 | 'aria-label': 'Click'
35 | });
36 |
37 | fireEvent.click(getByLabelText('Click'));
38 |
39 | expect(onClick).toHaveBeenCalledTimes(1);
40 | expect(window.open).toHaveBeenCalledTimes(1);
41 | });
42 |
43 | it('Call button without onClick', () => {
44 | window.open = jest.fn();
45 |
46 | const { getByLabelText } = createComponent({
47 | number: '+1-202-555-0107',
48 | 'aria-label': 'Click'
49 | });
50 |
51 | fireEvent.click(getByLabelText('Click'));
52 |
53 | expect(window.open).toHaveBeenCalledTimes(1);
54 | });
55 | });
56 |
57 | function createComponent(props = {}) {
58 | const defaultProps = {
59 | ...props
60 | };
61 |
62 | return render();
63 | }
64 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Whatsapp
2 |
3 | [](https://www.npmjs.com/package/react-whatsapp) • [](https://github.com/andrelmlins/react-whatsapp/blob/master/LICENSE) • [[](https://github.com/andrelmlins/react-whatsapp/actions/workflows/nodejs.yml) • [](https://app.netlify.com/sites/react-whatsapp/deploys)
4 |
5 | React component for whatsapp click to chat
6 |
7 | ## Installation
8 |
9 | ```
10 | npm i react-whatsapp
11 | // OR
12 | yarn add react-whatsapp
13 | ```
14 |
15 | ## Demo [Link](https://react-whatsapp.netlify.com/)
16 |
17 | Local demo:
18 |
19 | ```
20 | git clone https://github.com/andrelmlins/react-whatsapp.git
21 | cd react-whatsapp
22 | npm install && npm run start
23 | ```
24 |
25 | ## Examples
26 |
27 | ```jsx
28 | import React from 'react';
29 | import { render } from 'react-dom';
30 | import ReactWhatsapp from 'react-whatsapp';
31 |
32 | const App = () => (
33 |
34 | );
35 |
36 | render(, document.getElementById('root'));
37 | ```
38 |
39 | ## Properties
40 |
41 | | Prop | Default | Type | Description |
42 | | ------- | ------- | ------- | ----------------------------------------------------- |
43 | | number | -- | string | Phone number |
44 | | message | -- | string | Message for chat |
45 | | element | button | element | Either a string to use a HTML element or a component. |
46 |
47 | ## NPM Statistics
48 |
49 | Download stats for this NPM package
50 |
51 | [](https://nodei.co/npm/react-whatsapp/)
52 |
53 | ## License
54 |
55 | React Whatsapp is open source software [licensed as MIT](https://github.com/andrelmlins/react-whatsapp/blob/master/LICENSE).
56 |
--------------------------------------------------------------------------------