From f961d11de7aeee74cb8411867e68ee0788d7c456 Mon Sep 17 00:00:00 2001 From: Levi Thomason Date: Sat, 16 Jul 2016 08:46:43 -0700 Subject: [PATCH 1/8] chore(eslint): disable react/sort-comp --- .eslintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index 48fce67c6c..e68e987a27 100644 --- a/.eslintrc +++ b/.eslintrc @@ -7,7 +7,7 @@ "complexity": [1, 10], "consistent-return": 0, "no-confusing-arrow": 0, - "react/sort-comp": 1, + "react/sort-comp": 0, "valid-jsdoc": 0, "react/jsx-curly-spacing": 0 } From 218733d9cc57723e124a557cb6d14cc241367e0b Mon Sep 17 00:00:00 2001 From: Levi Thomason Date: Sat, 16 Jul 2016 08:48:14 -0700 Subject: [PATCH 2/8] feat(ComponentDescription): show sui doc link on children --- .../Components/ComponentDoc/ComponentDescription.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/app/Components/ComponentDoc/ComponentDescription.js b/docs/app/Components/ComponentDoc/ComponentDescription.js index 7913934e80..225d38904e 100644 --- a/docs/app/Components/ComponentDoc/ComponentDescription.js +++ b/docs/app/Components/ComponentDoc/ComponentDescription.js @@ -25,10 +25,12 @@ export default class ComponentDescription extends Component { renderSemanticDocsLink = () => { const { _meta } = this.props - if (!META.isSemanticUI(_meta) || !META.isParent(_meta)) { - return null - } - const url = `http://semantic-ui.com/${_meta.type}s/${_meta.name}.html`.toLowerCase() + + if (!META.isSemanticUI(_meta)) return null + + const name = META.isParent(_meta) ? _meta.name : _meta.parent + const url = `http://semantic-ui.com/${_meta.type}s/${name}.html`.toLowerCase() + return ( From 26bc58a32a5947c2a4b2ed407f34348ec0b99008 Mon Sep 17 00:00:00 2001 From: Levi Thomason Date: Sat, 16 Jul 2016 08:49:06 -0700 Subject: [PATCH 3/8] fix(ComponentDoc): hide empty prop tables --- docs/app/Components/ComponentDoc/ComponentDoc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/app/Components/ComponentDoc/ComponentDoc.js b/docs/app/Components/ComponentDoc/ComponentDoc.js index 244cc3a69d..5b2316c54b 100644 --- a/docs/app/Components/ComponentDoc/ComponentDoc.js +++ b/docs/app/Components/ComponentDoc/ComponentDoc.js @@ -21,6 +21,7 @@ const ComponentDoc = ({ _meta }) => { docPath={docPath} /> + {docgen.props && } ) From f5ca5525cb0165e47add6390f73de068231c7a29 Mon Sep 17 00:00:00 2001 From: Levi Thomason Date: Sat, 16 Jul 2016 08:50:31 -0700 Subject: [PATCH 4/8] fix(AutoControlledComponent): default checked/value state --- src/utils/AutoControlledComponent.js | 18 ++++++++++++++++-- .../utils/AutoControlledComponent-test.js | 16 ++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/utils/AutoControlledComponent.js b/src/utils/AutoControlledComponent.js index 9bf5e6d14f..a992d973da 100644 --- a/src/utils/AutoControlledComponent.js +++ b/src/utils/AutoControlledComponent.js @@ -40,8 +40,22 @@ export default class AutoControlledComponent extends Component { this.state = _.transform(autoControlledProps, (res, prop) => { const defaultPropName = getDefaultPropName(prop) - // apply default props if they exist - res[prop] = this.props[defaultPropName in this.props ? defaultPropName : prop] + // try to set initial state in this order: + // - default props + // - then, regular props + // - then, `checked` defaults to false + // - then, `value` defaults to null + // React doesn't allow changing from uncontrolled to controlled components + // this is why we default checked/value if they are not present. + if (_.has(this.props, defaultPropName)) { + res[prop] = this.props[defaultPropName] + } else if (_.has(this.props, prop)) { + res[prop] = this.props[prop] + } else if (prop === 'checked') { + res[prop] = false + } else if (prop === 'value') { + res[prop] = '' + } if (process.env.NODE_ENV !== 'production') { const { name } = this.constructor diff --git a/test/specs/utils/AutoControlledComponent-test.js b/test/specs/utils/AutoControlledComponent-test.js index b29360f746..5e3db2798e 100644 --- a/test/specs/utils/AutoControlledComponent-test.js +++ b/test/specs/utils/AutoControlledComponent-test.js @@ -116,6 +116,22 @@ describe('extending AutoControlledComponent', () => { _.each(props, (val, key) => wrapper.should.not.have.state(key, val)) }) + + it('defaults "checked" to false if not present', () => { + consoleUtil.disableOnce() + TestClass.autoControlledProps.push('checked') + + shallow() + .should.have.state('checked', false) + }) + + it('defaults "value" to an empty string if not present', () => { + consoleUtil.disableOnce() + TestClass.autoControlledProps.push('value') + + shallow() + .should.have.state('value', '') + }) }) describe('default props', () => { From 8b8aae8fa67f7282f4d0c019ae6a0abb530629bb Mon Sep 17 00:00:00 2001 From: Levi Thomason Date: Sat, 16 Jul 2016 08:51:14 -0700 Subject: [PATCH 5/8] feat(commonTests): add event suggestion --- test/specs/commonTests.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/specs/commonTests.js b/test/specs/commonTests.js index 53d630f624..b80c2824cc 100644 --- a/test/specs/commonTests.js +++ b/test/specs/commonTests.js @@ -170,7 +170,8 @@ export const isConformant = (Component, requiredProps = {}) => { handlerSpy.called.should.equal(true, `<${constructorName} ${listenerName}={${handlerName}} />\n` + - `${leftPad} ^ was not called on "${eventName}"\n` + `${leftPad} ^ was not called on "${eventName}".` + + 'You may need to hoist your event handlers up to the root element.\n' ) // TODO: /~https://github.com/TechnologyAdvice/stardust/issues/218 From 7875b5cb71858c96f2c6456a01f8919aa2a6d56a Mon Sep 17 00:00:00 2001 From: Levi Thomason Date: Sat, 16 Jul 2016 08:51:53 -0700 Subject: [PATCH 6/8] refactor(Accordion-test): reword test --- test/specs/modules/Accordion/Accordion-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/specs/modules/Accordion/Accordion-test.js b/test/specs/modules/Accordion/Accordion-test.js index 5d5874ea0c..a166031ebc 100644 --- a/test/specs/modules/Accordion/Accordion-test.js +++ b/test/specs/modules/Accordion/Accordion-test.js @@ -35,7 +35,7 @@ describe('Accordion', () => { titles.at(1).should.have.className('active') contents.at(1).should.have.className('active') }) - it('Accordion.Content is active at activeIndex - 1', () => { + it('makes Accordion.Content at activeIndex - 1 "active"', () => { const contents = shallow( From 5dddd76f7b99726243d66685660e620e4aede5d2 Mon Sep 17 00:00:00 2001 From: Levi Thomason Date: Sat, 16 Jul 2016 08:53:46 -0700 Subject: [PATCH 7/8] feat(Checkbox): remove jquery and update to v1 --- .../States/CheckboxRemoteControlExample.js | 7 +- .../modules/Checkbox/States/Checked.js | 2 +- .../modules/Checkbox/States/Disabled.js | 4 +- .../modules/Checkbox/States/ReadOnly.js | 2 +- .../modules/Checkbox/States/States.js | 8 +- .../Examples/modules/Checkbox/Types/Radio.js | 4 +- .../modules/Checkbox/Types/RadioGroup.js | 24 ++- .../Examples/modules/Checkbox/Types/Slider.js | 2 +- .../Examples/modules/Checkbox/Types/Toggle.js | 2 +- .../Examples/modules/Checkbox/Types/Types.js | 7 + .../modules/Checkbox/Variations/Fitted.js | 4 +- .../modules/Checkbox/Variations/Variations.js | 4 +- src/modules/Checkbox/Checkbox.js | 179 ++++++++++++------ src/utils/jquery.js | 3 - test/specs/modules/Checkbox/Checkbox-test.js | 133 +++++++++++-- 15 files changed, 288 insertions(+), 97 deletions(-) diff --git a/docs/app/Examples/modules/Checkbox/States/CheckboxRemoteControlExample.js b/docs/app/Examples/modules/Checkbox/States/CheckboxRemoteControlExample.js index 7d025811f6..b37ccb8dc8 100644 --- a/docs/app/Examples/modules/Checkbox/States/CheckboxRemoteControlExample.js +++ b/docs/app/Examples/modules/Checkbox/States/CheckboxRemoteControlExample.js @@ -2,15 +2,14 @@ import React, { Component } from 'react' import { Button, Checkbox } from 'stardust' export default class CheckboxRemoteControlExample extends Component { - toggle = () => { - this.refs.checkbox.plugin('toggle') - }; + state = { checked: false } + toggle = () => this.setState({ checked: !this.state.checked }) render() { return (
- +
) } diff --git a/docs/app/Examples/modules/Checkbox/States/Checked.js b/docs/app/Examples/modules/Checkbox/States/Checked.js index ba2ac1325e..6c0481a5c7 100644 --- a/docs/app/Examples/modules/Checkbox/States/Checked.js +++ b/docs/app/Examples/modules/Checkbox/States/Checked.js @@ -3,6 +3,6 @@ import { Checkbox } from 'stardust' export default CheckboxCheckedExample => { return ( - + ) } diff --git a/docs/app/Examples/modules/Checkbox/States/Disabled.js b/docs/app/Examples/modules/Checkbox/States/Disabled.js index e41d9709e8..0af8285746 100644 --- a/docs/app/Examples/modules/Checkbox/States/Disabled.js +++ b/docs/app/Examples/modules/Checkbox/States/Disabled.js @@ -6,10 +6,10 @@ export default class CheckboxDisabledExample extends Component { return (
- + - +
) diff --git a/docs/app/Examples/modules/Checkbox/States/ReadOnly.js b/docs/app/Examples/modules/Checkbox/States/ReadOnly.js index 01b9df65fb..4058b959af 100644 --- a/docs/app/Examples/modules/Checkbox/States/ReadOnly.js +++ b/docs/app/Examples/modules/Checkbox/States/ReadOnly.js @@ -4,7 +4,7 @@ import { Checkbox } from 'stardust' export default class CheckboxReadOnlyExample extends Component { render() { return ( - + ) } } diff --git a/docs/app/Examples/modules/Checkbox/States/States.js b/docs/app/Examples/modules/Checkbox/States/States.js index d2fe6daae7..f687f1395b 100644 --- a/docs/app/Examples/modules/Checkbox/States/States.js +++ b/docs/app/Examples/modules/Checkbox/States/States.js @@ -13,13 +13,11 @@ export default class CheckboxStatesExamples extends Component { examplePath='modules/Checkbox/States/Checked' > - Use -   + Use{' '}
defaultChecked -   - as you normally would to set default form values. + {' '}as you normally would to set default form values. diff --git a/docs/app/Examples/modules/Checkbox/Types/Radio.js b/docs/app/Examples/modules/Checkbox/Types/Radio.js index 032b3b87c4..bf8fcaeb50 100644 --- a/docs/app/Examples/modules/Checkbox/Types/Radio.js +++ b/docs/app/Examples/modules/Checkbox/Types/Radio.js @@ -4,9 +4,7 @@ import { Checkbox } from 'stardust' export default class CheckboxRadioExample extends Component { render() { return ( -
- -
+ ) } } diff --git a/docs/app/Examples/modules/Checkbox/Types/RadioGroup.js b/docs/app/Examples/modules/Checkbox/Types/RadioGroup.js index 3af9fe33aa..dbce004d87 100644 --- a/docs/app/Examples/modules/Checkbox/Types/RadioGroup.js +++ b/docs/app/Examples/modules/Checkbox/Types/RadioGroup.js @@ -2,14 +2,34 @@ import React, { Component } from 'react' import { Form, Checkbox } from 'stardust' export default class CheckboxRadioGroupExample extends Component { + state = {} + handleClick = (e, { value }) => this.setState({ value }) + render() { return (
- + Selected value: {this.state.value} + + + - +
) diff --git a/docs/app/Examples/modules/Checkbox/Types/Slider.js b/docs/app/Examples/modules/Checkbox/Types/Slider.js index e6a2a765d5..879474f9de 100644 --- a/docs/app/Examples/modules/Checkbox/Types/Slider.js +++ b/docs/app/Examples/modules/Checkbox/Types/Slider.js @@ -4,7 +4,7 @@ import { Checkbox } from 'stardust' export default class CheckboxSliderExample extends Component { render() { return ( - + ) } } diff --git a/docs/app/Examples/modules/Checkbox/Types/Toggle.js b/docs/app/Examples/modules/Checkbox/Types/Toggle.js index 9de6bac65d..44acb1a3ed 100644 --- a/docs/app/Examples/modules/Checkbox/Types/Toggle.js +++ b/docs/app/Examples/modules/Checkbox/Types/Toggle.js @@ -4,7 +4,7 @@ import { Checkbox } from 'stardust' export default class CheckboxToggleExample extends Component { render() { return ( - + ) } } diff --git a/docs/app/Examples/modules/Checkbox/Types/Types.js b/docs/app/Examples/modules/Checkbox/Types/Types.js index 88f91e95e1..22ab2afb1f 100644 --- a/docs/app/Examples/modules/Checkbox/Types/Types.js +++ b/docs/app/Examples/modules/Checkbox/Types/Types.js @@ -2,10 +2,17 @@ import React, { Component } from 'react' import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample' import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection' +import { Message } from 'stardust' + export default class CheckboxTypesExamples extends Component { render() { return ( + + All checkbox types use an input with type checkbox, except for type radio. + {' '}Use inputType if you'd like to mix and match style and behavior. + {' '}For instance, type slider with inputType radio for exclusive sliders. + - + - + ) diff --git a/docs/app/Examples/modules/Checkbox/Variations/Variations.js b/docs/app/Examples/modules/Checkbox/Variations/Variations.js index 55759bc9d7..e883acfce5 100644 --- a/docs/app/Examples/modules/Checkbox/Variations/Variations.js +++ b/docs/app/Examples/modules/Checkbox/Variations/Variations.js @@ -13,11 +13,11 @@ export default class CheckboxVariationsExamples extends Component { examplePath='modules/Checkbox/Variations/Fitted' > - The  + The{' '} fitted -  class is automatically applied if there is no label prop. + {' '}class is automatically applied if there is no label prop. diff --git a/src/modules/Checkbox/Checkbox.js b/src/modules/Checkbox/Checkbox.js index f064c9e22c..85966f7c1e 100644 --- a/src/modules/Checkbox/Checkbox.js +++ b/src/modules/Checkbox/Checkbox.js @@ -1,86 +1,151 @@ import _ from 'lodash' +import React, { PropTypes } from 'react' +import cx from 'classnames' + +import AutoControlledComponent from '../../utils/AutoControlledComponent' import META from '../../utils/Meta' +import { makeDebugger } from '../../utils/debug' import { getUnhandledProps } from '../../utils/propUtils' -import React, { Component, PropTypes } from 'react' -import classNames from 'classnames' -import $ from 'jquery' -export default class Checkbox extends Component { +const debug = makeDebugger('checkbox') + +// maps checkbox types to input types +const typeMap = { + checkbox: 'checkbox', + radio: 'radio', + slider: 'checkbox', + toggle: 'checkbox', +} + +const _meta = { + library: META.library.semanticUI, + name: 'Checkbox', + type: META.type.module, + props: { + inputType: [ + 'checkbox', + 'radio', + ], + type: [ + 'checkbox', + 'radio', + 'slider', + 'toggle', + ], + }, +} + +/** + * A checkbox allows a user to select a value from a small set of options, often binary + * @see Radio + */ +export default class Checkbox extends AutoControlledComponent { static propTypes = { - beforeChecked: PropTypes.func, - beforeDeterminate: PropTypes.func, - beforeIndeterminate: PropTypes.func, - beforeUnchecked: PropTypes.func, className: PropTypes.string, + + /** Whether or not checkbox is checked. */ + checked: PropTypes.bool, + + /** The initial value of checked. */ + defaultChecked: PropTypes.bool, + + /** Removes padding for a label. Auto applied when there is no label. */ + fitted: PropTypes.bool, + + /** The text of the associated label element. */ label: PropTypes.string, + + /** HTML input type, either checkbox or radio. */ + inputType: PropTypes.oneOf(_meta.props.inputType), + + /** Called with (event, { name, value, checked }) when the checkbox or label is clicked. */ + onClick: PropTypes.func, + + /** + * Display as a checkbox, radio, slider, or toggle. + * The input type is `checkbox` for both slider and toggle types. + * You can set `inputType` separately to mix and match appearance and behavior. + */ + type: PropTypes.oneOf(_meta.props.type), + + /** The HTML input name. */ + name: PropTypes.string, + + /** The HTML input value. */ + value: PropTypes.string, + + /** Called with (event, { name, value, checked }) when the user attempts to change the value. */ onChange: PropTypes.func, - onChecked: PropTypes.func, - onDeterminate: PropTypes.func, - onDisable: PropTypes.func, - onEnable: PropTypes.func, - onIndeterminate: PropTypes.func, - onUnchecked: PropTypes.func, - type: PropTypes.string, } static defaultProps = { type: 'checkbox', } - componentDidMount() { - this.element = $(this.refs.element) - this.element.checkbox({ - onChange: this.props.onChange, - onChecked: this.props.onChecked, - onIndeterminate: this.props.onIndeterminate, - onDeterminate: this.props.onDeterminate, - onUnchecked: this.props.onUnchecked, - beforeChecked: this.props.beforeChecked, - beforeIndeterminate: this.props.beforeIndeterminate, - beforeDeterminate: this.props.beforeDeterminate, - beforeUnchecked: this.props.beforeUnchecked, - onEnable: this.props.onEnable, - onDisable: this.props.onDisable, - }) - } + static autoControlledProps = [ + 'checked', + ] - componentWillUnmount() { - _.invoke(this, 'element.off') - } + static _meta = _meta - static _meta = { - library: META.library.semanticUI, - name: 'Checkbox', - type: META.type.module, - } + state = {} - handleChange = (e) => { - const { onChange } = this.props - if (onChange) onChange(e) - } + handleClick = (e) => { + debug('handleClick()') + const { disabled, onChange, onClick, name, readOnly, value } = this.props + // using a ref here allows us to let the browser manage radio group state for us + // this is a special exception where we are reading state from the DOM + // otherwise, all radio groups would have to be controlled components + const refChecked = _.get(this.refs, 'input.checked') + debug(` name: ${name}`) + debug(` value: ${value}`) + debug(` refChecked: ${refChecked}`) + + if (onClick) onClick(e, { name, value, checked: !!refChecked }) + if (onChange) onChange(e, { name, value, checked: !refChecked }) - plugin(...args) { - return this.element.checkbox(...args) + if (!disabled && !readOnly) { + this.trySetState({ checked: !refChecked }) + } } render() { - let type = this.props.type - if (_.includes(this.props.className, 'radio')) { - type = 'radio' - } - const classes = classNames( + const { className, inputType, label, name, onChange, type, value } = this.props + const { checked } = this.state + const classes = cx( 'ui', - this.props.className, + // don't add duplicate "checkbox" classes, but add any other type + type !== 'checkbox' && type, // auto apply fitted class to compact white space when there is no label // http://semantic-ui.com/modules/checkbox.html#fitted - { fitted: !this.props.label }, - 'checkbox' + !label && 'fitted', + checked && 'checked', + 'checkbox', + className ) - const props = getUnhandledProps(Checkbox, this.props) + const rest = getUnhandledProps(Checkbox, this.props) + // Heads Up! + // onChange props are never called as the user cannot click on the hidden input. + // We call onChange in the onClick handler. + // This exists only to prevent React "prop checked without onChange" warnings. return ( -
- - +
+ +
) } diff --git a/src/utils/jquery.js b/src/utils/jquery.js index b4cf0f5736..ddbbf45308 100644 --- a/src/utils/jquery.js +++ b/src/utils/jquery.js @@ -21,8 +21,5 @@ debug('Loading jQuery') // load jQuery, then load SUI jQuery window.jQuery = window.$ = require('jquery') -debug('Loading SUI Checkbox plugin') -require('semantic-ui-css/components/checkbox') - debug('Loading SUI Form plugin') require('semantic-ui-css/components/form') diff --git a/test/specs/modules/Checkbox/Checkbox-test.js b/test/specs/modules/Checkbox/Checkbox-test.js index 1bec7e674d..d2ceda1ed0 100644 --- a/test/specs/modules/Checkbox/Checkbox-test.js +++ b/test/specs/modules/Checkbox/Checkbox-test.js @@ -1,27 +1,134 @@ +import _ from 'lodash' import React from 'react' import Checkbox from 'src/modules/Checkbox/Checkbox' import * as common from 'test/specs/commonTests' +import sandbox from 'test/utils/Sandbox-util' describe('Checkbox', () => { common.isConformant(Checkbox) common.hasUIClassName(Checkbox) - common.rendersChildren(Checkbox) - it('can be checked by default', () => { - shallow() - .find('input') - .should.be.checked() + describe('defaultChecked', () => { + it('sets the initial checked state', () => { + // consoleUtil.disableOnce() + shallow() + .find('input') + .should.be.checked() + }) }) - it('should init the semantic ui plugin', () => { - mount() - .instance() - // the component exposes the jQuery element as 'element' - // 'checkbox' is the jQuery plugin - .element.checkbox.called.should.equal(true) - }) - it('should have a fitted class if no label is given', () => { + + it('adds the "fitted" class if no label is given', () => { shallow() .should.have.className('fitted') }) + + describe('disabled', () => { + it('cannot be checked', () => { + shallow() + .simulate('click') + .find('input') + .should.not.be.checked() + }) + it('cannot be unchecked', () => { + shallow() + .simulate('click') + .find('input') + .should.be.checked() + }) + }) + + describe('inputType', () => { + it('overrides type', () => { + // for every type, override with each inputType + _.each(Checkbox._meta.props.type, (type) => { + _.each(Checkbox._meta.props.inputType, (inputType) => { + shallow() + .find('input') + .should.have.prop('type', inputType) + }) + }) + }) + }) + + describe('type', () => { + it('renders an input of type checkbox when not set', () => { + shallow() + .find('input') + .should.have.prop('type', 'checkbox') + }) + it('renders an input of type checkbox when set to slider', () => { + shallow() + .find('input') + .should.have.prop('type', 'checkbox') + }) + it('renders an input of type checkbox when set to toggle', () => { + shallow() + .find('input') + .should.have.prop('type', 'checkbox') + }) + it('renders an input of type radio when set to radio', () => { + shallow() + .find('input') + .should.have.prop('type', 'radio') + }) + it('it adds the prop value to className, except checkbox', () => { + const types = _.without(Checkbox._meta.props.type, 'checkbox') + _.each(types, (type) => { + shallow() + .should.have.className(type) + }) + }) + it('does not add duplicate checkbox classes when set to "checkbox"', () => { + shallow() + .prop('className') + .match(/checkbox/g) + .should.have.length(1) + }) + }) + + describe('onClick', () => { + it('is called with (event { name, value, checked }) on label click', () => { + const spy = sandbox.spy() + const expectProps = { name: 'foo', value: 'bar', checked: false } + mount() + .find('label') + .simulate('click') + + spy.should.have.been.calledOnce() + spy.should.have.been.calledWithMatch({}, {}) + spy.firstCall.args[1] + .should.deep.equal(expectProps) + }) + }) + + describe('onChange', () => { + it('is called with (event { name, value, !checked }) on click', () => { + const spy = sandbox.spy() + const expectProps = { name: 'foo', value: 'bar', checked: false } + mount() + .find('label') + .simulate('click') + + spy.should.have.been.calledOnce() + spy.should.have.been.calledWithMatch({}, {}) + spy.firstCall.args[1] + .should.deep.equal({ ...expectProps, checked: true }) + }) + }) + + describe('readOnly', () => { + it('cannot be checked', () => { + shallow() + .simulate('click') + .find('input') + .should.not.be.checked() + }) + it('cannot be unchecked', () => { + shallow() + .simulate('click') + .find('input') + .should.be.checked() + }) + }) }) From b9d6650611cca1bccc630aadb324819b6da6861b Mon Sep 17 00:00:00 2001 From: Levi Thomason Date: Sat, 16 Jul 2016 08:56:40 -0700 Subject: [PATCH 8/8] feat(Radio): add Radio component --- .../Examples/addons/Radio/RadioExamples.js | 14 +++++++ .../Examples/addons/Radio/States/Checked.js | 8 ++++ .../Examples/addons/Radio/States/Disabled.js | 15 +++++++ .../Radio/States/RadioRemoteControlExample.js | 22 ++++++++++ .../Examples/addons/Radio/States/ReadOnly.js | 8 ++++ .../Examples/addons/Radio/States/States.js | 41 +++++++++++++++++++ docs/app/Examples/addons/Radio/Types/Radio.js | 8 ++++ .../Examples/addons/Radio/Types/RadioGroup.js | 35 ++++++++++++++++ .../app/Examples/addons/Radio/Types/Slider.js | 8 ++++ .../app/Examples/addons/Radio/Types/Toggle.js | 8 ++++ docs/app/Examples/addons/Radio/Types/Types.js | 30 ++++++++++++++ .../addons/Radio/Variations/Fitted.js | 18 ++++++++ .../addons/Radio/Variations/Variations.js | 24 +++++++++++ src/addons/Radio/Radio.js | 22 ++++++++++ src/index.js | 3 +- test/specs/addons/Radio/Radio-test.js | 15 +++++++ 16 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 docs/app/Examples/addons/Radio/RadioExamples.js create mode 100644 docs/app/Examples/addons/Radio/States/Checked.js create mode 100644 docs/app/Examples/addons/Radio/States/Disabled.js create mode 100644 docs/app/Examples/addons/Radio/States/RadioRemoteControlExample.js create mode 100644 docs/app/Examples/addons/Radio/States/ReadOnly.js create mode 100644 docs/app/Examples/addons/Radio/States/States.js create mode 100644 docs/app/Examples/addons/Radio/Types/Radio.js create mode 100644 docs/app/Examples/addons/Radio/Types/RadioGroup.js create mode 100644 docs/app/Examples/addons/Radio/Types/Slider.js create mode 100644 docs/app/Examples/addons/Radio/Types/Toggle.js create mode 100644 docs/app/Examples/addons/Radio/Types/Types.js create mode 100644 docs/app/Examples/addons/Radio/Variations/Fitted.js create mode 100644 docs/app/Examples/addons/Radio/Variations/Variations.js create mode 100644 src/addons/Radio/Radio.js create mode 100644 test/specs/addons/Radio/Radio-test.js diff --git a/docs/app/Examples/addons/Radio/RadioExamples.js b/docs/app/Examples/addons/Radio/RadioExamples.js new file mode 100644 index 0000000000..09be6d5e1a --- /dev/null +++ b/docs/app/Examples/addons/Radio/RadioExamples.js @@ -0,0 +1,14 @@ +import React from 'react' +import Types from './Types/Types' +import States from './States/States' +import Variations from './Variations/Variations' + +const RadioExamples = () => ( +
+ + + +
+) + +export default RadioExamples diff --git a/docs/app/Examples/addons/Radio/States/Checked.js b/docs/app/Examples/addons/Radio/States/Checked.js new file mode 100644 index 0000000000..c1b12d9dae --- /dev/null +++ b/docs/app/Examples/addons/Radio/States/Checked.js @@ -0,0 +1,8 @@ +import React from 'react' +import { Radio } from 'stardust' + +const RadioCheckedExample = () => ( + +) + +export default RadioCheckedExample diff --git a/docs/app/Examples/addons/Radio/States/Disabled.js b/docs/app/Examples/addons/Radio/States/Disabled.js new file mode 100644 index 0000000000..4d41d34077 --- /dev/null +++ b/docs/app/Examples/addons/Radio/States/Disabled.js @@ -0,0 +1,15 @@ +import React from 'react' +import { Form, Radio, Field } from 'stardust' + +const RadioDisabledExample = () => ( +
+ + + + + + +
+) + +export default RadioDisabledExample diff --git a/docs/app/Examples/addons/Radio/States/RadioRemoteControlExample.js b/docs/app/Examples/addons/Radio/States/RadioRemoteControlExample.js new file mode 100644 index 0000000000..63e75fc040 --- /dev/null +++ b/docs/app/Examples/addons/Radio/States/RadioRemoteControlExample.js @@ -0,0 +1,22 @@ +import React, { Component } from 'react' +import { Button, Radio } from 'stardust' + +export default class RadioRemoteControlExample extends Component { + state = { checked: false } + toggle = () => this.setState({ checked: !this.state.checked }) + + render() { + return ( +
+ + +
+ ) + } +} diff --git a/docs/app/Examples/addons/Radio/States/ReadOnly.js b/docs/app/Examples/addons/Radio/States/ReadOnly.js new file mode 100644 index 0000000000..0cec35db66 --- /dev/null +++ b/docs/app/Examples/addons/Radio/States/ReadOnly.js @@ -0,0 +1,8 @@ +import React from 'react' +import { Radio } from 'stardust' + +const RadioReadOnlyExample = () => ( + +) + +export default RadioReadOnlyExample diff --git a/docs/app/Examples/addons/Radio/States/States.js b/docs/app/Examples/addons/Radio/States/States.js new file mode 100644 index 0000000000..7427abd4d1 --- /dev/null +++ b/docs/app/Examples/addons/Radio/States/States.js @@ -0,0 +1,41 @@ +import React, { Component } from 'react' +import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample' +import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection' +import { Message } from 'stardust' + +export default class RadioStatesExamples extends Component { + render() { + return ( + + + + Use{' '} + + defaultChecked + + {' '}as you normally would to set default form values. + + + + + + + ) + } +} diff --git a/docs/app/Examples/addons/Radio/Types/Radio.js b/docs/app/Examples/addons/Radio/Types/Radio.js new file mode 100644 index 0000000000..059cd088a2 --- /dev/null +++ b/docs/app/Examples/addons/Radio/Types/Radio.js @@ -0,0 +1,8 @@ +import React from 'react' +import { Radio } from 'stardust' + +const RadioRadioExample = () => ( + +) + +export default RadioRadioExample diff --git a/docs/app/Examples/addons/Radio/Types/RadioGroup.js b/docs/app/Examples/addons/Radio/Types/RadioGroup.js new file mode 100644 index 0000000000..a7e0d4b51c --- /dev/null +++ b/docs/app/Examples/addons/Radio/Types/RadioGroup.js @@ -0,0 +1,35 @@ +import React, { Component } from 'react' +import { Form, Radio } from 'stardust' + +export default class RadioGroupExample extends Component { + state = {} + handleClick = (e, { value }) => this.setState({ value }) + + render() { + return ( +
+ + Selected value: {this.state.value} + + + + + + + +
+ ) + } +} diff --git a/docs/app/Examples/addons/Radio/Types/Slider.js b/docs/app/Examples/addons/Radio/Types/Slider.js new file mode 100644 index 0000000000..fd968cdaa8 --- /dev/null +++ b/docs/app/Examples/addons/Radio/Types/Slider.js @@ -0,0 +1,8 @@ +import React from 'react' +import { Radio } from 'stardust' + +const RadioSliderExample = () => ( + +) + +export default RadioSliderExample diff --git a/docs/app/Examples/addons/Radio/Types/Toggle.js b/docs/app/Examples/addons/Radio/Types/Toggle.js new file mode 100644 index 0000000000..00bee521fd --- /dev/null +++ b/docs/app/Examples/addons/Radio/Types/Toggle.js @@ -0,0 +1,8 @@ +import React from 'react' +import { Radio } from 'stardust' + +const RadioToggleExample = () => ( + +) + +export default RadioToggleExample diff --git a/docs/app/Examples/addons/Radio/Types/Types.js b/docs/app/Examples/addons/Radio/Types/Types.js new file mode 100644 index 0000000000..9035af1a16 --- /dev/null +++ b/docs/app/Examples/addons/Radio/Types/Types.js @@ -0,0 +1,30 @@ +import React from 'react' +import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample' +import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection' + +const RadioTypesExamples = () => ( + + + + + + +) + +export default RadioTypesExamples diff --git a/docs/app/Examples/addons/Radio/Variations/Fitted.js b/docs/app/Examples/addons/Radio/Variations/Fitted.js new file mode 100644 index 0000000000..e377059040 --- /dev/null +++ b/docs/app/Examples/addons/Radio/Variations/Fitted.js @@ -0,0 +1,18 @@ +import React from 'react' +import { Radio, Segment } from 'stardust' + +const RadioFittedExample = () => ( +
+ + + + + + + + + +
+) + +export default RadioFittedExample diff --git a/docs/app/Examples/addons/Radio/Variations/Variations.js b/docs/app/Examples/addons/Radio/Variations/Variations.js new file mode 100644 index 0000000000..ab9f23aa82 --- /dev/null +++ b/docs/app/Examples/addons/Radio/Variations/Variations.js @@ -0,0 +1,24 @@ +import React from 'react' +import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample' +import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection' +import { Message } from 'stardust' + +const RadioVariationsExamples = () => ( + + + + The{' '} + + fitted + + {' '}class is automatically applied if there is no label prop. + + + +) + +export default RadioVariationsExamples diff --git a/src/addons/Radio/Radio.js b/src/addons/Radio/Radio.js new file mode 100644 index 0000000000..8dcb24e473 --- /dev/null +++ b/src/addons/Radio/Radio.js @@ -0,0 +1,22 @@ +import React from 'react' +import META from '../../utils/Meta' +import Checkbox from '../../modules/Checkbox/Checkbox' + +/** + * A is sugar for . + * Useful for exclusive groups of type='slider' or type='toggle'. + * @see Checkbox + */ +function Radio(props) { + return ( + + ) +} + +Radio._meta = { + library: META.library.stardust, + name: 'Radio', + type: META.type.addon, +} + +export default Radio diff --git a/src/index.js b/src/index.js index f7670c001a..9d19b24c3c 100644 --- a/src/index.js +++ b/src/index.js @@ -5,8 +5,9 @@ import { deprecateComponent } from './utils/deprecate' // Addons // ---------------------------------------- export Confirm from './addons/Confirm/Confirm' -export Textarea from './addons/Textarea/Textarea' +export Radio from './addons/Radio/Radio' export Select from './addons/Select/Select' +export Textarea from './addons/Textarea/Textarea' // ---------------------------------------- // Collections diff --git a/test/specs/addons/Radio/Radio-test.js b/test/specs/addons/Radio/Radio-test.js new file mode 100644 index 0000000000..e107fe6d04 --- /dev/null +++ b/test/specs/addons/Radio/Radio-test.js @@ -0,0 +1,15 @@ +import React from 'react' + +import Radio from 'src/addons/Radio/Radio' +import Checkbox from 'src/modules/Checkbox/Checkbox' +import * as common from 'test/specs/commonTests' + +describe('Radio', () => { + common.isConformant(Radio) + + it('renders a radio Checkbox', () => { + shallow() + .first() + .should.contain() + }) +})