From 2503a448c82ef2ff573dadc80f9917cdd38449e9 Mon Sep 17 00:00:00 2001 From: Levi Thomason Date: Thu, 1 Sep 2016 12:54:16 -0700 Subject: [PATCH] refactor(Checkbox): remove inputType --- src/addons/Radio/Radio.js | 38 +++++----- src/modules/Checkbox/Checkbox.js | 69 ++++++++---------- test/specs/addons/Radio/Radio-test.js | 13 +++- test/specs/modules/Checkbox/Checkbox-test.js | 73 ++++++-------------- 4 files changed, 81 insertions(+), 112 deletions(-) diff --git a/src/addons/Radio/Radio.js b/src/addons/Radio/Radio.js index fb04d3cf7c..a370154d0a 100644 --- a/src/addons/Radio/Radio.js +++ b/src/addons/Radio/Radio.js @@ -1,47 +1,45 @@ import React, { PropTypes } from 'react' -import { getElementType, getUnhandledProps, META } from '../../lib' +import { getUnhandledProps, META } from '../../lib' import { Checkbox } from '../../modules' /** - * A Radio is sugar for . + * A Radio is sugar for . * Useful for exclusive groups of sliders or toggles. * @see Checkbox * @see Form */ function Radio(props) { - const { inputType, type } = props + const { slider, toggle, type } = props const rest = getUnhandledProps(Radio, props) - const ElementType = getElementType(Radio, props) - return + // const ElementType = getElementType(Radio, props) + // radio, slider, toggle are exclusive + // use an undefined radio if slider or toggle are present + const radio = !(slider || toggle) || undefined + + return } Radio._meta = { name: 'Radio', type: META.TYPES.ADDON, + props: { + type: Checkbox._meta.props.type, + }, } Radio.propTypes = { - /** An element type to render as (string or function). */ - as: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.func, - ]), + /** Format to emphasize the current selection state */ + slider: Checkbox.propTypes.slider, + + /** Format to show an on or off choice */ + toggle: Checkbox.propTypes.toggle, /** HTML input type, either checkbox or radio. */ - inputType: Checkbox.propTypes.inputType, - - /** - * 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: Checkbox.propTypes.type, + type: PropTypes.oneOf(Radio._meta.props.type), } Radio.defaultProps = { - as: Checkbox, - inputType: 'radio', type: 'radio', } diff --git a/src/modules/Checkbox/Checkbox.js b/src/modules/Checkbox/Checkbox.js index ac33934a9d..73416426fb 100644 --- a/src/modules/Checkbox/Checkbox.js +++ b/src/modules/Checkbox/Checkbox.js @@ -3,6 +3,7 @@ import cx from 'classnames' import { AutoControlledComponent as Component, + customPropTypes, getElementType, getUnhandledProps, META, @@ -12,27 +13,13 @@ import { const debug = makeDebugger('checkbox') -// maps checkbox types to input types -const typeMap = { - checkbox: 'checkbox', - radio: 'radio', - slider: 'checkbox', - toggle: 'checkbox', -} - const _meta = { name: 'Checkbox', type: META.TYPES.MODULE, props: { - inputType: [ - 'checkbox', - 'radio', - ], type: [ 'checkbox', 'radio', - 'slider', - 'toggle', ], }, } @@ -59,6 +46,24 @@ export default class Checkbox extends Component { /** The initial value of checked. */ defaultChecked: PropTypes.bool, + /** Format to emphasize the current selection state */ + slider: customPropTypes.every([ + PropTypes.bool, + customPropTypes.disallow(['radio', 'toggle']), + ]), + + /** Format as a radio element. This means it is an exclusive option.*/ + radio: customPropTypes.every([ + PropTypes.bool, + customPropTypes.disallow(['slider', 'toggle']), + ]), + + /** Format to show an on or off choice */ + toggle: customPropTypes.every([ + PropTypes.bool, + customPropTypes.disallow(['radio', 'slider']), + ]), + /** A checkbox can appear disabled and be unable to change states */ disabled: PropTypes.bool, @@ -69,7 +74,7 @@ export default class Checkbox extends Component { label: PropTypes.string, /** HTML input type, either checkbox or radio. */ - inputType: PropTypes.oneOf(_meta.props.inputType), + type: PropTypes.oneOf(_meta.props.type), /** The HTML input name. */ name: PropTypes.string, @@ -83,13 +88,6 @@ export default class Checkbox extends Component { /** A checkbox can be read-only and unable to change states */ readOnly: PropTypes.bool, - /** - * 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 value. */ value: PropTypes.string, } @@ -106,16 +104,11 @@ export default class Checkbox extends Component { state = {} - get inputType() { - const { inputType, type } = this.props - return inputType || typeMap[type] - } - canToggle = () => { - const { disabled, readOnly } = this.props + const { disabled, radio, readOnly } = this.props const { checked } = this.state - return !(disabled || readOnly || this.inputType === 'radio' && checked) + return !disabled && !readOnly && !(radio && checked) } handleClick = (e) => { @@ -135,30 +128,26 @@ export default class Checkbox extends Component { } render() { - const { className, label, name, type, value } = this.props + const { className, label, name, radio, slider, toggle, type, value } = this.props const { checked } = this.state const classes = cx( 'ui', - // don't add duplicate "checkbox" classes, but add any other type - type !== 'checkbox' && type, + useKeyOnly(checked, 'checked'), // auto apply fitted class to compact white space when there is no label // http://semantic-ui.com/modules/checkbox.html#fitted useKeyOnly(!label, 'fitted'), - useKeyOnly(checked, 'checked'), + useKeyOnly(radio, 'radio'), + useKeyOnly(slider, 'slider'), + useKeyOnly(toggle, 'toggle'), 'checkbox', className ) const rest = getUnhandledProps(Checkbox, this.props) const ElementType = getElementType(Checkbox, this.props) return ( - + { const wrapper = shallow() wrapper.type().should.equal(Checkbox) - wrapper.should.have.prop('type', 'radio') - wrapper.should.have.prop('inputType', 'radio') + wrapper.should.have.prop('radio', true) + }) + + it('is not a radio when slider', () => { + shallow() + .should.not.have.prop('radio') + }) + + it('is not a radio when toggle', () => { + shallow() + .should.not.have.prop('radio') }) }) diff --git a/test/specs/modules/Checkbox/Checkbox-test.js b/test/specs/modules/Checkbox/Checkbox-test.js index 9ee6068624..cc15ffe61a 100644 --- a/test/specs/modules/Checkbox/Checkbox-test.js +++ b/test/specs/modules/Checkbox/Checkbox-test.js @@ -1,4 +1,3 @@ -import _ from 'lodash' import React from 'react' import Checkbox from 'src/modules/Checkbox/Checkbox' @@ -8,6 +7,9 @@ import { sandbox } from 'test/utils' describe('Checkbox', () => { common.isConformant(Checkbox) common.hasUIClassName(Checkbox) + common.propKeyOnlyToClassName(Checkbox, 'checked') + common.propKeyOnlyToClassName(Checkbox, 'slider') + common.propKeyOnlyToClassName(Checkbox, 'toggle') describe('defaultChecked', () => { it('sets the initial checked state', () => { @@ -18,6 +20,23 @@ describe('Checkbox', () => { }) }) + describe('checking', () => { + it('can be checked and unchecked', () => { + const wrapper = shallow() + + wrapper.find('input').should.not.be.checked() + wrapper.simulate('click').find('input').should.be.checked() + wrapper.simulate('click').find('input').should.not.be.checked() + }) + it('can be checked but not unchecked when radio', () => { + const wrapper = shallow() + + wrapper.find('input').should.not.be.checked() + wrapper.simulate('click').find('input').should.be.checked() + wrapper.simulate('click').find('input').should.be.checked() + }) + }) + describe('disabled', () => { it('cannot be checked', () => { shallow() @@ -33,67 +52,21 @@ describe('Checkbox', () => { }) }) - 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() + it('sets the input type ', () => { + 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) - }) - it('can be checked but not unchecked when radio', () => { - const wrapper = shallow() - - wrapper.find('input').should.not.be.checked() - wrapper.simulate('click').find('input').should.be.checked() - wrapper.simulate('click').find('input').should.be.checked() - }) - it('can be checked and unchecked when checkbox', () => { - const wrapper = shallow() - - wrapper.find('input').should.not.be.checked() - wrapper.simulate('click').find('input').should.be.checked() - wrapper.simulate('click').find('input').should.not.be.checked() - }) }) describe('onClick', () => {