├── .babelrc
├── .coveralls.yml
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .npmignore
├── .sass-lint.yml
├── .travis.yml
├── LICENSE
├── README.md
├── config
├── .eslintrc
├── env.js
├── jest
│ ├── cssTransform.js
│ ├── fileTransform.js
│ ├── setup.js
│ ├── setupTestFramework.js
│ └── transform.js
├── paths.js
├── polyfills.js
├── webpack.config.dev.js
└── webpack.config.prod.js
├── example
├── icon.png
└── index.html
├── package.json
├── scripts
├── build.js
├── start.js
└── test.js
├── src
├── components
│ ├── SmartBanner.js
│ └── __tests__
│ │ ├── SmartBanner.test.js
│ │ └── __snapshots__
│ │ └── SmartBanner.test.js.snap
├── example.js
├── icon.png
├── index.html
└── styles
│ └── style.scss
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-env",
4 | "@babel/preset-react"
5 | ],
6 | "plugins": [
7 | "@babel/plugin-proposal-class-properties"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/.coveralls.yml:
--------------------------------------------------------------------------------
1 | repo_token: qEafeG1Qpc3JMNK787kjVwNUxqtyoXlWl
2 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | dist/*
2 | example/*
3 | test/*
4 | node_modules/*
5 | package.json
6 | webpack.*.js
7 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['eslint-config-airbnb', 'prettier'],
3 | parser: 'babel-eslint',
4 | env: {
5 | browser: true,
6 | node: true,
7 | es6: true,
8 | jest: true,
9 | },
10 | rules: {
11 | 'camelcase': ["error", { allow: ["^UNSAFE_"]}],
12 | 'react/sort-comp': [1, {
13 | order: [
14 | 'static-methods',
15 | 'lifecycle',
16 | '/^UNSAFE_/',
17 | 'everything-else'
18 | ],
19 | }],
20 | 'prefer-destructuring': 'off',
21 | 'jsx-a11y/click-events-have-key-events': 'off',
22 | 'jsx-a11y/no-noninteractive-element-interactions': 'off',
23 | 'jsx-a11y/no-autofocus': 'off',
24 | 'jsx-a11y/no-noninteractive-tabindex': 'off',
25 | 'jsx-a11y/anchor-has-content': 'off',
26 | 'react/destructuring-assignment': 'off',
27 | 'react/jsx-no-bind': 'error',
28 | 'react/no-multi-comp': 'off',
29 | 'no-restricted-syntax': [
30 | 'error',
31 | 'DebuggerStatement',
32 | 'ForInStatement',
33 | 'WithStatement',
34 | ],
35 | 'newline-after-var': ['error', 'always'],
36 | 'newline-before-return': 'error',
37 | 'comma-dangle': ['error', 'always-multiline'], // https://github.com/airbnb/javascript/commit/788208295469e19b806c06e01095dc8ba1b6cdc9
38 | indent: ['error', 2, { SwitchCase: 1 }],
39 | 'no-console': 0,
40 | 'no-alert': 0,
41 | 'no-underscore-dangle': 'off',
42 | 'max-len': ['error', 150, 2, { ignoreUrls: true, ignoreComments: false }],
43 | 'react/require-default-props': 'off',
44 | 'react/jsx-curly-spacing': 'off',
45 | 'arrow-body-style': 'off',
46 | 'no-mixed-operators': [
47 | 'error',
48 | {
49 | groups: [
50 | ['&', '|', '^', '~', '<<', '>>', '>>>'],
51 | ['==', '!=', '===', '!==', '>', '>=', '<', '<='],
52 | ['&&', '||'],
53 | ['in', 'instanceof'],
54 | ],
55 | allowSamePrecedence: true,
56 | },
57 | ],
58 | 'react/jsx-filename-extension': ['error', { extensions: ['.js', '.jsx'] }],
59 | 'react/no-string-refs': 'off',
60 | 'arrow-parens': 'off',
61 | 'jsx-a11y/no-static-element-interactions': 'off',
62 | 'react/prefer-stateless-function': 'off',
63 | 'no-param-reassign': 'off',
64 | 'no-unused-vars': ['error', { ignoreRestSiblings: true }],
65 | 'import/no-unresolved': [
66 | 2,
67 | { ignore: ['react', 'react-dom', 'react-intl-tel-input'] },
68 | ],
69 | 'import/extensions': 'off',
70 | 'import/no-extraneous-dependencies': [
71 | 'error',
72 | {
73 | devDependencies: [
74 | 'test/**', // tape, common npm pattern
75 | 'tests/**', // also common npm pattern
76 | 'spec/**', // mocha, rspec-like pattern
77 | '**/__tests__/**', // jest pattern
78 | '**/__mocks__/**', // jest pattern
79 | 'test.js', // repos with a single test file
80 | 'test-*.js', // repos with multiple top-level test files
81 | '**/*.test.js', // tests where the extension denotes that it is a test
82 | '**/webpack.config.js', // webpack config
83 | '**/webpack.config.*.js', // webpack config
84 | 'config/jest/**',
85 | 'src/testUtils/**',
86 | '*.js',
87 | ],
88 | optionalDependencies: false,
89 | },
90 | ],
91 | indent: [
92 | 'error',
93 | 2,
94 | {
95 | SwitchCase: 1,
96 | VariableDeclarator: 1,
97 | outerIIFEBody: 1,
98 | MemberExpression: 1,
99 | // CallExpression: {
100 | // parameters: null,
101 | // },
102 | FunctionDeclaration: {
103 | parameters: 1,
104 | body: 1,
105 | },
106 | FunctionExpression: {
107 | parameters: 1,
108 | body: 1,
109 | },
110 | },
111 | ],
112 | 'no-plusplus': ['error', { allowForLoopAfterthoughts: true }],
113 | },
114 | plugins: ['react', 'import', 'security'],
115 | globals: {
116 | __DEVELOPMENT__: true,
117 | __CLIENT__: true,
118 | __SERVER__: true,
119 | __DISABLE_SSR__: true,
120 | __DEVTOOLS__: true,
121 | },
122 | };
123 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### SublimeText ###
2 | *.sublime-workspace
3 |
4 | ### JetBrains ###
5 | .idea
6 |
7 | ### OSX ###
8 | .DS_Store
9 | .AppleDouble
10 | .LSOverride
11 | Icon
12 |
13 | # Thumbnails
14 | ._*
15 |
16 | # Visual Studio
17 | *.history
18 | *.vscode
19 | .vscode/
20 | .history/
21 |
22 | # Files that might appear on external disk
23 | .Spotlight-V100
24 | .Trashes
25 |
26 | ### Windows ###
27 | # Windows image file caches
28 | Thumbs.db
29 | ehthumbs.db
30 |
31 | # Folder config file
32 | Desktop.ini
33 |
34 | # Recycle Bin used on file shares
35 | $RECYCLE.BIN/
36 |
37 | # App specific
38 |
39 | dist/
40 | node_modules/
41 | .tmp
42 | /src/main.js
43 | .grunt/
44 | .history/
45 | npm-debug.log
46 | build/**
47 | dist/**
48 | example/**
49 | test/**
50 | coverage/*
51 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | !dist/*
2 |
3 |
--------------------------------------------------------------------------------
/.sass-lint.yml:
--------------------------------------------------------------------------------
1 | options:
2 | merge-default-rules: false
3 | formatter: stylish
4 | files:
5 | include: '**/*.s+(a|c)ss'
6 | ignore:
7 | - 'node_modules/**/*.*'
8 | - 'src/styles/vendor/**/*.*'
9 | rules:
10 | # Extends
11 | extends-before-mixins: 0
12 | extends-before-declarations: 0
13 | placeholder-in-extend: 2
14 |
15 | # Mixins
16 | mixins-before-declarations: 2
17 |
18 | # Line Spacing
19 | one-declaration-per-line: 2
20 | empty-line-between-blocks: 2
21 | single-line-per-selector: 2
22 |
23 | # Disallows
24 | no-attribute-selectors: 0
25 | no-color-hex: 0
26 | no-color-keywords: 0
27 | no-combinators: 0
28 | no-css-comments: 0
29 | no-debug: 2
30 | no-disallowed-properties: 0
31 | no-duplicate-properties: 2
32 | no-empty-rulesets: 2
33 | no-extends: 0
34 | no-ids: 2
35 | no-important: 2
36 | no-invalid-hex: 2
37 | no-mergeable-selectors: 2
38 | no-misspelled-properties:
39 | - 2
40 | - extra-properties: ['overflow-scrolling']
41 | no-qualifying-elements: 0
42 | no-trailing-whitespace: 2
43 | no-trailing-zero: 2
44 | no-transition-all: 2
45 | no-universal-selectors: 0
46 | no-url-protocols: 2
47 | no-vendor-prefixes: 0
48 | no-warn: 2
49 | property-units: 0
50 |
51 | # Nesting
52 | force-attribute-nesting: 2
53 | force-element-nesting: 2
54 | force-pseudo-nesting: 2
55 |
56 | # Name Formats
57 | class-name-format: 2
58 | function-name-format: 2
59 | id-name-format:
60 | - 2
61 | - convention: snakecase
62 | mixin-name-format: 2
63 | placeholder-name-format: 2
64 | variable-name-format: 2
65 |
66 | # Style Guide
67 | attribute-quotes: 2
68 | bem-depth: 0
69 | border-zero: 2
70 | brace-style: 2
71 | clean-import-paths: 2
72 | empty-args: 2
73 | hex-length: 2
74 | hex-notation: 2
75 | indentation: 2
76 | leading-zero:
77 | - 2
78 | - include: true
79 | nesting-depth: 0
80 | property-sort-order: 0
81 | pseudo-element: 0
82 | quotes:
83 | - 2
84 | - style: single
85 | shorthand-values: 2
86 | url-quotes: 2
87 | variable-for-property: 2
88 | zero-unit: 2
89 |
90 | # Inner Spacing
91 | space-after-comma: 2
92 | space-before-colon: 2
93 | space-after-colon: 2
94 | space-before-brace: 2
95 | space-before-bang: 2
96 | space-after-bang: 2
97 | space-between-parens: 2
98 | space-around-operator: 2
99 |
100 | # Final Items
101 | trailing-semicolon: 2
102 | final-newline: 2
103 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | cache:
3 | yarn: true
4 | directories:
5 | - node_modules
6 | node_js:
7 | - "8.10.0"
8 | script:
9 | - yarn test
10 | - yarn lint
11 | - yarn build
12 | after_success: yarn run coverage
13 | after_script: yarn run coveralls
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Patrick Wang (patw)
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-SmartBanner
2 |
3 | [](https://travis-ci.org/patw0929/react-smartbanner)
4 | [](http://badge.fury.io/js/react-smartbanner)
5 | [](https://coveralls.io/github/patw0929/react-smartbanner?branch=master)
6 | []()
7 |
8 | Rewrite [Smart App Banner](https://github.com/kudago/smart-app-banner) in React.js.
9 |
10 |
11 | ## Demo & Examples
12 |
13 | Live demo: [patw0929.github.io/react-smartbanner](https://patw0929.github.io/react-smartbanner/)
14 |
15 | To build the examples locally, run:
16 |
17 | ```bash
18 | npm install
19 | npm start
20 | ```
21 |
22 | or
23 |
24 | ```bash
25 | yarn
26 | yarn start
27 | ```
28 |
29 | ## Installation
30 |
31 | The easiest way to use react-smartbanner is to install it from NPM and include it in your own React build process (using [Webpack](http://webpack.github.io/), etc).
32 |
33 | You can also use the standalone build by including `dist/main.js` in your page. If you use this, make sure you have already included React, and it is available as a global variable.
34 |
35 | ```
36 | npm install react-smartbanner --save
37 | ```
38 |
39 | or
40 |
41 | ```bash
42 | yarn add react-smartbanner
43 | ```
44 |
45 | ## Compatibility
46 |
47 | | react-smartbanner version | React version |
48 | | --- | --- |
49 | | `4.x.x+` | `^16.0.0` |
50 | | `3.x.x` | `^15.0.0` |
51 |
52 |
53 | ## Usage
54 |
55 | Remember to add following meta tags in your HTML page: (Use Facebook app as example)
56 |
57 | ```html
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | ```
68 |
69 | And React-SmartBanner component usage:
70 |
71 | ```javascript
72 | import React from 'react';
73 | import ReactDOM from 'react-dom';
74 | import SmartBanner from 'react-smartbanner';
75 | import 'react-smartbanner/dist/main.css';
76 |
77 | ReactDOM.render(, document.getElementById('content'));
78 | ```
79 |
80 | ### Properties
81 |
82 | Please see the [Demo Page](https://patw0929.github.io/react-smartbanner/)
83 |
84 |
85 | ## Development (`src` and the build process)
86 |
87 | **NOTE:** The source code for the component is in `src`. A UMD bundle is also built to `dist`, which can be included without the need for any build system.
88 |
89 | To build, watch and serve the examples (which will also watch the component source), run `npm start`.
90 |
91 | If you want to build to the bundle file to `dist/` folder, please run:
92 |
93 | ```bash
94 | npm run build
95 | ```
96 |
97 | or
98 |
99 | ```bash
100 | yarn run build
101 | ```
102 |
103 | ## Contributing
104 |
105 | To contribute to react-smartbanner, clone this repo locally and commit your code on a separate branch. Please write tests for your code, and run the linter before opening a pull-request:
106 |
107 | ```bash
108 | npm test
109 | npm run lint
110 | ```
111 |
112 | or
113 |
114 | ```bash
115 | yarn test
116 | yarn run lint
117 | ```
118 |
119 | ## Based on
120 |
121 | [Smart App Banner](https://github.com/kudago/smart-app-banner)
122 |
123 | ## License
124 |
125 | MIT
126 |
127 | Copyright (c) 2015-2019 patw.
128 |
129 |
--------------------------------------------------------------------------------
/config/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "no-var": 0,
4 | "comma-dangle": 0,
5 | "quote-props": 0,
6 | "object-shorthand": 0,
7 | "no-multiple-empty-lines": 0,
8 | "no-trailing-spaces": 0
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/config/env.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-var, arrow-parens, prefer-template */
2 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
3 | // injected into the application via DefinePlugin in Webpack configuration.
4 |
5 | const REACT_APP = /^REACT_APP_/i;
6 |
7 | function getClientEnvironment(publicUrl) {
8 | var NODE_ENV = JSON.stringify(process.env.NODE_ENV || 'development');
9 |
10 | const DEVELOPMENT = NODE_ENV === JSON.stringify('development');
11 | const SERVER = false;
12 | const CLIENT = true;
13 | const BUILD_NAME = JSON.stringify(process.env.BUILD_NAME || 'dev');
14 |
15 | const processEnv = Object.keys(process.env)
16 | .filter(key => REACT_APP.test(key))
17 | .reduce(
18 | (env, key) => {
19 | env[key] = JSON.stringify(process.env[key]); // eslint-disable-line no-param-reassign
20 |
21 | return env;
22 | },
23 | {
24 | // Useful for determining whether we’re running in production mode.
25 | // Most importantly, it switches React into the correct mode.
26 | NODE_ENV,
27 | BUILD_NAME,
28 | // Useful for resolving the correct path to static assets in `public`.
29 | // For example,
.
30 | // This should only be used as an escape hatch. Normally you would put
31 | // images into the `src` and `import` them in code to get their paths.
32 | PUBLIC_URL: JSON.stringify(publicUrl),
33 | }
34 | );
35 |
36 | return {
37 | 'process.env': processEnv,
38 | __SERVER__: SERVER,
39 | __CLIENT__: CLIENT,
40 | __DEVELOPMENT__: DEVELOPMENT,
41 | };
42 | }
43 |
44 | module.exports = getClientEnvironment;
45 |
--------------------------------------------------------------------------------
/config/jest/cssTransform.js:
--------------------------------------------------------------------------------
1 | // This is a custom Jest transformer turning style imports into empty objects.
2 | // http://facebook.github.io/jest/docs/tutorial-webpack.html
3 |
4 | module.exports = {
5 | process() {
6 | return `
7 | const idObj = require('identity-obj-proxy');
8 | module.exports = idObj;
9 | `;
10 | },
11 | getCacheKey() {
12 | // eslint-disable-line no-unused-vars
13 | // The output is always the same.
14 | return 'cssTransform';
15 | },
16 | };
17 |
--------------------------------------------------------------------------------
/config/jest/fileTransform.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | // This is a custom Jest transformer turning file imports into filenames.
4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html
5 |
6 | module.exports = {
7 | process(src, filename) {
8 | // eslint-disable-next-line prefer-template
9 | return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';';
10 | },
11 | };
12 |
--------------------------------------------------------------------------------
/config/jest/setup.js:
--------------------------------------------------------------------------------
1 | import jsdom from 'jsdom';
2 |
3 | window.__SERVER__ = false;
4 | window.__DEVELOPMENT__ = false;
5 |
6 | // Define some html to be our basic document
7 | // JSDOM will consume this and act as if we were in a browser
8 | const DEFAULT_HTML = '';
9 |
10 | // Define some variables to make it look like we're a browser
11 | // First, use JSDOM's fake DOM as the document
12 | const doc = jsdom.jsdom(DEFAULT_HTML);
13 |
14 | global.document = doc;
15 |
16 | // Set up a mock window
17 | global.window = doc.defaultView;
18 |
19 | // Allow for things like window.location
20 | global.navigator = window.navigator;
21 |
22 | const DATE_TO_USE = new Date('2017-05-11');
23 | const _Date = Date;
24 |
25 | global.Date = jest.fn(() => DATE_TO_USE);
26 | global.Date.UTC = _Date.UTC;
27 | global.Date.parse = _Date.parse;
28 | global.Date.now = _Date.now;
29 |
--------------------------------------------------------------------------------
/config/jest/setupTestFramework.js:
--------------------------------------------------------------------------------
1 | /* global jasmine:false */
2 | import Enzyme from 'enzyme';
3 | import Adapter from 'enzyme-adapter-react-16';
4 |
5 | Enzyme.configure({ adapter: new Adapter() });
6 |
7 | if (process.env.CI) {
8 | const jasmineReporters = require('jasmine-reporters'); // eslint-disable-line global-require
9 | const junitReporter = new jasmineReporters.JUnitXmlReporter({
10 | savePath: 'testresults',
11 | consolidateAll: false,
12 | });
13 |
14 | jasmine.getEnv().addReporter(junitReporter);
15 | }
16 |
--------------------------------------------------------------------------------
/config/jest/transform.js:
--------------------------------------------------------------------------------
1 | const babelJest = require('babel-jest');
2 |
3 | module.exports = babelJest.createTransformer();
4 |
--------------------------------------------------------------------------------
/config/paths.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var fs = require('fs');
3 |
4 | // Make sure any symlinks in the project folder are resolved:
5 | // https://github.com/facebookincubator/create-react-app/issues/637
6 | const appDirectory = fs.realpathSync(process.cwd());
7 |
8 | function resolveApp(relativePath) {
9 | return path.resolve(appDirectory, relativePath);
10 | }
11 |
12 | // We support resolving modules according to `NODE_PATH`.
13 | // This lets you use absolute paths in imports inside large monorepos:
14 | // https://github.com/facebookincubator/create-react-app/issues/253.
15 |
16 | // It works similar to `NODE_PATH` in Node itself:
17 | // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
18 |
19 | // We will export `nodePaths` as an array of absolute paths.
20 | // It will then be used by Webpack configs.
21 | // Jest doesn’t need this because it already handles `NODE_PATH` out of the box.
22 |
23 | // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
24 | // Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
25 | // https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421
26 |
27 | // eslint-disable-next-line vars-on-top
28 | const nodePaths = (process.env.NODE_PATH || '')
29 | .split(process.platform === 'win32' ? ';' : ':')
30 | .filter(Boolean)
31 | .filter(folder => !path.isAbsolute(folder))
32 | .map(resolveApp);
33 |
34 | // config after eject: we're in ./config/
35 | module.exports = {
36 | appBuild: resolveApp('example'),
37 | appDist: resolveApp('dist'),
38 | appPublic: resolveApp('public'),
39 | appHtml: resolveApp('src/index.html'),
40 | appExampleJs: resolveApp('src/example.js'),
41 | appPackageJson: resolveApp('package.json'),
42 | appSrc: resolveApp('src'),
43 | yarnLockFile: resolveApp('yarn.lock'),
44 | appNodeModules: resolveApp('node_modules'),
45 | ownNodeModules: resolveApp('node_modules'),
46 | nodePaths: nodePaths,
47 | };
48 |
--------------------------------------------------------------------------------
/config/polyfills.js:
--------------------------------------------------------------------------------
1 | if (typeof Promise === 'undefined') {
2 | // Rejection tracking prevents a common issue where React gets into an
3 | // inconsistent state due to an error, but it gets swallowed by a Promise,
4 | // and the user has no idea what causes React's erratic future behavior.
5 | require('promise/lib/rejection-tracking').enable();
6 | window.Promise = require('promise/lib/es6-extensions.js');
7 | }
8 |
9 | // fetch() polyfill for making API calls.
10 | require('whatwg-fetch');
11 |
12 | // Object.assign() is commonly used with React.
13 | // It will use the native implementation if it's present and isn't buggy.
14 | Object.assign = require('object-assign');
15 |
--------------------------------------------------------------------------------
/config/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Webpack development server configuration
3 | *
4 | * This file is set up for serving the webpack-dev-server, which will watch for changes and recompile as required if
5 | * the subfolder /webpack-dev-server/ is visited. Visiting the root will not automatically reload.
6 | */
7 |
8 | const webpack = require('webpack');
9 | const paths = require('./paths');
10 | const CopyWebpackPlugin = require('copy-webpack-plugin');
11 | const HtmlWebpackPlugin = require('html-webpack-plugin');
12 | const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
13 | const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
14 | const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
15 | const getClientEnvironment = require('./env');
16 |
17 | // Webpack uses `publicPath` to determine where the app is being served from.
18 | // In development, we always serve from the root. This makes config easier.
19 | const publicPath = '/';
20 | // `publicUrl` is just like `publicPath`, but we will provide it to our app
21 | // as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
22 | // Omit trailing shlash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz.
23 | const publicUrl = '';
24 | // Get enrivonment variables to inject into our app.
25 | const env = getClientEnvironment(publicUrl);
26 |
27 | module.exports = {
28 | mode: 'development',
29 | devtool: 'cheap-module-source-map',
30 | entry: [
31 | require.resolve('react-dev-utils/webpackHotDevClient'),
32 | require.resolve('./polyfills'),
33 | paths.appExampleJs,
34 | ],
35 |
36 | output: {
37 | path: paths.appBuild,
38 | pathinfo: true,
39 | filename: 'static/js/bundle.js',
40 | publicPath: publicPath,
41 | },
42 |
43 | externals: {
44 | react: 'React',
45 | 'react-dom': 'ReactDOM',
46 | },
47 |
48 | resolve: {
49 | modules: ['src', 'node_modules', ...paths.nodePaths],
50 | alias: {
51 | 'react-smartbanner': './components/SmartBanner.js',
52 | },
53 | },
54 | module: {
55 | rules: [
56 | {
57 | test: /\.(js|jsx)$/,
58 | enforce: 'pre',
59 | include: paths.appSrc,
60 | use: {
61 | loader: 'eslint-loader',
62 | },
63 | },
64 | {
65 | exclude: [
66 | /\.html$/,
67 | /\.(js|jsx)$/,
68 | /\.css$/,
69 | /\.scss$/,
70 | /\.json$/,
71 | /\.png$/,
72 | /\.svg$/,
73 | ],
74 | loader: 'url-loader',
75 | options: {
76 | limit: 10000,
77 | name: 'static/media/[name].[hash:8].[ext]',
78 | },
79 | },
80 | {
81 | test: /\.(js|jsx)$/,
82 | include: paths.appSrc,
83 | use: {
84 | loader: 'babel-loader',
85 | },
86 | },
87 | {
88 | test: /\.scss$/,
89 | use: [
90 | 'style-loader?sourceMap',
91 | 'css-loader',
92 | 'sass-loader?outputStyle=expanded',
93 | ],
94 | },
95 | {
96 | test: /\.css$/,
97 | use: [{ loader: 'style-loader' }, { loader: 'css-loader' }],
98 | },
99 | ],
100 | },
101 |
102 | plugins: [
103 | new webpack.NormalModuleReplacementPlugin(
104 | /^\.\/main\.css$/,
105 | '../dist/main.css'
106 | ),
107 | new CopyWebpackPlugin([{ from: 'src/icon.png', to: './' }]),
108 | new HtmlWebpackPlugin({
109 | inject: true,
110 | template: paths.appHtml,
111 | }),
112 | new InterpolateHtmlPlugin(HtmlWebpackPlugin, {
113 | PUBLIC_URL: publicUrl,
114 | }),
115 | new webpack.DefinePlugin(env),
116 | new webpack.HotModuleReplacementPlugin(),
117 | new CaseSensitivePathsPlugin(),
118 | new WatchMissingNodeModulesPlugin(paths.appNodeModules),
119 | ],
120 | node: {
121 | fs: 'empty',
122 | net: 'empty',
123 | tls: 'empty',
124 | },
125 | };
126 |
--------------------------------------------------------------------------------
/config/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-var, arrow-parens, prefer-template, comma-dangle, object-shorthand, global-require, func-names, no-else-return, vars-on-top */
2 |
3 | const webpack = require('webpack');
4 | const paths = require('./paths');
5 | const CopyWebpackPlugin = require('copy-webpack-plugin');
6 | const HtmlWebpackPlugin = require('html-webpack-plugin');
7 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
8 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
9 | const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
10 | const safeParser = require('postcss-safe-parser');
11 | const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
12 | const getClientEnvironment = require('./env');
13 |
14 | // Webpack uses `publicPath` to determine where the app is being served from.
15 | // In development, we always serve from the root. This makes config easier.
16 | const publicPath = '';
17 | // `publicUrl` is just like `publicPath`, but we will provide it to our app
18 | // as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
19 | // Omit trailing shlash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz.
20 | const publicUrl = '';
21 | // Get enrivonment variables to inject into our app.
22 | const env = getClientEnvironment(publicUrl);
23 |
24 | // Assert this just to be safe.
25 | // Development builds of React are slow and not intended for production.
26 | if (env['process.env'].NODE_ENV !== '"production"') {
27 | throw new Error('Production builds must have NODE_ENV=production.');
28 | }
29 |
30 | module.exports = {
31 | mode: 'production',
32 | devtool: false,
33 | entry: {
34 | main: './src/components/SmartBanner.js',
35 | example: [require.resolve('./polyfills'), './src/example.js'],
36 | },
37 |
38 | output: {
39 | path: paths.appBuild,
40 | pathinfo: true,
41 | filename: '[name].js',
42 | publicPath: publicPath,
43 | library: 'SmartBanner',
44 | libraryTarget: 'umd',
45 | globalObject: 'typeof self !== \'undefined\' ? self : this',
46 | },
47 |
48 | externals: {
49 | react: {
50 | root: 'React',
51 | commonjs2: 'react',
52 | commonjs: 'react',
53 | amd: 'react',
54 | },
55 | 'react-dom': {
56 | root: 'ReactDOM',
57 | commonjs2: 'react-dom',
58 | commonjs: 'react-dom',
59 | amd: 'react-dom',
60 | },
61 | 'prop-types': {
62 | root: 'PropTypes',
63 | commonjs2: 'prop-types',
64 | commonjs: 'prop-types',
65 | amd: 'prop-types',
66 | },
67 | },
68 |
69 | resolve: {
70 | modules: ['src', 'node_modules', ...paths.nodePaths],
71 | alias: {
72 | 'react-smartbanner': './components/SmartBanner.js',
73 | },
74 | },
75 | module: {
76 | rules: [
77 | {
78 | test: /\.(js|jsx)$/,
79 | loader: 'eslint-loader',
80 | enforce: 'pre',
81 | include: paths.appSrc,
82 | },
83 | {
84 | exclude: [
85 | /\.html$/,
86 | /\.(js|jsx)$/,
87 | /\.css$/,
88 | /\.scss$/,
89 | /\.json$/,
90 | /\.png$/,
91 | /\.svg$/,
92 | ],
93 | loader: 'url-loader',
94 | options: {
95 | limit: 10000,
96 | name: 'media/[name].[hash:8].[ext]',
97 | },
98 | },
99 | {
100 | test: /\.(js|jsx)$/,
101 | include: paths.appSrc,
102 | use: {
103 | loader: 'babel-loader',
104 | },
105 | },
106 | {
107 | test: /\.scss$/,
108 | use: [
109 | MiniCssExtractPlugin.loader,
110 | 'css-loader',
111 | 'sass-loader?outputStyle=expanded',
112 | ],
113 | },
114 | {
115 | test: /\.css$/,
116 | use: [{ loader: 'style-loader' }, { loader: 'css-loader' }],
117 | },
118 | ],
119 | },
120 | optimization: {
121 | minimizer: [
122 | new UglifyJsPlugin({
123 | cache: true,
124 | parallel: true,
125 | uglifyOptions: {
126 | compress: true,
127 | ecma: 6,
128 | mangle: true,
129 | output: {
130 | comments: false,
131 | beautify: false,
132 | },
133 | },
134 | sourceMap: false,
135 | }),
136 | new OptimizeCssAssetsPlugin({
137 | cssProcessorOptions: {
138 | parser: safeParser,
139 | discardComments: {
140 | removeAll: true,
141 | },
142 | },
143 | }),
144 | ],
145 | },
146 |
147 | plugins: [
148 | new webpack.LoaderOptionsPlugin({
149 | minimize: true,
150 | debug: false,
151 | }),
152 | new HtmlWebpackPlugin({
153 | inject: true,
154 | template: paths.appHtml,
155 | chunks: ['example'],
156 | minify: {
157 | removeComments: true,
158 | collapseWhitespace: true,
159 | removeRedundantAttributes: true,
160 | useShortDoctype: true,
161 | removeEmptyAttributes: true,
162 | removeStyleLinkTypeAttributes: true,
163 | keepClosingSlash: true,
164 | minifyJS: true,
165 | minifyCSS: true,
166 | minifyURLs: true,
167 | },
168 | }),
169 | // Generates an `index.html` file with the If you can see this, something is broken (or JS is not enabled)!!.
Installation
npm install --save react-smartbanner
or...
yarn add react-smartbanner
Syntax
General
Remember to add following meta tags in your HTML page: (Use Facebook app as example)
<head>
4 | <meta name="apple-itunes-app" content="app-id=284882215">
5 | <meta name="google-play-app" content="app-id=com.facebook.katana">
6 | <meta name="msApplication-ID" content="82a23635-5bd9-df11-a844-00237de2db9e">
7 | <meta name="msApplication-PackageFamilyName" content="facebook_9wzdncrfhv5g">
8 | <meta name="kindle-fire-app" content="app-id=B0094BB4TW">
9 |
10 | <link rel="apple-touch-icon" href="icon.png">
11 | <link rel="android-touch-icon" href="icon.png">
12 | <link rel="windows-touch-icon" href="icon.png">
13 | </head>
14 |
And React-SmartBanner component usage:
15 | import React from 'react';
16 | import ReactDOM from 'react-dom';
17 | import SmartBanner from 'react-smartbanner';
18 | import './node_modules/react-smartbanner/dist/main.css';
19 |
20 | ReactDOM.render(<SmartBanner title={'Facebook'} />, document.getElementById('content'));
21 |
Props
key | default | description |
---|
daysHidden | 15 | Days to hide banner after close button is clicked. |
---|
daysReminder | 90 | Days to hide banner after "VIEW" button is clicked. |
---|
appStoreLanguage | (user's browser language) | Language code for the App Store. |
---|
title | '' | App title. |
---|
author | '' | App author. |
---|
button | 'View' | Display on install button. (node) |
---|
storeText | { ios: 'On the App Store', android: 'In Google Play', windows: 'In Windows store', kindle: 'In Amazon Appstore' } | Store text (object). |
---|
price | { ios: 'FREE', android: 'FREE', windows: 'FREE', kindle: 'FREE' } | Price text (object). |
---|
position | 'top' / 'bottom' | Display position on screen. Bottom is fixed, top scrolls out. |
---|
force | '' | Force to display in specific device. (android , ios , windows , kindle ) |
---|
url | { ios: 'http://www.domain.com', android: 'http://www.domain2.com', windows: 'http://www.domain3.com', kindle: 'http://www.domain4.com' } | Custom URL for each device |
---|
ignoreIosVersion | false | Boolean to ignore the iOS version, so that the banner is also displayed on devices that support the native banner. |
---|
appMeta | { ios: 'apple-itunes-app', android: 'google-play-app', windows: 'msApplication-ID', kindle: 'kindle-fire-app' } | The custom meta tag name (object). It provide an option to enforce using custom meta tag to show js react-smartbanner for newer iOS versions. |
---|
onClose | No default value | Optional callback when user clicks on close button. |
---|
onInstall | No default value | Optional callback when user clicks on install button. |
---|
Based on
Lincense
MIT License