From 28a14d322fc2e813733e354aae42c7203318c74b Mon Sep 17 00:00:00 2001 From: Bryce Hammond Date: Tue, 17 Jul 2018 16:04:56 -0700 Subject: [PATCH] Add radio buttons knob type --- .../src/components/__tests__/RadioButtons.js | 44 +++++++++++++ .../src/components/types/RadioButtons.js | 66 +++++++++++++++++++ addons/knobs/src/components/types/index.js | 2 + addons/knobs/src/deprecated.js | 16 ++++- addons/knobs/src/index.js | 4 ++ .../addon-knobs.stories.storyshot | 2 +- .../stories/addon-knobs.stories.js | 10 ++- .../__snapshots__/storyshots.js.snap | 2 +- 8 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 addons/knobs/src/components/__tests__/RadioButtons.js create mode 100644 addons/knobs/src/components/types/RadioButtons.js diff --git a/addons/knobs/src/components/__tests__/RadioButtons.js b/addons/knobs/src/components/__tests__/RadioButtons.js new file mode 100644 index 00000000000..1b45a5222b7 --- /dev/null +++ b/addons/knobs/src/components/__tests__/RadioButtons.js @@ -0,0 +1,44 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import RadioButtonType from '../types/RadioButtons'; + +describe('RadioButtons', () => { + let knob; + + beforeEach(() => { + knob = { + name: 'Colors', + value: '#00ff00', + options: { + Green: '#00ff00', + Red: '#ff0000', + }, + }; + }); + + describe('displays value', () => { + it('correctly renders labels', () => { + const wrapper = shallow(); + + const greenLabel = wrapper.find('label').first(); + expect(greenLabel.text()).toEqual('Green'); + }); + + it('sets value on the radio buttons', () => { + const wrapper = shallow(); + + const greenInput = wrapper.find('input').first(); + expect(greenInput.prop('value')).toEqual('#00ff00'); + }); + + it('marks the correct checkbox as checked', () => { + const wrapper = shallow(); + + const greenInput = wrapper.find('input').first(); + const redInput = wrapper.find('input').last(); + + expect(greenInput.prop('checked')).toEqual(true); + expect(redInput.prop('checked')).toEqual(false); + }); + }); +}); diff --git a/addons/knobs/src/components/types/RadioButtons.js b/addons/knobs/src/components/types/RadioButtons.js new file mode 100644 index 00000000000..9e79b0b2115 --- /dev/null +++ b/addons/knobs/src/components/types/RadioButtons.js @@ -0,0 +1,66 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; + +const styles = { + label: { + fontSize: 11, + padding: '5px', + }, +}; + +class RadioButtonsType extends Component { + renderRadioButtonList({ options }) { + if (Array.isArray(options)) { + return options.map(val => this.renderRadioButton(val, val)); + } + return Object.keys(options).map(key => this.renderRadioButton(key, options[key])); + } + + renderRadioButton(label, value) { + const opts = { label, value }; + const { onChange } = this.props; + const { name } = this.props.knob; + const id = `${name}-${opts.value}`; + + return ( +
+ onChange(e.target.value)} + checked={value === this.props.knob.value} + /> + +
+ ); + } + + render() { + const { knob, onChange } = this.props; + + return
{this.renderRadioButtonList(knob, onChange)}
; + } +} + +RadioButtonsType.defaultProps = { + knob: {}, + onChange: value => value, +}; + +RadioButtonsType.propTypes = { + knob: PropTypes.shape({ + name: PropTypes.string, + value: PropTypes.string, + options: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), + }), + onChange: PropTypes.func, +}; + +RadioButtonsType.serialize = value => value; +RadioButtonsType.deserialize = value => value; + +export default RadioButtonsType; diff --git a/addons/knobs/src/components/types/index.js b/addons/knobs/src/components/types/index.js index 8b99c3e3f90..087d71bc08a 100644 --- a/addons/knobs/src/components/types/index.js +++ b/addons/knobs/src/components/types/index.js @@ -4,6 +4,7 @@ import ColorType from './Color'; import BooleanType from './Boolean'; import ObjectType from './Object'; import SelectType from './Select'; +import RadioButtonsType from './RadioButtons'; import ArrayType from './Array'; import DateType from './Date'; import ButtonType from './Button'; @@ -16,6 +17,7 @@ export default { boolean: BooleanType, object: ObjectType, select: SelectType, + radioButtons: RadioButtonsType, array: ArrayType, date: DateType, button: ButtonType, diff --git a/addons/knobs/src/deprecated.js b/addons/knobs/src/deprecated.js index 7822c6bf60c..a780c265bc4 100644 --- a/addons/knobs/src/deprecated.js +++ b/addons/knobs/src/deprecated.js @@ -10,13 +10,27 @@ import { array, date, select, + radioButtons, files, button, withKnobs as commonWithKnobs, withKnobsOptions as commonWithKnobsOptions, } from '.'; -export { knob, text, boolean, number, color, object, array, date, select, files, button }; +export { + knob, + text, + boolean, + number, + color, + object, + array, + date, + select, + radioButtons, + files, + button, +}; export const selectV2 = deprecate(select, 'selectV2 has been renamed to select'); diff --git a/addons/knobs/src/index.js b/addons/knobs/src/index.js index 6aec38d440c..beaa327042b 100644 --- a/addons/knobs/src/index.js +++ b/addons/knobs/src/index.js @@ -52,6 +52,10 @@ export function select(name, options, value, groupId) { return manager.knob(name, { type: 'select', selectV2: true, options, value, groupId }); } +export function radioButtons(name, options, value, groupId) { + return manager.knob(name, { type: 'radioButtons', options, value, groupId }); +} + export function array(name, value, separator = ',', groupId) { return manager.knob(name, { type: 'array', value, separator, groupId }); } diff --git a/examples/official-storybook/stories/__snapshots__/addon-knobs.stories.storyshot b/examples/official-storybook/stories/__snapshots__/addon-knobs.stories.storyshot index 96b47dad9fa..c41fa56c5c0 100644 --- a/examples/official-storybook/stories/__snapshots__/addon-knobs.stories.storyshot +++ b/examples/official-storybook/stories/__snapshots__/addon-knobs.stories.storyshot @@ -39,7 +39,7 @@ exports[`Storyshots Addons|Knobs.withKnobs tweaks static values 1`] = ` style="background-color:#dedede;border:2px dashed silver;border-radius:10px;padding:10px" >

- My name is Storyteller, I'm 70 years old, and my favorite fruit is apple. + My name is Storyteller, I'm 70 years old, and my favorite fruit is apple. I also enjoy watermelon.

My birthday is: January 20, 2017 diff --git a/examples/official-storybook/stories/addon-knobs.stories.js b/examples/official-storybook/stories/addon-knobs.stories.js index 76ca5f7566c..0206fe246f4 100644 --- a/examples/official-storybook/stories/addon-knobs.stories.js +++ b/examples/official-storybook/stories/addon-knobs.stories.js @@ -10,6 +10,7 @@ import { boolean, color, select, + radioButtons, array, date, button, @@ -44,6 +45,13 @@ storiesOf('Addons|Knobs.withKnobs', module) Cherry: 'cherry', }; const fruit = select('Fruit', fruits, 'apple'); + const otherFruits = { + Kiwi: 'kiwi', + Guava: 'guava', + Watermelon: 'watermelon', + }; + + const otherFruit = radioButtons('Other Fruit', otherFruits, 'watermelon'); const dollars = number('Dollars', 12.5, { min: 0, max: 100, step: 0.01 }); const years = number('Years in NY', 9); @@ -63,7 +71,7 @@ storiesOf('Addons|Knobs.withKnobs', module) const defaultBirthday = new Date('Jan 20 2017 GMT+0'); const birthday = date('Birthday', defaultBirthday); - const intro = `My name is ${name}, I'm ${age} years old, and my favorite fruit is ${fruit}.`; + const intro = `My name is ${name}, I'm ${age} years old, and my favorite fruit is ${fruit}. I also enjoy ${otherFruit}.`; const style = { backgroundColor, ...otherStyles }; const salutation = nice ? 'Nice to meet you!' : 'Leave me alone!'; const dateOptions = { year: 'numeric', month: 'long', day: 'numeric', timeZone: 'UTC' }; diff --git a/examples/react-native-vanilla/__tests__/__snapshots__/storyshots.js.snap b/examples/react-native-vanilla/__tests__/__snapshots__/storyshots.js.snap index 1c122504b1e..2b6337fe6f7 100644 --- a/examples/react-native-vanilla/__tests__/__snapshots__/storyshots.js.snap +++ b/examples/react-native-vanilla/__tests__/__snapshots__/storyshots.js.snap @@ -114,7 +114,7 @@ exports[`Storyshots Knobs with knobs 1`] = ` allowFontScaling={true} ellipsizeMode="tail" > - My name is Storyteller, I'm 70 years old, and my favorite fruit is apple. + My name is Storyteller, I'm 70 years old, and my favorite fruit is apple. I also enjoy watermelon.