Commit e5c79f3b by Ari Rizzitano Committed by GitHub

Merge pull request #2 from edx/ari/dropdown

Dropdown component
parents b530f2e6 dde0cc24
...@@ -7,5 +7,7 @@ ...@@ -7,5 +7,7 @@
}], }],
"babel-preset-react" "babel-preset-react"
], ],
"plugins": ["transform-object-rest-spread"] "plugins": [
"transform-object-rest-spread"
]
} }
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
"jsx": true "jsx": true
} }
}, },
"rules": {
"react/no-array-index-key": "off"
},
"settings": { "settings": {
"import/resolver": "webpack" "import/resolver": "webpack"
}, },
......
yarn.lock binary
...@@ -76,12 +76,81 @@ exports[`Storyshots CheckBox disabled 1`] = ` ...@@ -76,12 +76,81 @@ exports[`Storyshots CheckBox disabled 1`] = `
</label> </label>
`; `;
exports[`Storyshots Dropdown basic usage 1`] = `
<div
className="paragon-component dropdown"
>
<button
aria-expanded={false}
aria-haspopup="true"
buttonType="secondary"
className="btn border-0 dropdown-toggle btn-secondary"
classNames={
Array [
"border-0",
"dropdown-toggle",
]
}
display="Search Engines"
inputRef={[Function]}
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Search Engines
</button>
<ul
aria-hidden={true}
aria-label="Search Engines"
className="dropdown-menu"
role="menu"
>
<li
className="dropdown-item"
>
<a
href="https://google.com"
onKeyDown={[Function]}
role="menuitem"
tabIndex="-1"
>
Google
</a>
</li>
<li
className="dropdown-item"
>
<a
href="https://duckduckgo.com"
onKeyDown={[Function]}
role="menuitem"
tabIndex="-1"
>
DuckDuckGo
</a>
</li>
<li
className="dropdown-item"
>
<a
href="https://yahoo.com"
onKeyDown={[Function]}
role="menuitem"
tabIndex="-1"
>
Yahoo
</a>
</li>
</ul>
</div>
`;
exports[`Storyshots InputSelect basic usage 1`] = ` exports[`Storyshots InputSelect basic usage 1`] = `
<div <div
className="form-group" className="form-group"
> >
<label <label
className=""
htmlFor="textInput5" htmlFor="textInput5"
> >
Fruits Fruits
...@@ -124,7 +193,6 @@ exports[`Storyshots InputSelect separate labels and values 1`] = ` ...@@ -124,7 +193,6 @@ exports[`Storyshots InputSelect separate labels and values 1`] = `
className="form-group" className="form-group"
> >
<label <label
className=""
htmlFor="textInput6" htmlFor="textInput6"
> >
New England States New England States
...@@ -177,7 +245,6 @@ exports[`Storyshots InputSelect separate option groups 1`] = ` ...@@ -177,7 +245,6 @@ exports[`Storyshots InputSelect separate option groups 1`] = `
className="form-group" className="form-group"
> >
<label <label
className=""
htmlFor="textInput7" htmlFor="textInput7"
> >
Northeast States Northeast States
...@@ -278,7 +345,6 @@ exports[`Storyshots InputSelect with validation 1`] = ` ...@@ -278,7 +345,6 @@ exports[`Storyshots InputSelect with validation 1`] = `
className="form-group" className="form-group"
> >
<label <label
className=""
htmlFor="textInput8" htmlFor="textInput8"
> >
Favorite Color Favorite Color
...@@ -336,7 +402,6 @@ exports[`Storyshots InputText minimal usage 1`] = ` ...@@ -336,7 +402,6 @@ exports[`Storyshots InputText minimal usage 1`] = `
className="form-group" className="form-group"
> >
<label <label
className=""
htmlFor="textInput9" htmlFor="textInput9"
> >
First Name First Name
...@@ -363,7 +428,6 @@ exports[`Storyshots InputText validation 1`] = ` ...@@ -363,7 +428,6 @@ exports[`Storyshots InputText validation 1`] = `
className="form-group" className="form-group"
> >
<label <label
className=""
htmlFor="textInput10" htmlFor="textInput10"
> >
Username Username
......
// you can use this file to add your custom webpack plugins, loaders and anything you like. const path = require('path');
// This is just the basic way to add addional webpack configurations.
// For more information refer the docs: https://getstorybook.io/docs/configurations/custom-webpack-config
// IMPORTANT
// When you add this file, we won't add the default configurations which is similar
// to "React Create App". This only has babel loader to load JavaScript.
module.exports = { module.exports = {
plugins: [ devtool: "source-map",
// your custom plugins
],
module: { module: {
loaders: [ rules: [
// add your custom loaders. {
test: /\.scss$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[name]__[local]___[hash:base64:5]',
sourceMap: true,
},
},
{
loader: 'sass-loader',
options: {
data: '@import "paragon-reset";',
includePaths: [
path.join(__dirname, '../src/utils'),
],
sourceMap: true,
},
},
],
},
], ],
}, },
}; };
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
"name": "paragon", "name": "paragon",
"version": "0.0.1", "version": "0.0.1",
"description": "Accessible, responsive UI component library based on Bootstrap.", "description": "Accessible, responsive UI component library based on Bootstrap.",
"main": "dist/paragon.min.js", "main": "src/index.js",
"author": "arizzitano", "author": "arizzitano",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
"test": "jest --coverage" "test": "jest --coverage"
}, },
"dependencies": { "dependencies": {
"bootstrap": "4.0.0-alpha.6",
"classnames": "^2.2.5", "classnames": "^2.2.5",
"prop-types": "^15.5.8", "prop-types": "^15.5.8",
"react": "^15.5.4", "react": "^15.5.4",
...@@ -36,7 +37,9 @@ ...@@ -36,7 +37,9 @@
"babel-preset-env": "^1.4.0", "babel-preset-env": "^1.4.0",
"babel-preset-es2015": "^6.24.1", "babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1", "babel-preset-react": "^6.24.1",
"babili-webpack-plugin": "^0.1.1",
"coveralls": "^2.13.1", "coveralls": "^2.13.1",
"css-loader": "^0.28.4",
"enzyme": "^2.8.2", "enzyme": "^2.8.2",
"eslint": "^3.19.0", "eslint": "^3.19.0",
"eslint-config-airbnb": "^14.1.0", "eslint-config-airbnb": "^14.1.0",
...@@ -45,11 +48,26 @@ ...@@ -45,11 +48,26 @@
"eslint-plugin-jsx-a11y": "^4.0.0", "eslint-plugin-jsx-a11y": "^4.0.0",
"eslint-plugin-react": "^6.10.3", "eslint-plugin-react": "^6.10.3",
"husky": "^0.13.4", "husky": "^0.13.4",
"identity-obj-proxy": "^3.0.0",
"jest": "^20.0.4", "jest": "^20.0.4",
"node-sass": "^4.5.3",
"postcss-scss": "^1.0.1",
"react-router-dom": "^4.1.1", "react-router-dom": "^4.1.1",
"react-test-renderer": "^15.6.1", "react-test-renderer": "^15.6.1",
"sass-loader": "^6.0.5",
"source-map-loader": "^0.2.1", "source-map-loader": "^0.2.1",
"style-loader": "^0.18.2",
"webpack": "^2.4.1", "webpack": "^2.4.1",
"webpack-dev-server": "^2.4.4" "webpack-dev-server": "^2.4.4"
},
"jest": {
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
"\\.(css|scss)$": "identity-obj-proxy"
},
"coveragePathIgnorePatterns": [
"/node_modules/",
"(.stories)\\.(jsx)$"
]
} }
} }
import React from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import buttons from 'bootstrap/scss/_buttons.scss';
function Button(props) {
return (
<button
className={classNames([
buttons.btn,
...props.classNames,
], {
[buttons[`btn-${props.buttonType}`]]: props.buttonType !== undefined,
})}
onBlur={props.onBlur}
onClick={props.onClick}
onKeyDown={props.onKeyDown}
type={props.type}
ref={props.inputRef}
{...props}
>
{props.display}
</button>
);
}
Button.propTypes = {
buttonType: PropTypes.string,
classNames: PropTypes.arrayOf(PropTypes.string),
display: PropTypes.string.isRequired,
inputRef: PropTypes.func,
onBlur: PropTypes.func,
onClick: PropTypes.func,
onKeyDown: PropTypes.func,
type: PropTypes.string,
};
Button.defaultProps = {
buttonType: undefined,
classNames: [],
inputRef: () => {},
onBlur: () => {},
onClick: () => {},
onKeyDown: () => {},
type: 'button',
};
export default Button;
/* eslint-disable import/no-extraneous-dependencies */
import React from 'react';
import { storiesOf } from '@storybook/react';
import Dropdown from './index';
storiesOf('Dropdown', module)
.add('basic usage', () => (
<Dropdown
title="Search Engines"
menuItems={[
{
label: 'Google',
href: 'https://google.com',
},
{
label: 'DuckDuckGo',
href: 'https://duckduckgo.com',
},
{
label: 'Yahoo',
href: 'https://yahoo.com',
},
]}
/>
));
/* eslint-disable import/no-extraneous-dependencies */
import React from 'react';
import { shallow } from 'enzyme';
import Dropdown from './index';
const props = {
title: 'Example',
menuItems: [
{ label: 'Example 1', href: 'http://example1.com' },
{ label: 'Example 2', href: 'http://example2.com' },
],
};
describe('<Dropdown />', () => {
it('renders correctly', () => {
const wrapper = shallow(
<Dropdown
{...props}
/>,
);
const menu = wrapper.find('ul');
const button = wrapper.find('[type="button"]');
expect(button.exists()).toEqual(true);
expect(button.prop('aria-expanded')).toEqual(false);
expect(menu.exists()).toEqual(true);
expect(menu.find('li')).toHaveLength(2);
expect(menu.prop('aria-label')).toEqual(props.title);
expect(menu.prop('aria-hidden')).toEqual(true);
});
it('renders correctly', () => {
const wrapper = shallow(
<Dropdown
{...props}
/>,
);
expect(wrapper.find('[type="button"]').exists()).toEqual(true);
expect(wrapper.find('li')).toHaveLength(2);
expect(wrapper.find('[aria-expanded=false]').exists()).toEqual(true);
});
it('opens on click', () => {
const wrapper = shallow(
<Dropdown
{...props}
/>,
);
const button = wrapper.find('[type="button"]');
button.simulate('click');
expect(wrapper.find('[aria-hidden=false]').exists()).toEqual(true);
});
});
import React from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import dd from 'bootstrap/scss/_dropdown.scss';
import borders from 'bootstrap/scss/utilities/_borders.scss';
import pc from '../utils/base-styles.scss';
import Button from '../Button';
const triggerKeys = {
OPEN_MENU: ['ArrowDown', 'Space'],
CLOSE_MENU: ['Escape'],
NAVIGATE_DOWN: ['ArrowDown', 'Tab'],
NAVIGATE_UP: ['ArrowUp'],
};
class Dropdown extends React.Component {
static isTriggerKey(action, keyName) {
return triggerKeys[action].indexOf(keyName) > -1;
}
constructor(props) {
super(props);
this.addEvents = this.addEvents.bind(this);
this.handleDocumentClick = this.handleDocumentClick.bind(this);
this.handleToggleKeyDown = this.handleToggleKeyDown.bind(this);
this.handleMenuKeyDown = this.handleMenuKeyDown.bind(this);
this.removeEvents = this.removeEvents.bind(this);
this.toggle = this.toggle.bind(this);
this.menuItems = [];
this.state = {
open: false,
focusIndex: 0,
};
}
componentWillUpdate(_, nextState) {
if (nextState.open) {
this.addEvents();
} else {
this.removeEvents();
}
}
componentDidUpdate() {
if (this.state.open && this.menuItems.length > 0) {
this.menuItems[this.state.focusIndex].focus();
} else if (this.toggleElem) {
this.toggleElem.focus();
}
}
addEvents() {
document.addEventListener('click', this.handleDocumentClick, true);
}
removeEvents() {
document.removeEventListener('click', this.handleDocumentClick, true);
}
handleDocumentClick(e) {
if (this.container && this.container.contains(e.target) && this.container !== e.target) {
return;
}
this.toggle();
}
handleMenuKeyDown(e) {
e.preventDefault();
if (Dropdown.isTriggerKey('CLOSE_MENU', e.key)) {
this.toggle();
} else if (Dropdown.isTriggerKey('NAVIGATE_DOWN', e.key)) {
this.setState({
focusIndex: (this.state.focusIndex + 1) % this.props.menuItems.length,
});
} else if (Dropdown.isTriggerKey('NAVIGATE_UP', e.key)) {
this.setState({
focusIndex: ((this.state.focusIndex - 1) + this.props.menuItems.length) %
this.props.menuItems.length,
});
}
}
handleToggleKeyDown(e) {
if (!this.state.open && Dropdown.isTriggerKey('OPEN_MENU', e.key)) {
this.toggle();
} else if (this.state.open && Dropdown.isTriggerKey('CLOSE_MENU', e.key)) {
this.toggle();
}
}
toggle() {
this.setState({
open: !this.state.open,
focusIndex: 0,
});
}
generateMenuItems(menuItems) {
return menuItems.map((menuItem, i) => (
<li className={dd['dropdown-item']} key={i}>
<a
role="menuitem"
href={menuItem.href}
onKeyDown={this.handleMenuKeyDown}
ref={(item) => {
this.menuItems[i] = item;
}}
tabIndex="-1"
>
{menuItem.label}
</a>
</li>
));
}
render() {
const menuItems = this.generateMenuItems(this.props.menuItems);
return (
<div
className={classNames([
pc['paragon-component'],
dd.dropdown,
{ [dd.show]: this.state.open },
])}
ref={(container) => { this.container = container; }}
>
<Button
aria-expanded={this.state.open}
aria-haspopup="true"
buttonType="secondary"
display={this.props.title}
onClick={this.toggle}
onKeyDown={this.handleToggleKeyDown}
classNames={[
borders['border-0'],
dd['dropdown-toggle'],
]}
type="button"
inputRef={(toggleElem) => { this.toggleElem = toggleElem; }}
/>
<ul
aria-label={this.props.title}
aria-hidden={!this.state.open}
className={dd['dropdown-menu']}
role="menu"
>
{menuItems}
</ul>
</div>
);
}
}
Dropdown.propTypes = {
title: PropTypes.string.isRequired,
menuItems: PropTypes.arrayOf(PropTypes.shape({
label: PropTypes.string,
href: PropTypes.string,
})).isRequired,
};
export default Dropdown;
import Button from './Button';
import CheckBox from './CheckBox'; import CheckBox from './CheckBox';
import Dropdown from './Dropdown';
import InputSelect from './InputSelect'; import InputSelect from './InputSelect';
import InputText from './InputText'; import InputText from './InputText';
import Tabs from './Tabs'; import Tabs from './Tabs';
export { export {
Button,
CheckBox, CheckBox,
Dropdown,
InputSelect, InputSelect,
InputText, InputText,
Tabs, Tabs,
......
/* eslint-disable react/no-unused-prop-types */ /* eslint-disable react/no-unused-prop-types */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Label, FormGroup, FormFeedback, FormText } from 'reactstrap'; import { FormGroup, FormFeedback, FormText } from 'reactstrap';
import newId from './newId'; import newId from './newId';
...@@ -86,7 +86,7 @@ const asInput = (WrappedComponent) => { ...@@ -86,7 +86,7 @@ const asInput = (WrappedComponent) => {
return ( return (
<FormGroup> <FormGroup>
<Label for={this.state.id}>{this.props.label}</Label> <label htmlFor={this.state.id}>{this.props.label}</label>
<WrappedComponent <WrappedComponent
{...this.props} {...this.props}
{...this.state} {...this.state}
......
.paragon-component {
@import "~bootstrap/scss/_reboot";
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
box-sizing: border-box;
line-height: 1.15;
font-size: $font-size-base;
font-weight: $font-weight-base;
line-height: $line-height-base;
margin: 0;
color: $body-color;
background-color: $body-bg;
}
@import "~bootstrap/scss/variables";
@import "~bootstrap/scss/mixins";
// Variable Overrides
$font-family-base: inherit;
$font-family-sans-serif: inherit;
$font-family-serif: inherit;
// Because Bootstrap styles are based on rems, the root HTML element's
// font size determines the size of the components. Because the consumer
// may want to render Paragon components alongside existing UI, we provide
// the option to scale all rem-based variables within Paragon based on
// their own base rem value.
// It's worth looking into doing this on-the-fly via postcss or something
// similar, so we don't have to hardcode the overrides.
// (font size on root element / default (16px))
$base-rem-size: 0.625;
$spacer: $spacer / $base-rem-size;
$font-size-base: $font-size-base / $base-rem-size;
$font-size-lg: $font-size-lg / $base-rem-size;
$font-size-sm: $font-size-sm / $base-rem-size;
$font-size-xs: $font-size-xs / $base-rem-size;
$font-size-h1: $font-size-h1 / $base-rem-size;
$font-size-h2: $font-size-h2 / $base-rem-size;
$font-size-h3: $font-size-h3 / $base-rem-size;
$font-size-h4: $font-size-h4 / $base-rem-size;
$font-size-h5: $font-size-h5 / $base-rem-size;
$font-size-h6: $font-size-h6 / $base-rem-size;
$display1-size: $display1-size / $base-rem-size;
$display2-size: $display2-size / $base-rem-size;
$display3-size: $display3-size / $base-rem-size;
$display4-size: $display4-size / $base-rem-size;
$lead-font-size: $lead-font-size / $base-rem-size;
$blockquote-border-width: $blockquote-border-width / $base-rem-size;
$kbd-box-shadow: $kbd-box-shadow / $base-rem-size;
$border-radius: $border-radius / $base-rem-size;
$border-radius-lg: $border-radius-lg / $base-rem-size;
$border-radius-sm: $border-radius-sm / $base-rem-size;
$table-cell-padding: $table-cell-padding / $base-rem-size;
$table-sm-cell-padding: $table-sm-cell-padding / $base-rem-size;
$btn-padding-x: $btn-padding-x / $base-rem-size;
$btn-padding-y: $btn-padding-y / $base-rem-size;
$btn-padding-x-sm: $btn-padding-x-sm / $base-rem-size;
$btn-padding-y-sm: $btn-padding-y-sm / $base-rem-size;
$btn-padding-x-lg: $btn-padding-x-lg / $base-rem-size;
$btn-padding-y-lg: $btn-padding-y-lg / $base-rem-size;
$btn-block-spacing-y: $btn-block-spacing-y / $base-rem-size;
$btn-toolbar-margin: $btn-toolbar-margin / $base-rem-size;
$input-padding-x: $input-padding-x / $base-rem-size;
$input-padding-y: $input-padding-y / $base-rem-size;
$input-padding-x-sm: $input-padding-x-sm / $base-rem-size;
$input-padding-y-sm: $input-padding-y-sm / $base-rem-size;
$input-padding-x-lg: $input-padding-x-lg / $base-rem-size;
$input-padding-y-lg: $input-padding-y-lg / $base-rem-size;
$form-text-margin-top: $form-text-margin-top / $base-rem-size;
$form-check-margin-bottom: $form-check-margin-bottom / $base-rem-size;
$form-check-input-gutter: $form-check-input-gutter / $base-rem-size;
$form-check-input-margin-y: $form-check-input-margin-y / $base-rem-size;
$form-check-input-margin-x: $form-check-input-margin-x / $base-rem-size;
$form-check-inline-margin-x: $form-check-inline-margin-x / $base-rem-size;
$custom-control-gutter: $custom-control-gutter / $base-rem-size;
$custom-control-spacer-x: $custom-control-spacer-x / $base-rem-size;
$custom-control-spacer-y: $custom-control-spacer-y / $base-rem-size;
$custom-control-indicator-size: $custom-control-indicator-size / $base-rem-size;
$custom-control-indicator-margin-y: $custom-control-indicator-margin-y / $base-rem-size;
$custom-control-indicator-box-shadow: $custom-control-indicator-box-shadow / $base-rem-size;
$custom-select-padding-x: $custom-select-padding-x / $base-rem-size;
$custom-select-padding-y: $custom-select-padding-y / $base-rem-size;
$custom-select-indicator-padding: $custom-select-indicator-padding / $base-rem-size;
$custom-select-sm-padding-y: $custom-select-sm-padding-y / $base-rem-size;
$custom-file-height: $custom-file-height / $base-rem-size;
$custom-file-width: $custom-file-width / $base-rem-size;
$custom-file-focus-box-shadow: $custom-file-focus-box-shadow / $base-rem-size;
$custom-file-padding-x: $custom-file-padding-x / $base-rem-size;
$custom-file-padding-y: $custom-file-padding-y / $base-rem-size;
$custom-file-box-shadow: $custom-file-box-shadow / $base-rem-size;
$dropdown-min-width: $dropdown-min-width / $base-rem-size;
$dropdown-padding-y: $dropdown-padding-y / $base-rem-size;
$dropdown-margin-top: $dropdown-margin-top / $base-rem-size;
$dropdown-box-shadow: $dropdown-box-shadow / $base-rem-size;
$dropdown-item-padding-x: $dropdown-item-padding-x / $base-rem-size;
$navbar-brand-padding-y: $navbar-brand-padding-y / $base-rem-size;
$navbar-toggler-padding-x: $navbar-toggler-padding-x / $base-rem-size;
$navbar-toggler-padding-y: $navbar-toggler-padding-y / $base-rem-size;
$nav-item-margin: $nav-item-margin / $base-rem-size;
$nav-item-inline-spacer: $nav-item-inline-spacer / $base-rem-size;
$pagination-padding-x: $pagination-padding-x / $base-rem-size;
$pagination-padding-y: $pagination-padding-y / $base-rem-size;
$pagination-padding-x-sm: $pagination-padding-x-sm / $base-rem-size;
$pagination-padding-y-sm: $pagination-padding-y-sm / $base-rem-size;
$pagination-padding-x-lg: $pagination-padding-x-lg / $base-rem-size;
$pagination-padding-y-lg: $pagination-padding-y-lg / $base-rem-size;
$jumbotron-padding: $jumbotron-padding / $base-rem-size;
$card-spacer-x: $card-spacer-x / $base-rem-size;
$card-spacer-y: $card-spacer-y / $base-rem-size;
$card-img-overlay-padding: $card-img-overlay-padding / $base-rem-size;
$card-columns-gap: $card-columns-gap / $base-rem-size;
$badge-pill-border-radius: $badge-pill-border-radius / $base-rem-size;
$alert-padding-x: $alert-padding-x / $base-rem-size;
$alert-padding-y: $alert-padding-y / $base-rem-size;
$progress-height: $progress-height / $base-rem-size;
$progress-font-size: $progress-font-size / $base-rem-size;
$progress-box-shadow: $progress-box-shadow / $base-rem-size;
$list-group-item-padding-x: $list-group-item-padding-x / $base-rem-size;
$list-group-item-padding-y: $list-group-item-padding-y / $base-rem-size;
$thumbnail-padding: $thumbnail-padding / $base-rem-size;
$breadcrumb-padding-y: $breadcrumb-padding-y / $base-rem-size;
$breadcrumb-padding-x: $breadcrumb-padding-x / $base-rem-size;
$breadcrumb-item-padding: $breadcrumb-item-padding / $base-rem-size;
$code-padding-x: $code-padding-x / $base-rem-size;
$code-padding-y: $code-padding-y / $base-rem-size;
const path = require('path'); const path = require('path');
const BabiliPlugin = require('babili-webpack-plugin');
const env = process.env.NODE_ENV || 'dev'; const env = process.env.NODE_ENV || 'dev';
...@@ -15,6 +16,9 @@ const base = { ...@@ -15,6 +16,9 @@ const base = {
resolve: { resolve: {
extensions: ['.js', '.jsx'], extensions: ['.js', '.jsx'],
}, },
plugins: [
new BabiliPlugin(),
],
module: { module: {
rules: [ rules: [
{ {
...@@ -30,41 +34,43 @@ const base = { ...@@ -30,41 +34,43 @@ const base = {
{ loader: 'source-map-loader' }, { loader: 'source-map-loader' },
], ],
}, },
{
test: /\.scss$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[name]__[local]___[hash:base64:5]',
},
},
{
loader: 'sass-loader',
options: {
data: '@import "paragon-reset";',
includePaths: [
path.join(__dirname, './src/utils'),
],
},
},
],
},
], ],
}, },
}; };
const additionalConfig = { const additionalConfig = {
// dev runs the doc site locally.
dev: {
devServer: {
contentBase: path.resolve('./docs'),
historyApiFallback: true,
stats: {
chunks: false,
},
},
node: {
fs: 'empty',
},
module: {
loaders: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loaders: [
'babel-loader?cacheDirectory',
],
},
],
},
},
// production builds the library for external consumption // production builds the library for external consumption
production: { production: {
entry: path.resolve('./src/index.js'), entry: {
Dropdown: path.resolve('./src/Dropdown.jsx'),
},
output: { output: {
path: path.resolve('./dist'), path: path.resolve('./dist'),
filename: 'paragon.min.js', filename: '[name].js',
library: 'paragon', library: 'paragon',
libraryTarget: 'umd', libraryTarget: 'umd',
}, },
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment