Commit b4be5070 by Ari Rizzitano Committed by GitHub

Merge pull request #12 from edx/excise-reactstrap

excise reactstrap
parents d592aea4 e48cf637
......@@ -78,12 +78,12 @@ exports[`Storyshots CheckBox disabled 1`] = `
exports[`Storyshots Dropdown basic usage 1`] = `
<div
className="paragon-component dropdown"
className="dropdown"
>
<button
aria-expanded={false}
aria-haspopup="true"
className="btn border-0 dropdown-toggle btn-secondary"
className="btn-borderless dropdown-toggle btn btn-secondary"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
......@@ -153,6 +153,7 @@ exports[`Storyshots InputSelect basic usage 1`] = `
name="fruits"
onBlur={[Function]}
onChange={[Function]}
type="select"
value="strawberry"
>
<option
......@@ -195,6 +196,7 @@ exports[`Storyshots InputSelect separate labels and values 1`] = `
name="new-england-states"
onBlur={[Function]}
onChange={[Function]}
type="select"
value="RI"
>
<option
......@@ -247,6 +249,7 @@ exports[`Storyshots InputSelect separate option groups 1`] = `
name="northeast-states"
onBlur={[Function]}
onChange={[Function]}
type="select"
value="MD"
>
<optgroup
......@@ -347,6 +350,7 @@ exports[`Storyshots InputSelect with validation 1`] = `
name="color"
onBlur={[Function]}
onChange={[Function]}
type="select"
value=""
>
<option
......@@ -448,31 +452,132 @@ exports[`Storyshots InputText validation 1`] = `
exports[`Storyshots Paragon Welcome 1`] = `
<div
className="css-jail"
style={
Object {
"fontFamily": "\\"Helvetica Neue\\", Helvetica, \\"Segoe UI\\", Arial, freesans, sans-serif",
"lineHeight": 1.4,
"margin": 15,
"maxWidth": 600,
"fontFamily": "sans-serif",
}
}
>
<h1>
💎 Paragon
</h1>
<p>
This is a documentation and demo space for the Paragon accessible UI component library. Better docs coming soon, but for now, check out our existing components via the links to the left-hand side.
</p>
<p>
Documentation generated with
<a
href="https://github.com/kadirahq/react-storybook"
rel="noopener noreferrer"
target="_blank"
<div
style={
Object {
"fontFamily": "\\"Helvetica Neue\\", Helvetica, \\"Segoe UI\\", Arial, freesans, sans-serif",
"lineHeight": 1.4,
"margin": 15,
"maxWidth": 600,
}
}
>
<h1>
💎 Paragon
</h1>
<p>
This is a documentation and demo space for the Paragon accessible UI component library. Better docs coming soon, but for now, check out our existing components via the links to the left-hand side.
</p>
<p>
Documentation generated with
<a
href="https://github.com/kadirahq/react-storybook"
rel="noopener noreferrer"
target="_blank"
>
React Storybook
</a>
.
</p>
</div>
</div>
`;
exports[`Storyshots Tabs basic usage 1`] = `
<div>
<ul
className="nav nav-tabs"
role="tablist"
>
<li
className="nav-item"
id="tab-label-tabInterface11-0"
>
<a
aria-controls="tab-panel-tabInterface11-0"
aria-selected={true}
className="nav-link active"
onClick={[Function]}
role="tab"
tabIndex={0}
>
Panel 1
</a>
</li>
<li
className="nav-item"
id="tab-label-tabInterface11-1"
>
<a
aria-controls="tab-panel-tabInterface11-1"
aria-selected={false}
className="nav-link"
onClick={[Function]}
role="tab"
tabIndex={-1}
>
Panel 2
</a>
</li>
<li
className="nav-item"
id="tab-label-tabInterface11-2"
>
<a
aria-controls="tab-panel-tabInterface11-2"
aria-selected={false}
className="nav-link"
onClick={[Function]}
role="tab"
tabIndex={-1}
>
Panel 3
</a>
</li>
</ul>
<div
className="tab-content"
>
<div
aria-hidden={false}
aria-labelledby="tab-label-tabInterface11-0"
className="tab-pane active"
id="tab-panel-tabInterface11-0"
role="tabpanel"
>
<div>
Hello I am the first panel
</div>
</div>
<div
aria-hidden={true}
aria-labelledby="tab-label-tabInterface11-1"
className="tab-pane"
id="tab-panel-tabInterface11-1"
role="tabpanel"
>
<div>
Hello I am the second panel
</div>
</div>
<div
aria-hidden={true}
aria-labelledby="tab-label-tabInterface11-2"
className="tab-pane"
id="tab-panel-tabInterface11-2"
role="tabpanel"
>
React Storybook
</a>
.
</p>
<div>
Hello I am the third panel
</div>
</div>
</div>
</div>
`;
import { configure } from '@storybook/react';
import React from 'react';
import { configure, addDecorator } from '@storybook/react';
import { setOptions } from '@storybook/addon-options';
import CssJail from '../src/CssJail';
setOptions({
name: '💎 PARAGON',
url: 'https://github.com/edx/paragon',
......@@ -9,6 +12,12 @@ setOptions({
const req = require.context('../src', true, /\.stories\.jsx$/);
addDecorator(story => (
<CssJail>
{story()}
</CssJail>
));
function loadStories() {
require('./Paragon.stories.jsx');
req.keys().forEach((filename) => req(filename));
......
......@@ -22,8 +22,7 @@
"prop-types": "^15.5.8",
"react": "^15.5.4",
"react-dom": "^15.5.4",
"react-transition-group": "^1.1.2",
"reactstrap": "^4.6.2"
"react-transition-group": "^1.1.2"
},
"devDependencies": {
"@storybook/addon-options": "^3.1.6",
......
@import "~bootstrap/scss/_buttons";
......@@ -2,7 +2,7 @@ import React from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import buttons from 'bootstrap/scss/_buttons.scss';
import styles from './Button.scss';
function Button(props) {
const {
......@@ -20,19 +20,19 @@ function Button(props) {
return (
<button
className={classNames([
buttons.btn,
...props.className,
...className,
styles.btn,
], {
[buttons[`btn-${props.buttonType}`]]: props.buttonType !== undefined,
[styles[`btn-${buttonType}`]]: buttonType !== undefined,
})}
onBlur={props.onBlur}
onClick={props.onClick}
onKeyDown={props.onKeyDown}
type={props.type}
ref={props.inputRef}
onBlur={onBlur}
onClick={onClick}
onKeyDown={onKeyDown}
type={type}
ref={inputRef}
{...other}
>
{props.display}
{display}
</button>
);
}
......
.css-jail {
@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 React from 'react';
import PropTypes from 'prop-types';
import styles from './CssJail.scss';
function CssJail({ children }) {
return (
<div
style={{ fontFamily: 'sans-serif' }}
className={styles['css-jail']}
>
{children}
</div>
);
}
CssJail.propTypes = {
children: PropTypes.element.isRequired,
};
export default CssJail;
@import "~bootstrap/scss/_dropdown";
.btn-borderless {
border: 0 !important;
}
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 styles from './Dropdown.scss';
import Button from '../Button';
export const triggerKeys = {
......@@ -100,7 +98,7 @@ class Dropdown extends React.Component {
generateMenuItems(menuItems) {
return menuItems.map((menuItem, i) => (
<li className={dd['dropdown-item']} key={i}>
<li className={styles['dropdown-item']} key={i}>
<a
role="menuitem"
href={menuItem.href}
......@@ -121,9 +119,8 @@ class Dropdown extends React.Component {
return (
<div
className={classNames([
pc['paragon-component'],
dd.dropdown,
{ [dd.show]: this.state.open },
styles.dropdown,
{ [styles.show]: this.state.open },
])}
ref={(container) => { this.container = container; }}
>
......@@ -135,8 +132,8 @@ class Dropdown extends React.Component {
onClick={this.toggle}
onKeyDown={this.handleToggleKeyDown}
className={[
borders['border-0'],
dd['dropdown-toggle'],
styles['btn-borderless'],
styles['dropdown-toggle'],
]}
type="button"
inputRef={(toggleElem) => { this.toggleElem = toggleElem; }}
......@@ -144,7 +141,7 @@ class Dropdown extends React.Component {
<ul
aria-label={this.props.title}
aria-hidden={!this.state.open}
className={dd['dropdown-menu']}
className={styles['dropdown-menu']}
role="menu"
>
{menuItems}
......
import React from 'react';
import { Input } from 'reactstrap';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import asInput, { inputProps } from '../asInput';
......@@ -40,8 +40,9 @@ class Select extends React.Component {
const options = this.getOptions();
return (
<Input
<select
id={props.id}
className={classNames(props.className)}
type="select"
name={props.name}
value={props.value}
......@@ -50,7 +51,7 @@ class Select extends React.Component {
onBlur={props.onBlur}
>
{options}
</Input>
</select>
);
}
}
......
import React from 'react';
import { Input } from 'reactstrap';
import classNames from 'classnames';
import asInput, { inputProps } from '../asInput';
function Text(props) {
return (
<Input
<input
id={props.id}
className={classNames(props.className)}
type="text"
name={props.name}
value={props.value}
......@@ -17,7 +18,6 @@ function Text(props) {
aria-invalid={!props.isValid}
disabled={props.disabled}
required={props.required}
state={props.inputState}
/>
);
}
......
@import "~bootstrap/scss/_nav";
/* eslint-disable import/no-extraneous-dependencies */
import React from 'react';
import { storiesOf } from '@storybook/react';
import Tabs from './index';
storiesOf('Tabs', module)
.add('basic usage', () => (
<Tabs
labels={[
'Panel 1',
'Panel 2',
'Panel 3',
]}
>
<div>Hello I am the first panel</div>
<div>Hello I am the second panel</div>
<div>Hello I am the third panel</div>
</Tabs>
));
/* eslint-disable import/no-extraneous-dependencies */
import React from 'react';
import { shallow } from 'enzyme';
import Tabs from './index';
const props = {
labels: [
'first',
'second',
'third',
],
children: [
<div>first</div>,
<div>second</div>,
<div>third</div>,
],
};
const tabSelectedAtIndex = (index, wrapper) => {
wrapper.find('a').forEach((node, i) => {
expect(node.prop('aria-selected')).toEqual(i === index);
});
wrapper.find('.tab-pane').forEach((node, i) => {
expect(node.hasClass('active')).toEqual(i === index);
});
};
describe('<Tabs />', () => {
it('renders with first tab selected', () => {
const wrapper = shallow(
<Tabs
{...props}
/>,
);
tabSelectedAtIndex(0, wrapper);
});
describe('switches tab selection', () => {
it('on click', () => {
const wrapper = shallow(
<Tabs
{...props}
/>,
);
wrapper.find('a').forEach((node, i) => {
node.simulate('click');
tabSelectedAtIndex(i, wrapper);
});
});
});
});
import React from 'react';
import { TabContent, TabPane, Nav, NavItem, NavLink } from 'reactstrap';
import classnames from 'classnames';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import { newId } from '../utils/newId';
import styles from './Tabs.scss';
import newId from '../utils/newId';
class Tabs extends React.Component {
constructor(props) {
......@@ -33,46 +33,53 @@ class Tabs extends React.Component {
}
buildLabels() {
return this.props.tabLabels.map((label, i) => {
return this.props.labels.map((label, i) => {
const selected = this.state.activeTab === i;
const labelId = this.genLabelId(i);
return (
<NavItem
aria-selected={selected}
aria-controls={this.genPanelId(i)}
<li
className={styles['nav-item']}
id={labelId}
key={labelId}
role="tab"
tabIndex={selected ? 0 : -1}
>
<NavLink
className={classnames({ active: selected })}
<a
aria-selected={selected}
aria-controls={this.genPanelId(i)}
className={classNames(
styles['nav-link'],
{ [styles.active]: selected },
)}
onClick={() => { this.toggle(i); }}
role="tab"
tabIndex={selected ? 0 : -1}
>
{label}
</NavLink>
</NavItem>
</a>
</li>
);
});
}
buildPanels() {
return this.props.panels.map((panel, i) => {
return this.props.children.map((panel, i) => {
const selected = this.state.activeTab === i;
const panelId = this.genPanelId(i);
return (
<TabPane
<div
aria-hidden={!selected}
aria-labelledby={this.genLabelId(i)}
className={classNames(
styles['tab-pane'],
{ [styles.active]: selected },
)}
id={panelId}
key={panelId}
role="tabpanel"
tabId={i}
>
{panel}
</TabPane>
</div>
);
});
}
......@@ -83,24 +90,30 @@ class Tabs extends React.Component {
return (
<div>
<Nav tabs role="tablist">
<ul
className={classNames([
styles.nav,
styles['nav-tabs'],
])}
role="tablist"
>
{labels}
</Nav>
<TabContent activeTab={this.state.activeTab}>
</ul>
<div className={styles['tab-content']}>
{panels}
</TabContent>
</div>
</div>
);
}
}
// TODO: custom validator that ensures tabLabels and panels are the same length
// TODO: custom validator that ensures labels and panels are the same length
Tabs.propTypes = {
tabLabels: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.String),
PropTypes.arrayOf(PropTypes.Element),
labels: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.string),
PropTypes.arrayOf(PropTypes.element),
]).isRequired,
panels: PropTypes.arrayOf(PropTypes.Element).isRequired,
children: PropTypes.arrayOf(PropTypes.element).isRequired,
};
export default Tabs;
@import "~bootstrap/scss/_forms";
/* eslint-disable react/no-unused-prop-types */
import React from 'react';
import PropTypes from 'prop-types';
import { FormGroup, FormFeedback, FormText } from 'reactstrap';
import newId from '../utils/newId';
import styles from './asInput.scss';
export const getDisplayName = WrappedComponent =>
WrappedComponent.displayName || WrappedComponent.name || 'Component';
......@@ -21,6 +21,7 @@ export const inputProps = {
onChange: PropTypes.func,
onBlur: PropTypes.func,
validator: PropTypes.func,
className: PropTypes.arrayOf(PropTypes.string),
};
const asInput = (WrappedComponent) => {
......@@ -49,18 +50,18 @@ const asInput = (WrappedComponent) => {
if (!this.state.isValid) {
desc.error = (
<FormFeedback id={errorId} key="0">
<div className={styles['form-control-feedback']} id={errorId} key="0">
{this.state.validationMessage}
</FormFeedback>
</div>
);
desc.describedBy = errorId;
}
if (this.props.description) {
desc.description = (
<FormText id={descriptionId} key="1">
<small className={styles['form-text']} id={descriptionId} key="1">
{this.props.description}
</FormText>
</small>
);
desc.describedBy = `${desc.describedBy} ${descriptionId}`.trim();
}
......@@ -86,18 +87,22 @@ const asInput = (WrappedComponent) => {
const { description, error, describedBy } = this.getDescriptions();
return (
<FormGroup>
<div className={styles['form-group']}>
<label htmlFor={this.state.id}>{this.props.label}</label>
<WrappedComponent
{...this.props}
{...this.state}
className={[
styles['form-control'],
...this.props.className,
]}
describedBy={describedBy}
onChange={this.handleChange}
onBlur={this.handleBlur}
/>
{error}
{description}
</FormGroup>
</div>
);
}
}
......@@ -114,6 +119,7 @@ const asInput = (WrappedComponent) => {
disabled: false,
required: false,
validator: undefined,
className: [],
};
return NewComponent;
......
......@@ -17,7 +17,7 @@ $font-family-serif: inherit;
// similar, so we don't have to hardcode the overrides.
// (font size on root element / default (16px))
$base-rem-size: 0.625;
$base-rem-size: 1 !default;
$spacer: $spacer / $base-rem-size;
$font-size-base: $font-size-base / $base-rem-size;
......
......@@ -1806,7 +1806,7 @@ clap@^1.0.9:
dependencies:
chalk "^1.1.3"
classnames@^2.2.3, classnames@^2.2.5:
classnames@^2.2.5:
version "2.2.5"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d"
......@@ -4507,14 +4507,6 @@ lodash.isarray@^3.0.0:
version "3.0.4"
resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55"
lodash.isfunction@^3.0.8:
version "3.0.8"
resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.8.tgz#4db709fc81bc4a8fd7127a458a5346c5cdce2c6b"
lodash.isobject@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d"
lodash.isplainobject@^4.0.6:
version "4.0.6"
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
......@@ -4567,10 +4559,6 @@ lodash.tail@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664"
lodash.tonumber@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/lodash.tonumber/-/lodash.tonumber-4.0.3.tgz#0b96b31b35672793eb7f5a63ee791f1b9e9025d9"
lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
......@@ -5948,21 +5936,6 @@ react@^15.5.4:
object-assign "^4.1.0"
prop-types "^15.5.10"
reactstrap-tether@1.3.4:
version "1.3.4"
resolved "https://registry.yarnpkg.com/reactstrap-tether/-/reactstrap-tether-1.3.4.tgz#86d94d30216ffa34ceb2c626f4b9912c0d193894"
reactstrap@^4.6.2:
version "4.8.0"
resolved "https://registry.yarnpkg.com/reactstrap/-/reactstrap-4.8.0.tgz#eb350642a6282c79c7e1bd8d6f75c627131a42f6"
dependencies:
classnames "^2.2.3"
lodash.isfunction "^3.0.8"
lodash.isobject "^3.0.2"
lodash.tonumber "^4.0.3"
prop-types "^15.5.8"
reactstrap-tether "1.3.4"
read-pkg-up@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
......@@ -6134,18 +6107,18 @@ repeating@^2.0.0:
dependencies:
is-finite "^1.0.0"
request@2, request@2.79.0, request@^2.79.0:
version "2.79.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de"
request@2, request@^2.79.0, request@^2.81.0:
version "2.81.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
dependencies:
aws-sign2 "~0.6.0"
aws4 "^1.2.1"
caseless "~0.11.0"
caseless "~0.12.0"
combined-stream "~1.0.5"
extend "~3.0.0"
forever-agent "~0.6.1"
form-data "~2.1.1"
har-validator "~2.0.6"
har-validator "~4.2.1"
hawk "~3.1.3"
http-signature "~1.1.0"
is-typedarray "~1.0.0"
......@@ -6153,24 +6126,26 @@ request@2, request@2.79.0, request@^2.79.0:
json-stringify-safe "~5.0.1"
mime-types "~2.1.7"
oauth-sign "~0.8.1"
qs "~6.3.0"
performance-now "^0.2.0"
qs "~6.4.0"
safe-buffer "^5.0.1"
stringstream "~0.0.4"
tough-cookie "~2.3.0"
tunnel-agent "~0.4.1"
tunnel-agent "^0.6.0"
uuid "^3.0.0"
request@^2.81.0:
version "2.81.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
request@2.79.0:
version "2.79.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de"
dependencies:
aws-sign2 "~0.6.0"
aws4 "^1.2.1"
caseless "~0.12.0"
caseless "~0.11.0"
combined-stream "~1.0.5"
extend "~3.0.0"
forever-agent "~0.6.1"
form-data "~2.1.1"
har-validator "~4.2.1"
har-validator "~2.0.6"
hawk "~3.1.3"
http-signature "~1.1.0"
is-typedarray "~1.0.0"
......@@ -6178,12 +6153,10 @@ request@^2.81.0:
json-stringify-safe "~5.0.1"
mime-types "~2.1.7"
oauth-sign "~0.8.1"
performance-now "^0.2.0"
qs "~6.4.0"
safe-buffer "^5.0.1"
qs "~6.3.0"
stringstream "~0.0.4"
tough-cookie "~2.3.0"
tunnel-agent "^0.6.0"
tunnel-agent "~0.4.1"
uuid "^3.0.0"
require-directory@^2.1.1:
......
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