diff --git a/addons/knobs/README.md b/addons/knobs/README.md
index d5afc4c2c4b3..9a4b8e6912c9 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 3c98a04f8db3..bd4ec782a111 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:
+
);
});
diff --git a/addons/knobs/src/angular/index.js b/addons/knobs/src/angular/index.js
index 3d5666ea205a..753ed8ec964a 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 9c3a7749520d..ffe86e593c2c 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 000000000000..bff94041546e
--- /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 ebd51deb858e..8b99c3e3f908 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 51c13caacd49..5c264102cb25 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 5bb16a890651..9454c665e0b8 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 71c0b7d792f1..b07851b30d79 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 df66a1036db3..cb37c127708b 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 1a1cb2209103..01efdf0c4570 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:
+
+
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:
+
+
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 5e59026bf466..0233ea31a855 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:
{items.map(item => - {item}
)}
{salutation}
+
+ When I am happy I look like this:
+
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:
{items.map(item => - {item}
)}
{salutation}
+
+ When I am happy I look like this:
+
PS. My shirt pocket contains: