Initial commit.
This commit is contained in:
commit
88a6747e7f
17
.babelrc
Normal file
17
.babelrc
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
[
|
||||||
|
"@babel/preset-env", {
|
||||||
|
"targets": {
|
||||||
|
"chrome": 60,
|
||||||
|
"edge": 14,
|
||||||
|
"firefox": 54,
|
||||||
|
"ie": 9,
|
||||||
|
"opera": 46,
|
||||||
|
"safari": 9
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@babel/preset-react"
|
||||||
|
]
|
||||||
|
}
|
55
.eslintrc.json
Normal file
55
.eslintrc.json
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"commonjs": true,
|
||||||
|
"jasmine": true,
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
"extends": "eslint:recommended",
|
||||||
|
"parser": "babel-eslint",
|
||||||
|
"parserOptions": {
|
||||||
|
"sourceType": "module",
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"modules": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugins": ["jasmine"],
|
||||||
|
"rules": {
|
||||||
|
"no-undef": "error",
|
||||||
|
"quotes": [
|
||||||
|
2, "single", {"avoidEscape": true, "allowTemplateLiterals": true}
|
||||||
|
],
|
||||||
|
"no-unused-vars": [0],
|
||||||
|
"no-console": [2],
|
||||||
|
"no-empty": ["error", {"allowEmptyCatch": true}],
|
||||||
|
"array-bracket-spacing": ["error", "never"],
|
||||||
|
"block-spacing": ["error", "always"],
|
||||||
|
"brace-style": ["error", "1tbs", {"allowSingleLine": true}],
|
||||||
|
"camelcase": ["error", {"properties": "never"}],
|
||||||
|
"comma-dangle": ["error", "never"],
|
||||||
|
"comma-spacing": ["error", {"before": false, "after": true}],
|
||||||
|
"comma-style": ["error", "last"],
|
||||||
|
"computed-property-spacing": ["error", "never"],
|
||||||
|
"key-spacing": [
|
||||||
|
"error", {"beforeColon": false, "afterColon": true, "mode": "strict"}
|
||||||
|
],
|
||||||
|
"keyword-spacing": ["error", { "before": true, "after": true }],
|
||||||
|
"linebreak-style": ["error", "unix"],
|
||||||
|
"max-len": ["error", 120],
|
||||||
|
"no-multiple-empty-lines": ["error"],
|
||||||
|
"no-spaced-func": ["error"],
|
||||||
|
"no-trailing-spaces": ["error"],
|
||||||
|
"no-unreachable": [1],
|
||||||
|
"no-whitespace-before-property": ["error"],
|
||||||
|
"object-curly-spacing": ["error", "never"],
|
||||||
|
"one-var-declaration-per-line": ["error", "always"],
|
||||||
|
"one-var": ["error", "never"],
|
||||||
|
"semi-spacing": ["error", {"before": false, "after": true}],
|
||||||
|
"semi": ["error", "always"],
|
||||||
|
"space-before-function-paren": ["error", "always"],
|
||||||
|
"space-before-blocks": ["error", "always"],
|
||||||
|
"space-in-parens": ["error", "never"],
|
||||||
|
"space-infix-ops": ["error"],
|
||||||
|
"unicode-bom": ["error", "never"]
|
||||||
|
}
|
||||||
|
}
|
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
lib/
|
||||||
|
node_modules/
|
||||||
|
react-custom-popup-*.tgz
|
||||||
|
yarn-error.log
|
||||||
|
.sass-cache/
|
||||||
|
bthlabs-react-custom-popup-*.tgz
|
95
Gruntfile.js
Normal file
95
Gruntfile.js
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
var underscore = require('underscore');
|
||||||
|
var webpack = require('webpack');
|
||||||
|
|
||||||
|
var defs = require('./package.defs.js');
|
||||||
|
|
||||||
|
var distWebpackConfig = require('./webpack.dist.js');
|
||||||
|
var exampleWebpackConfig = require('./webpack.example.js');
|
||||||
|
|
||||||
|
module.exports = function (grunt) {
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-sass');
|
||||||
|
grunt.loadNpmTasks('grunt-karma');
|
||||||
|
grunt.loadNpmTasks('grunt-webpack');
|
||||||
|
grunt.loadNpmTasks('gruntify-eslint');
|
||||||
|
|
||||||
|
grunt.initConfig({
|
||||||
|
clean: {
|
||||||
|
options: {
|
||||||
|
force: true
|
||||||
|
},
|
||||||
|
all: [
|
||||||
|
defs.LIB_DIR,
|
||||||
|
path.resolve(defs.EXAMPLE_ASSETS_DIR, 'example.js'),
|
||||||
|
path.resolve(defs.EXAMPLE_ASSETS_DIR, 'example.js.map'),
|
||||||
|
path.resolve(defs.EXAMPLE_ASSETS_DIR, 'example.css'),
|
||||||
|
path.resolve(defs.EXAMPLE_ASSETS_DIR, 'example.css.map')
|
||||||
|
]
|
||||||
|
},
|
||||||
|
eslint: {
|
||||||
|
example: {
|
||||||
|
src: ['./example/example.js']
|
||||||
|
},
|
||||||
|
sources: {
|
||||||
|
src: ['./src/**/*.js']
|
||||||
|
},
|
||||||
|
tests: {
|
||||||
|
src: ['./tests/**/*.spec.js']
|
||||||
|
},
|
||||||
|
tools: {
|
||||||
|
src: [
|
||||||
|
'./Gruntfile.js', './karma.conf.js', './package.defs.js',
|
||||||
|
'./webpack.dist.js', './webpack.example.js'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
karma: {
|
||||||
|
options: {
|
||||||
|
configFile: 'karma.conf.js'
|
||||||
|
},
|
||||||
|
dist: {
|
||||||
|
browsers: ['ChromiumHeadless'],
|
||||||
|
reporters: ['progress'],
|
||||||
|
singleRun: true,
|
||||||
|
webpackMiddleware: {
|
||||||
|
noInfo: true,
|
||||||
|
stats: 'errors-only'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sass: {
|
||||||
|
dist: {
|
||||||
|
src: [path.resolve(defs.SRC_DIR, 'Popup.scss')],
|
||||||
|
dest: path.resolve(defs.LIB_DIR, defs.CSS_FILENAME_DIST),
|
||||||
|
options: {
|
||||||
|
precision: 10,
|
||||||
|
sourcemap: 'auto',
|
||||||
|
style: 'compressed'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: {
|
||||||
|
src: [path.resolve(defs.EXAMPLE_DIR, 'example.scss')],
|
||||||
|
dest: path.resolve(defs.EXAMPLE_ASSETS_DIR, 'example.css'),
|
||||||
|
options: {
|
||||||
|
precision: 10,
|
||||||
|
sourcemap: 'auto',
|
||||||
|
style: 'expanded'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
webpack: {
|
||||||
|
dist: distWebpackConfig,
|
||||||
|
example: exampleWebpackConfig
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
grunt.registerTask('dist', [
|
||||||
|
'clean', 'eslint', 'karma:dist', 'sass:dist', 'webpack:dist'
|
||||||
|
]);
|
||||||
|
|
||||||
|
grunt.registerTask('example', [
|
||||||
|
'clean', 'sass:example', 'webpack:dist', 'webpack:example'
|
||||||
|
]);
|
||||||
|
};
|
19
LICENSE
Normal file
19
LICENSE
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (c) 2019-present Tomek Wójcik <tomek@bthlabs.pl>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
28
NOTICE.txt
Normal file
28
NOTICE.txt
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
react-custom-popup
|
||||||
|
Copyright (c) 2019-present Tomek Wójcik <tomek@bthlabs.pl>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
react-custom-popup includes the following third party software
|
||||||
|
|
||||||
|
Marx
|
||||||
|
Copyright (c) 2018 Matthew Blode
|
||||||
|
Licensed under terms of the MIT License
|
174
README.md
Normal file
174
README.md
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
# react-custom-popup
|
||||||
|
|
||||||
|
React Custom Popup is a React component for simply building custom popups and
|
||||||
|
modals.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
react-custom-popup requires **React 16.4.0 or later** and
|
||||||
|
**ReactDOM 16.4.0 or later**.
|
||||||
|
|
||||||
|
```
|
||||||
|
yarn add --dev react-custom-popup
|
||||||
|
```
|
||||||
|
|
||||||
|
This assumes that you’re using [yarn](https://yarnpkg.com/en/) package manager
|
||||||
|
with a module bundler like [Webpack](https://webpack.js.org/).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The following snippet shows the example usage of react-custom-popup.
|
||||||
|
|
||||||
|
```
|
||||||
|
import React from 'react';
|
||||||
|
import Popup from 'react-custom-popup';
|
||||||
|
|
||||||
|
class App extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.refButton = React.createRef();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
popupVisible: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
onShowPopupButtonClick (event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
this.setState({popupVisible: true});
|
||||||
|
}
|
||||||
|
onHidePopup (event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
this.setState({popupVisible: false});
|
||||||
|
}
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<button
|
||||||
|
ref={this.refButton}
|
||||||
|
onClick={this.onShowPopupButtonClick}
|
||||||
|
>
|
||||||
|
Show Popup
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<Popup
|
||||||
|
anchor={this.refButton}
|
||||||
|
visible={this.state.popupVisible}
|
||||||
|
onOverlayClick={this.onHidePopup}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<h2>Hello, I'm a popup!</h2>
|
||||||
|
<p>
|
||||||
|
<button onClick={this.onHidePopup}>Close</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Popup>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Styling
|
||||||
|
|
||||||
|
react-custom-popup includes CSS file that provides generic styles for the
|
||||||
|
popup. It's recommended to include it in your project's styles.
|
||||||
|
|
||||||
|
You can customize aspects of the popup's layout by passing a custom CSS class
|
||||||
|
using the *className* prop.
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### `Popup`
|
||||||
|
|
||||||
|
This is the custom popup component. It renders the popup in a React portal.
|
||||||
|
|
||||||
|
Example DOM structure rendered by the component:
|
||||||
|
|
||||||
|
```
|
||||||
|
<div className="bthlabs-react-custom-popup">
|
||||||
|
<div className="bthlabs-rcp-overlay" />
|
||||||
|
<div className="bthlabs-rcp-inner">
|
||||||
|
<!-- Children will be rendered here -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
The `div.bthlabs-rcp-inner` will be positioned according to the anchor. If
|
||||||
|
anchor isn't passed, the position will default to `left: 0px; top: 0px;`,
|
||||||
|
unless modified by `onLayout` prop.
|
||||||
|
|
||||||
|
**Props**
|
||||||
|
|
||||||
|
* `anchor` (*React ref*, optional) - a ref to an anchor element which will be
|
||||||
|
used to position the inner layer.
|
||||||
|
* `className` (*string*, optional) - custom CSS class.
|
||||||
|
* `hideOverlay` (*boolean*, optional) - allows the wrapping component to hide
|
||||||
|
the overlay. Defaults to `false`.
|
||||||
|
* `visible` (*boolean*, required) - specifies whether the popup is visible.
|
||||||
|
* `onLayout` (*function*, optional) - callback which allows the wrapping
|
||||||
|
component to modify the inner layer's layout. Read below for more information.
|
||||||
|
* `onOverlayClick` (*function*, optional) - *onClick* handler for the
|
||||||
|
`div.bthlabs-rcp-overlay` element.
|
||||||
|
|
||||||
|
**The onLayout callback**
|
||||||
|
|
||||||
|
The *onLayout* prop allows the wrapping component to modify the inner layer's
|
||||||
|
layout. If specified, it should accept an array of integers (`[<left>, <top>]`)
|
||||||
|
and return an array of either two or more integers
|
||||||
|
(`[<left>, <top>, <width>, <height>]`). The *width* and *height* fields can
|
||||||
|
be omitted.
|
||||||
|
|
||||||
|
Example *onLayout* callback:
|
||||||
|
|
||||||
|
```
|
||||||
|
const onLayout = (layout) => {
|
||||||
|
return [
|
||||||
|
layout[0] + 10, // left
|
||||||
|
layout[1] + 20, // top
|
||||||
|
100, // width
|
||||||
|
200 // height
|
||||||
|
];
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
This function would shift the inner layer by 10px to the right and 20px to the
|
||||||
|
bottom and
|
||||||
|
set its size to 100px of width and 200px of height.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
To bootstrap the development environment, clone the repo and run `npm install`
|
||||||
|
from the root directory.
|
||||||
|
|
||||||
|
The `package.json` file provides the following scripts:
|
||||||
|
|
||||||
|
* `build` - builds the library modules,
|
||||||
|
* `build:example` - builds the example project,
|
||||||
|
* `lint` - performs an eslint run over the source code,
|
||||||
|
* `test` - performs a single test run,
|
||||||
|
* `test:watch` - starts karma with watcher.
|
||||||
|
|
||||||
|
**NOTE**: Tests require *Chromium* to be installed and available in the path.
|
||||||
|
Consult
|
||||||
|
[karma-chrome-launcher](https://github.com/karma-runner/karma-chrome-launcher)
|
||||||
|
docs for more info.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
If you think you found a bug or want to send a patch, feel free to contact
|
||||||
|
me through e-mail.
|
||||||
|
|
||||||
|
If you're sending a patch, make sure it passes eslint checks and is tested.
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
react-custom-popup is developed by [Tomek Wójcik](https://www.bthlabs.pl/).
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
react-custom-popup is licensed under the MIT License.
|
2
example/.gitignore
vendored
Normal file
2
example/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
assets/example.css*
|
||||||
|
assets/example.js*
|
2
example/assets/marx.min.css
vendored
Normal file
2
example/assets/marx.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
108
example/example.js
Normal file
108
example/example.js
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
|
import Popup from '../lib/react-custom-popup.js';
|
||||||
|
|
||||||
|
const CUSTOM_DIMENSION = 500;
|
||||||
|
|
||||||
|
class App extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
this.onCustomLayoutCheckboxClick = this.onCustomLayoutCheckboxClick.bind(this);
|
||||||
|
this.onHideOverlayCheckboxClick = this.onHideOverlayCheckboxClick.bind(this);
|
||||||
|
this.onShowPopupButtonClick = this.onShowPopupButtonClick.bind(this);
|
||||||
|
this.onPopupOverlayClick = this.onPopupOverlayClick.bind(this);
|
||||||
|
|
||||||
|
this.refButton = React.createRef();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
customLayout: false,
|
||||||
|
hideOverlay: false,
|
||||||
|
popupVisible: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
onHideOverlayCheckboxClick (event) {
|
||||||
|
this.setState({hideOverlay: !this.state.hideOverlay});
|
||||||
|
}
|
||||||
|
onCustomLayoutCheckboxClick (event) {
|
||||||
|
this.setState({customLayout: !this.state.customLayout});
|
||||||
|
}
|
||||||
|
onShowPopupButtonClick (event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
this.setState({popupVisible: true});
|
||||||
|
}
|
||||||
|
onPopupOverlayClick (event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
this.setState({popupVisible: false});
|
||||||
|
}
|
||||||
|
onPopupLayout (currentLayout) {
|
||||||
|
|
||||||
|
return [
|
||||||
|
(window.innerWidth - CUSTOM_DIMENSION) / 2,
|
||||||
|
(window.innerHeight - CUSTOM_DIMENSION) / 2,
|
||||||
|
CUSTOM_DIMENSION,
|
||||||
|
CUSTOM_DIMENSION
|
||||||
|
];
|
||||||
|
}
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<h1>React Custom Popup Example</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>Configure</strong>
|
||||||
|
<br/>
|
||||||
|
<input
|
||||||
|
id="checkboxHideOverlay"
|
||||||
|
type="checkbox"
|
||||||
|
defaultValue={this.state.hideOverlay}
|
||||||
|
onClick={this.onHideOverlayCheckboxClick}
|
||||||
|
/>
|
||||||
|
<label htmlFor="checkboxHideOverlay">Hide overlay</label>
|
||||||
|
<br/>
|
||||||
|
<input
|
||||||
|
id="checkboxCustomLayout"
|
||||||
|
type="checkbox"
|
||||||
|
defaultValue={this.state.customLayout}
|
||||||
|
onClick={this.onCustomLayoutCheckboxClick}
|
||||||
|
/>
|
||||||
|
<label htmlFor="checkboxCustomLayout">Custom layout</label>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<button
|
||||||
|
ref={this.refButton}
|
||||||
|
onClick={this.onShowPopupButtonClick}
|
||||||
|
>
|
||||||
|
Show Popup
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<Popup
|
||||||
|
anchor={this.refButton}
|
||||||
|
hideOverlay={this.state.hideOverlay}
|
||||||
|
visible={this.state.popupVisible}
|
||||||
|
onLayout={
|
||||||
|
(this.state.customLayout) ? this.onPopupLayout : undefined
|
||||||
|
}
|
||||||
|
onOverlayClick={this.onPopupOverlayClick}
|
||||||
|
>
|
||||||
|
<div id="popup" className={`${this.state.customLayout ? 'custom-layout' : ''}`}>
|
||||||
|
<h2>Hello, I'm a popup!</h2>
|
||||||
|
<p>
|
||||||
|
<button onClick={this.onPopupOverlayClick}>Close</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Popup>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
ReactDOM.render(
|
||||||
|
<App />, document.getElementById('main')
|
||||||
|
);
|
||||||
|
});
|
43
example/example.scss
Normal file
43
example/example.scss
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
@import "../src/Popup";
|
||||||
|
|
||||||
|
#main {
|
||||||
|
margin: 1em;
|
||||||
|
|
||||||
|
p {
|
||||||
|
input[type="checkbox"] {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#popup {
|
||||||
|
background: white;
|
||||||
|
box-shadow: 0px 0px 2px #ebebeb;
|
||||||
|
border: 1px solid #ebebeb;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
&.custom-layout {
|
||||||
|
height: 500px;
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin-top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bthlabs-react-custom-popup {
|
||||||
|
.bthlabs-rcp-overlay {
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
}
|
16
example/index.html
Normal file
16
example/index.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>React Custom Popup Example</title>
|
||||||
|
<meta name="description" content="React Custom Popup Example">
|
||||||
|
<meta name="author" content="Tomek Wójcik <contact@bthlabs.pl> (https://www.bthlabs.pl/)">
|
||||||
|
<link rel="stylesheet" href="assets/marx.min.css">
|
||||||
|
<link rel="stylesheet" href="assets/example.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="main">
|
||||||
|
</div>
|
||||||
|
<script src="assets/example.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
45
karma.conf.js
Normal file
45
karma.conf.js
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
var defs = require('./package.defs.js');
|
||||||
|
|
||||||
|
module.exports = function (config) {
|
||||||
|
config.set({
|
||||||
|
basePath: '.',
|
||||||
|
browsers: ['Chromium'],
|
||||||
|
frameworks: ['jasmine'],
|
||||||
|
files: [
|
||||||
|
'tests/__entry__.js'
|
||||||
|
],
|
||||||
|
mochaReporter: {
|
||||||
|
ignoreSkipped: true
|
||||||
|
},
|
||||||
|
preprocessors: {
|
||||||
|
'tests/__entry__.js': ['webpack', 'sourcemap']
|
||||||
|
},
|
||||||
|
reporters: ['mocha'],
|
||||||
|
singleRun: false,
|
||||||
|
webpack: {
|
||||||
|
devtool: 'inline-source-map',
|
||||||
|
module: {
|
||||||
|
loaders: [
|
||||||
|
{
|
||||||
|
test: /\.js?/,
|
||||||
|
include: defs.SRC_DIR,
|
||||||
|
loader: ['babel-loader']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.js?/,
|
||||||
|
include: defs.TESTS_DIR,
|
||||||
|
loader: ['babel-loader']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'src': defs.SRC_DIR,
|
||||||
|
'tests': defs.TESTS_DIR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
24
package.defs.js
Normal file
24
package.defs.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/* eslint-env node */
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
var LIB_DIR = path.resolve(__dirname, 'lib');
|
||||||
|
var SRC_DIR = path.resolve(__dirname, 'src');
|
||||||
|
var TESTS_DIR = path.resolve(__dirname, 'tests');
|
||||||
|
|
||||||
|
var EXAMPLE_DIR = path.resolve(__dirname, 'example');
|
||||||
|
var EXAMPLE_ASSETS_DIR = path.resolve(EXAMPLE_DIR, 'assets');
|
||||||
|
|
||||||
|
var LIBRARY_NAME = 'react-custom-popup';
|
||||||
|
var FILENAME_DIST = 'react-custom-popup.js';
|
||||||
|
var CSS_FILENAME_DIST = 'react-custom-popup.css';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
LIB_DIR: LIB_DIR,
|
||||||
|
SRC_DIR: SRC_DIR,
|
||||||
|
LIBRARY_NAME: LIBRARY_NAME,
|
||||||
|
FILENAME_DIST: FILENAME_DIST,
|
||||||
|
EXAMPLE_DIR: EXAMPLE_DIR,
|
||||||
|
EXAMPLE_ASSETS_DIR: EXAMPLE_ASSETS_DIR,
|
||||||
|
CSS_FILENAME_DIST: CSS_FILENAME_DIST,
|
||||||
|
TESTS_DIR: TESTS_DIR
|
||||||
|
};
|
58
package.json
Normal file
58
package.json
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
{
|
||||||
|
"name": "@bthlabs/react-custom-popup",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "./lib/react-custom-popup.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "./node_modules/.bin/grunt dist",
|
||||||
|
"build:example": "./node_modules/.bin/grunt example",
|
||||||
|
"lint": "./node_modules/.bin/grunt eslint",
|
||||||
|
"test": "./node_modules/.bin/grunt karma:dist",
|
||||||
|
"test:watch": "./node_modules/.bin/karma start"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.bthlabs.pl/tomekwojcik/react-custom-popup.git"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
|
"author": "Tomek Wójcik <contact@bthlabs.pl> (https://www.bthlabs.pl/)",
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "7.2.2",
|
||||||
|
"@babel/preset-env": "7.3.1",
|
||||||
|
"@babel/preset-react": "7.0.0",
|
||||||
|
"babel-eslint": "8.2.1",
|
||||||
|
"babel-loader": "8.0.5",
|
||||||
|
"enzyme": "3.5.0",
|
||||||
|
"enzyme-adapter-react-16": "1.3.0",
|
||||||
|
"eslint-plugin-jasmine": "2.9.1",
|
||||||
|
"grunt": "1.0.1",
|
||||||
|
"grunt-contrib-clean": "1.1.0",
|
||||||
|
"grunt-contrib-sass": "1.0.0",
|
||||||
|
"grunt-karma": "2.0.0",
|
||||||
|
"grunt-webpack": "3.0.2",
|
||||||
|
"gruntify-eslint": "4.0.0",
|
||||||
|
"jasmine-core": "2.8.0",
|
||||||
|
"karma": "2.0.0",
|
||||||
|
"karma-chrome-launcher": "2.2.0",
|
||||||
|
"karma-jasmine": "1.1.1",
|
||||||
|
"karma-mocha-reporter": "2.2.5",
|
||||||
|
"karma-sourcemap-loader": "0.3.7",
|
||||||
|
"karma-webpack": "2.0.9",
|
||||||
|
"react": "16.4.0",
|
||||||
|
"react-dom": "16.4.0",
|
||||||
|
"underscore": "1.8.3",
|
||||||
|
"webpack": "3.10.0",
|
||||||
|
"webpack-uglify-js-plugin": "1.1.9"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"classnames": "2.2.6",
|
||||||
|
"prop-types": "15.6.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.4.0",
|
||||||
|
"react-dom": ">=16.4.0"
|
||||||
|
}
|
||||||
|
}
|
91
src/Popup.js
Normal file
91
src/Popup.js
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2019-present Tomek Wójcik <tomek@bthlabs.pl>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ClassName from 'classnames';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
|
export const Popup = (props) => {
|
||||||
|
const className = ClassName('bthlabs-react-custom-popup', props.className, {
|
||||||
|
'bthlabs-rcp-visible': props.visible
|
||||||
|
});
|
||||||
|
|
||||||
|
let layout = [0, 0];
|
||||||
|
const body = document.body;
|
||||||
|
const innerStyle = {};
|
||||||
|
if (props.visible && props.anchor && props.anchor.current) {
|
||||||
|
const box = props.anchor.current.getBoundingClientRect();
|
||||||
|
const document_ = document.documentElement;
|
||||||
|
|
||||||
|
const scrollTop = (
|
||||||
|
window.pageYOffset || document_.scrollTop || body.scrollTop
|
||||||
|
);
|
||||||
|
const scrollLeft = (
|
||||||
|
window.pageXOffset || document_.scrollLeft || body.scrollLeft
|
||||||
|
);
|
||||||
|
|
||||||
|
const clientTop = document_.clientTop || body.clientTop || 0;
|
||||||
|
const clientLeft = document_.clientLeft || body.clientLeft || 0;
|
||||||
|
|
||||||
|
layout[0] = box.left + scrollLeft - clientLeft;
|
||||||
|
layout[1] = box.top + scrollTop - clientTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.visible) {
|
||||||
|
if (typeof props.onLayout === 'function') {
|
||||||
|
layout = props.onLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
innerStyle.left = `${layout[0]}px`;
|
||||||
|
innerStyle.top = `${layout[1]}px`;
|
||||||
|
|
||||||
|
if (layout[2]) {
|
||||||
|
innerStyle.width = `${layout[2]}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layout[3]) {
|
||||||
|
innerStyle.height = `${layout[3]}px`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReactDOM.createPortal(
|
||||||
|
<div className={className}>
|
||||||
|
{!props.hideOverlay &&
|
||||||
|
<div className="bthlabs-rcp-overlay" onClick={props.onOverlayClick} />
|
||||||
|
}
|
||||||
|
<div className="bthlabs-rcp-inner" style={innerStyle}>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
props.domNode || body
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Popup.propTypes = {
|
||||||
|
anchor: PropTypes.object,
|
||||||
|
className: PropTypes.string,
|
||||||
|
hideOverlay: PropTypes.bool,
|
||||||
|
visible: PropTypes.bool.isRequired,
|
||||||
|
onLayout: PropTypes.func,
|
||||||
|
onOverlayClick: PropTypes.func
|
||||||
|
};
|
24
src/Popup.scss
Normal file
24
src/Popup.scss
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/*!
|
||||||
|
* react-custom-popup | Copyright 2019-present Tomek Wójcik <tomek@bthlabs.pl> | MIT License
|
||||||
|
* https://git.bthlabs.pl/tomekwojcik/react-custom-popup
|
||||||
|
*/
|
||||||
|
|
||||||
|
.bthlabs-react-custom-popup {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&.bthlabs-rcp-visible {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bthlabs-rcp-overlay {
|
||||||
|
height: 100vh;
|
||||||
|
left: 0px;
|
||||||
|
position: fixed;
|
||||||
|
top: 0px;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bthlabs-rcp-inner {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
}
|
8
src/index.js
Normal file
8
src/index.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/*!
|
||||||
|
* react-custom-popup | Copyright 2019-present Tomek Wójcik <tomek@bthlabs.pl> | MIT License
|
||||||
|
* https://git.bthlabs.pl/tomekwojcik/react-custom-popup
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Popup} from './Popup';
|
||||||
|
|
||||||
|
export default Popup;
|
191
tests/Popup.spec.js
Normal file
191
tests/Popup.spec.js
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
import {mount, shallow} from 'enzyme';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import {Popup} from 'src/Popup';
|
||||||
|
|
||||||
|
class Wrapper extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.anchorRef = React.createRef();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
popupVisible: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
render () {
|
||||||
|
const buttonStyle = {
|
||||||
|
left: '10px',
|
||||||
|
position: 'relative',
|
||||||
|
top: '10px'
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<button ref={this.anchorRef} style={buttonStyle}>Anchor</button>
|
||||||
|
<Popup anchor={this.anchorRef} visible={this.state.popupVisible}>
|
||||||
|
<span>HERE POPUP CONTENT BE</span>
|
||||||
|
</Popup>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Popup', () => {
|
||||||
|
let mountNode = null;
|
||||||
|
let onLayout = null;
|
||||||
|
let onOverlayClick = null;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
onLayout = jasmine.createSpy('fakeOnLayout');
|
||||||
|
onOverlayClick = jasmine.createSpy('fakeOnOverlayClick');
|
||||||
|
|
||||||
|
mountNode = document.createElement('div');
|
||||||
|
document.body.appendChild(mountNode);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
document.body.removeChild(mountNode);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render with a custom class', () => {
|
||||||
|
const component = shallow(
|
||||||
|
<Popup className='spam' visible={false}>
|
||||||
|
<span>HERE POPUP CONTENT BE</span>
|
||||||
|
</Popup>
|
||||||
|
);
|
||||||
|
|
||||||
|
const popup = component.find('.bthlabs-react-custom-popup');
|
||||||
|
expect(popup.hasClass('spam')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render as invisible', () => {
|
||||||
|
const component = shallow(
|
||||||
|
<Popup className='spam' visible={false}>
|
||||||
|
<span>HERE POPUP CONTENT BE</span>
|
||||||
|
</Popup>
|
||||||
|
);
|
||||||
|
|
||||||
|
const popup = component.find('.bthlabs-react-custom-popup');
|
||||||
|
expect(popup.hasClass('bthlabs-rcp-visible')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render as visible', () => {
|
||||||
|
const component = shallow(
|
||||||
|
<Popup className='spam' visible={true}>
|
||||||
|
<span>HERE POPUP CONTENT BE</span>
|
||||||
|
</Popup>
|
||||||
|
);
|
||||||
|
|
||||||
|
const popup = component.find('.bthlabs-react-custom-popup');
|
||||||
|
expect(popup.hasClass('bthlabs-rcp-visible')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow hiding the overlay', () => {
|
||||||
|
const component = shallow(
|
||||||
|
<Popup hideOverlay={true} visible={false}>
|
||||||
|
<span>HERE POPUP CONTENT BE</span>
|
||||||
|
</Popup>
|
||||||
|
);
|
||||||
|
|
||||||
|
const overlay = component.find('.bthlabs-rcp-overlay');
|
||||||
|
expect(overlay.exists()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should configure and render the overlay', () => {
|
||||||
|
const component = shallow(
|
||||||
|
<Popup visible={false} onOverlayClick={onOverlayClick}>
|
||||||
|
<span>HERE POPUP CONTENT BE</span>
|
||||||
|
</Popup>
|
||||||
|
);
|
||||||
|
|
||||||
|
const overlay = component.find('.bthlabs-rcp-overlay');
|
||||||
|
expect(overlay.exists()).toBe(true);
|
||||||
|
expect(overlay.prop('onClick')).toBe(onOverlayClick);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not layout the inner layer when invisible', () => {
|
||||||
|
const component = shallow(
|
||||||
|
<Popup visible={false}>
|
||||||
|
<span>HERE POPUP CONTENT BE</span>
|
||||||
|
</Popup>
|
||||||
|
);
|
||||||
|
|
||||||
|
const inner = component.find('.bthlabs-rcp-inner');
|
||||||
|
expect(inner.prop('style')).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not call onLayout when invisible', () => {
|
||||||
|
const component = shallow(
|
||||||
|
<Popup visible={false} onLayout={onLayout}>
|
||||||
|
<span>HERE POPUP CONTENT BE</span>
|
||||||
|
</Popup>
|
||||||
|
);
|
||||||
|
|
||||||
|
const inner = component.find('.bthlabs-rcp-inner');
|
||||||
|
expect(onLayout).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply default layout to the inner layer if rendering without anchor', () => {
|
||||||
|
const component = shallow(
|
||||||
|
<Popup visible={true}>
|
||||||
|
<span>HERE POPUP CONTENT BE</span>
|
||||||
|
</Popup>
|
||||||
|
);
|
||||||
|
|
||||||
|
const inner = component.find('.bthlabs-rcp-inner');
|
||||||
|
expect(inner.prop('style')).toEqual({left: '0px', top: '0px'});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should layout the inner layer according to the anchor', () => {
|
||||||
|
const component = mount(<Wrapper />, {attachTo: mountNode});
|
||||||
|
component.setState({popupVisible: true});
|
||||||
|
|
||||||
|
const popup = component.find(Popup);
|
||||||
|
const inner = popup.find('.bthlabs-rcp-inner');
|
||||||
|
expect(inner.prop('style').left).not.toEqual('0px');
|
||||||
|
expect(inner.prop('style').top).not.toEqual('0px');
|
||||||
|
|
||||||
|
component.unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call onLayout to allow for customization of the inner layer layout', () => {
|
||||||
|
onLayout = onLayout.and.returnValue([20, 20]);
|
||||||
|
|
||||||
|
const component = shallow(
|
||||||
|
<Popup visible={true} onLayout={onLayout}>
|
||||||
|
<span>HERE POPUP CONTENT BE</span>
|
||||||
|
</Popup>
|
||||||
|
);
|
||||||
|
|
||||||
|
const inner = component.find('.bthlabs-rcp-inner');
|
||||||
|
expect(onLayout).toHaveBeenCalledWith([0, 0]);
|
||||||
|
expect(inner.prop('style')).toEqual({left: '20px', top: '20px'});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the inner layour height and width if onLayout specified them', () => {
|
||||||
|
onLayout = onLayout.and.returnValue([20, 20, 100, 100]);
|
||||||
|
|
||||||
|
const component = shallow(
|
||||||
|
<Popup visible={true} onLayout={onLayout}>
|
||||||
|
<span>HERE POPUP CONTENT BE</span>
|
||||||
|
</Popup>
|
||||||
|
);
|
||||||
|
|
||||||
|
const inner = component.find('.bthlabs-rcp-inner');
|
||||||
|
expect(onLayout).toHaveBeenCalledWith([0, 0]);
|
||||||
|
expect(inner.prop('style').height).toEqual('100px');
|
||||||
|
expect(inner.prop('style').width).toEqual('100px');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the children in the inner layer', () => {
|
||||||
|
const component = shallow(
|
||||||
|
<Popup visible={false}>
|
||||||
|
<span>HERE POPUP CONTENT BE</span>
|
||||||
|
</Popup>
|
||||||
|
);
|
||||||
|
|
||||||
|
const inner = component.find('.bthlabs-rcp-inner');
|
||||||
|
expect(inner.contains(<span>HERE POPUP CONTENT BE</span>)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
4
tests/__entry__.js
Normal file
4
tests/__entry__.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
require('tests/__setup__/enzyme.setup.js');
|
||||||
|
|
||||||
|
const testsContext = require.context('.', true, /\.spec\.js$/);
|
||||||
|
testsContext.keys().forEach(testsContext);
|
7
tests/__setup__/enzyme.setup.js
Normal file
7
tests/__setup__/enzyme.setup.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import Enzyme from 'enzyme';
|
||||||
|
import Adapter from 'enzyme-adapter-react-16';
|
||||||
|
|
||||||
|
Enzyme.configure({
|
||||||
|
adapter: new Adapter(),
|
||||||
|
disableLifecycleMethods: true,
|
||||||
|
});
|
32
webpack.dist.js
Normal file
32
webpack.dist.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
var webpack = require('webpack');
|
||||||
|
|
||||||
|
var defs = require('./package.defs.js');
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
devtool: 'source-map',
|
||||||
|
entry: path.resolve(defs.SRC_DIR, 'index.js'),
|
||||||
|
output: {
|
||||||
|
path: defs.LIB_DIR,
|
||||||
|
filename: defs.FILENAME_DIST,
|
||||||
|
library: defs.LIBRARY_NAME,
|
||||||
|
libraryTarget: 'umd',
|
||||||
|
umdNamedDefine: true
|
||||||
|
},
|
||||||
|
externals: {
|
||||||
|
react: 'react',
|
||||||
|
'react-dom': 'react-dom'
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
loaders: [
|
||||||
|
{
|
||||||
|
test: /\.js?/,
|
||||||
|
include: [defs.SRC_DIR],
|
||||||
|
loader: ['babel-loader']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = config;
|
25
webpack.example.js
Normal file
25
webpack.example.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
var webpack = require('webpack');
|
||||||
|
|
||||||
|
var defs = require('./package.defs.js');
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
devtool: 'source-map',
|
||||||
|
entry: path.resolve(defs.EXAMPLE_DIR, 'example.js'),
|
||||||
|
output: {
|
||||||
|
path: defs.EXAMPLE_ASSETS_DIR,
|
||||||
|
filename: 'example.js'
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
loaders: [
|
||||||
|
{
|
||||||
|
test: /\.js?/,
|
||||||
|
include: [defs.EXAMPLE_DIR, defs.SRC_DIR],
|
||||||
|
loader: ['babel-loader']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = config;
|
Loading…
Reference in New Issue
Block a user