diff --git a/addons/knobs/README.md b/addons/knobs/README.md index d5afc4c2c4b..9a4b8e6912c 100644 --- a/addons/knobs/README.md +++ b/addons/knobs/README.md @@ -310,6 +310,21 @@ const groupId = 'GROUP-ID1'; const value = selectV2(label, options, defaultValue, groupId); ``` +### files + +Allows you to get a value from a file input from the user. + +```js +import { files } from '@storybook/addon-knobs/react'; + +const label = 'Images'; +const defaultValue = []; + +const value = files(label, accept, defaultValue); +``` + +> Multiple files can be selected, and will be returned as an array of [Data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) + ### date Allow you to get date (and time) from the user. diff --git a/addons/knobs/example/stories/index.js b/addons/knobs/example/stories/index.js index 3c98a04f8db..bd4ec782a11 100644 --- a/addons/knobs/example/stories/index.js +++ b/addons/knobs/example/stories/index.js @@ -1,7 +1,18 @@ import React from 'react'; import { storiesOf } from '@storybook/react'; import moment from 'moment'; -import { withKnobs, number, object, boolean, text, select, date, array, color } from '../../src'; +import { + withKnobs, + number, + object, + boolean, + text, + select, + date, + array, + color, + files, +} from '../../src'; const stories = storiesOf('Example of Knobs', module); @@ -20,6 +31,10 @@ stories.add('with all knobs', () => { const passions = array('Passions', ['Fishing', 'Skiing']); + const images = files('Happy Picture', 'image/*', [ + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQfiARwMCyEWcOFPAAAAP0lEQVQoz8WQMQoAIAwDL/7/z3GwghSp4KDZyiUpBMCYUgd8rehtH16/l3XewgU2KAzapjXBbNFaPS6lDMlKB6OiDv3iAH1OAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE4LTAxLTI4VDEyOjExOjMzLTA3OjAwlAHQBgAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOC0wMS0yOFQxMjoxMTozMy0wNzowMOVcaLoAAAAASUVORK5CYII=', + ]); + const customStyle = object('Style', { fontFamily: 'Arial', padding: 20, @@ -38,6 +53,9 @@ stories.add('with all knobs', () => {

My favorite number is {favoriteNumber}.

My most comfortable room temperature is {comfortTemp} degrees Fahrenheit.

+

+ When I am happy I look like this: happy +

); }); diff --git a/addons/knobs/src/angular/index.js b/addons/knobs/src/angular/index.js index 3d5666ea205..753ed8ec964 100644 --- a/addons/knobs/src/angular/index.js +++ b/addons/knobs/src/angular/index.js @@ -14,10 +14,11 @@ import { select, selectV2, button, + files, manager, } from '../base'; -export { knob, text, boolean, number, color, object, array, date, select, selectV2, button }; +export { knob, text, boolean, number, color, object, array, date, select, selectV2, button, files }; export const angularHandler = (channel, knobStore) => getStory => context => prepareComponent({ getStory, context, channel, knobStore }); diff --git a/addons/knobs/src/base.js b/addons/knobs/src/base.js index 9c3a7749520..ffe86e593c2 100644 --- a/addons/knobs/src/base.js +++ b/addons/knobs/src/base.js @@ -69,3 +69,7 @@ export function date(name, value = new Date(), groupId) { export function button(name, callback, groupId) { return manager.knob(name, { type: 'button', callback, hideLabel: true, groupId }); } + +export function files(name, accept, value = []) { + return manager.knob(name, { type: 'files', accept, value }); +} diff --git a/addons/knobs/src/components/types/Files.js b/addons/knobs/src/components/types/Files.js new file mode 100644 index 00000000000..bff94041546 --- /dev/null +++ b/addons/knobs/src/components/types/Files.js @@ -0,0 +1,38 @@ +import { FileReader } from 'global'; +import PropTypes from 'prop-types'; +import React from 'react'; + +function fileReaderPromise(file) { + return new Promise(resolve => { + const fileReader = new FileReader(); + fileReader.onload = e => resolve(e.currentTarget.result); + fileReader.readAsDataURL(file); + }); +} + +const FilesType = ({ knob, onChange }) => ( + Promise.all(Array.from(e.target.files).map(fileReaderPromise)).then(onChange)} + accept={knob.accept} + /> +); + +FilesType.defaultProps = { + knob: {}, + onChange: value => value, +}; + +FilesType.propTypes = { + knob: PropTypes.shape({ + name: PropTypes.string, + }), + onChange: PropTypes.func, +}; + +FilesType.serialize = () => undefined; +FilesType.deserialize = () => undefined; + +export default FilesType; diff --git a/addons/knobs/src/components/types/index.js b/addons/knobs/src/components/types/index.js index ebd51deb858..8b99c3e3f90 100644 --- a/addons/knobs/src/components/types/index.js +++ b/addons/knobs/src/components/types/index.js @@ -7,6 +7,7 @@ import SelectType from './Select'; import ArrayType from './Array'; import DateType from './Date'; import ButtonType from './Button'; +import FilesType from './Files'; export default { text: TextType, @@ -18,4 +19,5 @@ export default { array: ArrayType, date: DateType, button: ButtonType, + files: FilesType, }; diff --git a/addons/knobs/src/index.js b/addons/knobs/src/index.js index 51c13caacd4..5c264102cb2 100644 --- a/addons/knobs/src/index.js +++ b/addons/knobs/src/index.js @@ -9,6 +9,7 @@ import { array, boolean, button, + files, color, date, knob, @@ -20,7 +21,7 @@ import { text, } from './base'; -export { knob, text, boolean, number, color, object, array, date, button, select, selectV2 }; +export { knob, text, boolean, number, color, object, array, date, button, select, selectV2, files }; deprecate(() => {}, 'Using @storybook/addon-knobs directly is discouraged, please use @storybook/addon-knobs/{{framework}}'); diff --git a/addons/knobs/src/polymer/index.js b/addons/knobs/src/polymer/index.js index 5bb16a89065..9454c665e0b 100644 --- a/addons/knobs/src/polymer/index.js +++ b/addons/knobs/src/polymer/index.js @@ -2,9 +2,21 @@ import addons from '@storybook/addons'; import window from 'global'; import './WrapStory.html'; -import { knob, text, boolean, number, color, object, array, date, select, manager } from '../base'; - -export { knob, text, boolean, number, color, object, array, date, select }; +import { + knob, + text, + boolean, + number, + color, + object, + array, + date, + select, + files, + manager, +} from '../base'; + +export { knob, text, boolean, number, color, object, array, date, select, files }; export function button(name, callback) { return manager.knob(name, { type: 'button', value: Date.now(), callback, hideLabel: true }); diff --git a/addons/knobs/src/react/index.js b/addons/knobs/src/react/index.js index 71c0b7d792f..b07851b30d7 100644 --- a/addons/knobs/src/react/index.js +++ b/addons/knobs/src/react/index.js @@ -15,10 +15,11 @@ import { select, selectV2, button, + files, manager, } from '../base'; -export { knob, text, boolean, number, color, object, array, date, select, selectV2, button }; +export { knob, text, boolean, number, color, object, array, date, select, selectV2, button, files }; export const reactHandler = (channel, knobStore) => getStory => context => { const initialContent = getStory(context); diff --git a/addons/knobs/src/vue/index.js b/addons/knobs/src/vue/index.js index df66a1036db..cb37c127708 100644 --- a/addons/knobs/src/vue/index.js +++ b/addons/knobs/src/vue/index.js @@ -12,10 +12,11 @@ import { select, selectV2, button, + files, manager, } from '../base'; -export { knob, text, boolean, number, color, object, array, date, select, selectV2, button }; +export { knob, text, boolean, number, color, object, array, date, select, selectV2, button, files }; export const vueHandler = (channel, knobStore) => getStory => context => ({ data() { diff --git a/examples/official-storybook/stories/__snapshots__/addon-knobs.stories.storyshot b/examples/official-storybook/stories/__snapshots__/addon-knobs.stories.storyshot index 1a1cb220910..01efdf0c457 100644 --- a/examples/official-storybook/stories/__snapshots__/addon-knobs.stories.storyshot +++ b/examples/official-storybook/stories/__snapshots__/addon-knobs.stories.storyshot @@ -44,6 +44,13 @@ exports[`Storyshots Addons|Knobs.withKnobs tweaks static values 1`] = `

