Optional Mount Parameters (#123)

* adding optional parameters

* updating bodyText mount prop to default

* updating snapshot for new props

* adding parent element for mounting

* adding null default for parentElement

* adding dist to gitignore

* adding test case for mounting to element

* adding div to document in test

* unmount after test

* updating test to pass

* updating eslint to handle certain rules as warning and fix others
This commit is contained in:
Gavin Foster 2019-06-20 12:26:02 -05:00 committed by Benjamin Boudreau
parent d00d6af82a
commit 309bfd6a5b
12 changed files with 77 additions and 24 deletions

View File

@ -7,7 +7,10 @@
"extends": "airbnb",
"rules": {
"no-underscore-dangle": 0,
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }]
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
"import/prefer-default-export": 1,
"import/no-extraneous-dependencies": 1,
"no-await-in-loop": 1
},
"plugins": ["react"],
"settings": {

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
node_modules
coverage
/dist/*

View File

@ -1,8 +1,8 @@
module.exports = {
process() {
return 'module.exports = {};'
return 'module.exports = {};';
},
getCacheKey() {
return 'cssTransform'
return 'cssTransform';
},
}
};

View File

@ -1,7 +1,7 @@
const path = require('path')
const path = require('path');
module.exports = {
process(src, filename) {
return `module.exports = ${JSON.stringify(path.basename(filename))};`
return `module.exports = ${JSON.stringify(path.basename(filename))};`;
},
}
};

View File

@ -8,7 +8,8 @@
"start": "webpack-dev-server",
"test": "jest",
"test-update-snapshots": "jest --updateSnapshot",
"deploy": "npm run build && gh-pages -d dist"
"deploy": "npm run build && gh-pages -d dist",
"lint": "./node_modules/.bin/eslint ."
},
"babel": {
"presets": [

View File

@ -8,7 +8,9 @@
<script src="./widget.js"></script>
<script>
EmbeddableWidget.mount();
EmbeddableWidget.mount({
bodyText: 'Body'
});
</script>
</body>
</html>

View File

@ -1,7 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Widget /> open/close 1`] = `
<Widget>
<Widget
bodyText="Body"
footerText="Footer"
headerText="Header"
>
<div
className="docked-widget"
>
@ -65,7 +69,11 @@ exports[`<Widget /> open/close 1`] = `
`;
exports[`<Widget /> open/close 2`] = `
<Widget>
<Widget
bodyText="Body"
footerText="Footer"
headerText="Header"
>
<div
className="docked-widget"
>
@ -121,7 +129,11 @@ exports[`<Widget /> open/close 2`] = `
`;
exports[`<Widget /> open/close 3`] = `
<Widget>
<Widget
bodyText="Body"
footerText="Footer"
headerText="Header"
>
<div
className="docked-widget"
>

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Transition } from 'react-transition-group';
import './widget.scss';
@ -47,6 +48,7 @@ class Widget extends Component {
render() {
const { opened } = this.state;
const body = this.renderBody();
const { bodyText, headerText, footerText } = this.props;
return (
<div className="docked-widget">
@ -55,7 +57,7 @@ class Widget extends Component {
<div className={`widget widget-${status}`}>
<div className="widget-header">
<div className="widget-header-title">
Header
{headerText}
</div>
<button
type="button"
@ -67,10 +69,10 @@ class Widget extends Component {
</button>
</div>
<div className="widget-body">
Body
{bodyText}
</div>
<div className="widget-footer">
Footer
{footerText}
</div>
</div>
)}
@ -81,4 +83,16 @@ class Widget extends Component {
}
}
Widget.propTypes = {
headerText: PropTypes.string,
bodyText: PropTypes.string,
footerText: PropTypes.string,
};
Widget.defaultProps = {
headerText: 'Header',
bodyText: 'Body',
footerText: 'Footer',
};
export default Widget;

View File

@ -33,5 +33,4 @@ describe('<Widget />', () => {
expect(widgetDom).toMatchSnapshot();
});
});

View File

@ -6,8 +6,8 @@ import '../../vendor/cleanslate.css';
export default class EmbeddableWidget {
static el;
static mount() {
const component = <Widget />;
static mount({ parentElement = null, ...props } = {}) {
const component = <Widget {...props} />;
function doRender() {
if (EmbeddableWidget.el) {
@ -15,7 +15,12 @@ export default class EmbeddableWidget {
}
const el = document.createElement('div');
el.setAttribute('class', 'cleanslate');
if (parentElement) {
document.querySelector(parentElement).appendChild(el);
} else {
document.body.appendChild(el);
}
ReactDOM.render(
component,
el,

View File

@ -21,6 +21,20 @@ describe('EmbeddableWidget', () => {
await waitForSelection(document, 'div');
});
test('#mount to document element', async () => {
const newElement = document.createElement('span');
newElement.setAttribute('id', 'widget-mount');
document.body.appendChild(newElement);
EmbeddableWidget.mount({
parentElement: '#widget-mount',
});
await waitForSelection(document, 'div');
expect(document.querySelectorAll('#widget-mount')).toHaveLength(1);
});
test('#mount twice', async () => {
EmbeddableWidget.mount();
expect(() => EmbeddableWidget.mount()).toThrow('already mounted');

View File

@ -1,5 +1,4 @@
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const increaseSpecificity = require('postcss-increase-specificity');
const JavaScriptObfuscator = require('webpack-obfuscator');
const CopyPlugin = require('copy-webpack-plugin');
@ -14,7 +13,7 @@ const defaultConfig = {
mode: process.env.NODE_ENV || 'development',
devServer: {
contentBase: publicDir,
port: 9000
port: 9000,
},
plugins: [
// new CleanWebpackPlugin({protectWebpackAssets: false}),
@ -39,7 +38,10 @@ const defaultConfig = {
{
test: /\.js$/,
exclude: /node_modules/,
use: ['eslint-loader']
loader: 'eslint-loader',
options: {
emitWarning: true,
},
},
{
test: /\.(scss|css)$/,