Nice to meet you!

+

+ When I am happy I look like this: + happy +


PS. My shirt pocket contains: @@ -147,6 +154,13 @@ exports[`Storyshots Addons|Knobs.withKnobsOptions tweaks static values with debo

Nice to meet you!

+

+ When I am happy I look like this: + happy +


PS. My shirt pocket contains: diff --git a/examples/official-storybook/stories/addon-knobs.stories.js b/examples/official-storybook/stories/addon-knobs.stories.js index 5e59026bf46..0233ea31a85 100644 --- a/examples/official-storybook/stories/addon-knobs.stories.js +++ b/examples/official-storybook/stories/addon-knobs.stories.js @@ -14,6 +14,7 @@ import { date, button, object, + files, } from '@storybook/addon-knobs/react'; class AsyncItemLoader extends React.Component { @@ -73,6 +74,9 @@ storiesOf('Addons|Knobs.withKnobs', module) padding: '10px', }); const nice = boolean('Nice', true); + const images = files('Happy Picture', 'image/*', [ + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQfiARwMCyEWcOFPAAAAP0lEQVQoz8WQMQoAIAwDL/7/z3GwghSp4KDZyiUpBMCYUgd8rehtH16/l3XewgU2KAzapjXBbNFaPS6lDMlKB6OiDv3iAH1OAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE4LTAxLTI4VDEyOjExOjMzLTA3OjAwlAHQBgAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOC0wMS0yOFQxMjoxMTozMy0wNzowMOVcaLoAAAAASUVORK5CYII=', + ]); // NOTE: the default value must not change - e.g., do not do date('Label', new Date()) or date('Label') const defaultBirthday = new Date('Jan 20 2017 GMT+0'); @@ -92,6 +96,9 @@ storiesOf('Addons|Knobs.withKnobs', module)

In my backpack, I have:

{salutation}

+

+ When I am happy I look like this: happy +


PS. My shirt pocket contains:

@@ -206,6 +213,9 @@ storiesOf('Addons|Knobs.withKnobsOptions', module) padding: '10px', }); const nice = boolean('Nice', true); + const images = files('Happy Picture', 'image/*', [ + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQfiARwMCyEWcOFPAAAAP0lEQVQoz8WQMQoAIAwDL/7/z3GwghSp4KDZyiUpBMCYUgd8rehtH16/l3XewgU2KAzapjXBbNFaPS6lDMlKB6OiDv3iAH1OAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE4LTAxLTI4VDEyOjExOjMzLTA3OjAwlAHQBgAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOC0wMS0yOFQxMjoxMTozMy0wNzowMOVcaLoAAAAASUVORK5CYII=', + ]); // NOTE: the default value must not change - e.g., do not do date('Label', new Date()) or date('Label') const defaultBirthday = new Date('Jan 20 2017 GMT+0'); @@ -228,6 +238,9 @@ storiesOf('Addons|Knobs.withKnobsOptions', module)

In my backpack, I have:

{salutation}

+

+ When I am happy I look like this: happy +


PS. My shirt pocket contains: