1 | # Storybook Addon Knobs
|
2 |
|
3 | Storybook Addon Knobs allow you to edit React props dynamically using the Storybook UI.
|
4 | You can also use Knobs as a dynamic variable inside stories in [Storybook](https://storybook.js.org).
|
5 |
|
6 | [Framework Support](https://github.com/storybooks/storybook/blob/master/ADDONS_SUPPORT.md)
|
7 |
|
8 | This is how Knobs look like:
|
9 |
|
10 | [![Storybook Knobs Demo](docs/storybook-knobs-example.png)](https://storybooks-official.netlify.com/?knob-Dollars=12.5&knob-Name=Storyteller&knob-Years%20in%20NY=9&knob-background=%23ffff00&knob-Age=70&knob-Items%5B0%5D=Laptop&knob-Items%5B1%5D=Book&knob-Items%5B2%5D=Whiskey&knob-Other%20Fruit=lime&knob-Birthday=1484870400000&knob-Nice=true&knob-Styles=%7B%22border%22%3A%223px%20solid%20%23ff00ff%22%2C%22padding%22%3A%2210px%22%7D&knob-Fruit=apple&selectedKind=Addons%7CKnobs.withKnobs&selectedStory=tweaks%20static%20values&full=0&addons=1&stories=1&panelRight=0&addonPanel=storybooks%2Fstorybook-addon-knobs)
|
11 |
|
12 | > Checkout the above [Live Storybook](https://storybooks-official.netlify.com/?knob-Dollars=12.5&knob-Name=Storyteller&knob-Years%20in%20NY=9&knob-background=%23ffff00&knob-Age=70&knob-Items%5B0%5D=Laptop&knob-Items%5B1%5D=Book&knob-Items%5B2%5D=Whiskey&knob-Other%20Fruit=lime&knob-Birthday=1484870400000&knob-Nice=true&knob-Styles=%7B%22border%22%3A%223px%20solid%20%23ff00ff%22%2C%22padding%22%3A%2210px%22%7D&knob-Fruit=apple&selectedKind=Addons%7CKnobs.withKnobs&selectedStory=tweaks%20static%20values&full=0&addons=1&stories=1&panelRight=0&addonPanel=storybooks%2Fstorybook-addon-knobs) or [watch this video](https://www.youtube.com/watch?v=kopW6vzs9dg&feature=youtu.be).
|
13 |
|
14 | ## Getting Started
|
15 |
|
16 | First of all, you need to install knobs into your project as a dev dependency.
|
17 |
|
18 | ```sh
|
19 | yarn add @storybook/addon-knobs --dev
|
20 | ```
|
21 |
|
22 | Then, configure it as an addon by adding it to your `addons.js` file (located in the Storybook config directory).
|
23 |
|
24 | ```js
|
25 | import '@storybook/addon-knobs/register';
|
26 | ```
|
27 |
|
28 | Now, write your stories with knobs.
|
29 |
|
30 | ### With React
|
31 | ```js
|
32 | import { storiesOf } from '@storybook/react';
|
33 | import { withKnobs, text, boolean, number } from '@storybook/addon-knobs';
|
34 |
|
35 | const stories = storiesOf('Storybook Knobs', module);
|
36 |
|
37 | // Add the `withKnobs` decorator to add knobs support to your stories.
|
38 | // You can also configure `withKnobs` as a global decorator.
|
39 | stories.addDecorator(withKnobs);
|
40 |
|
41 | // Knobs for React props
|
42 | stories.add('with a button', () => (
|
43 | <button disabled={boolean('Disabled', false)} >
|
44 | {text('Label', 'Hello Storybook')}
|
45 | </button>
|
46 | ));
|
47 |
|
48 | // Knobs as dynamic variables.
|
49 | stories.add('as dynamic variables', () => {
|
50 | const name = text('Name', 'Arunoda Susiripala');
|
51 | const age = number('Age', 89);
|
52 |
|
53 | const content = `I am ${name} and I'm ${age} years old.`;
|
54 | return (<div>{content}</div>);
|
55 | });
|
56 | ```
|
57 |
|
58 | ### With Vue.js
|
59 | MyButton.story.js:
|
60 | ```js
|
61 | import { storiesOf } from '@storybook/vue';
|
62 | import { withKnobs, text, boolean } from '@storybook/addon-knobs';
|
63 |
|
64 | import MyButton from './MyButton.vue';
|
65 |
|
66 | const stories = storiesOf('Storybook Knobs', module);
|
67 |
|
68 | // Add the `withKnobs` decorator to add knobs support to your stories.
|
69 | // You can also configure `withKnobs` as a global decorator.
|
70 | stories.addDecorator(withKnobs);
|
71 |
|
72 | // Assign `props` to the story's component, calling
|
73 | // knob methods within the `default` property of each prop,
|
74 | // then pass the story's prop data to the component’s prop in
|
75 | // the template with `v-bind:` or by placing the prop within
|
76 | // the component’s slot.
|
77 | stories.add('with a button', () => ({
|
78 | components: { MyButton },
|
79 | props: {
|
80 | isDisabled: {
|
81 | default: boolean('Disabled', false)
|
82 | },
|
83 | text: {
|
84 | default: text('Text', 'Hello Storybook')
|
85 | }
|
86 | },
|
87 | template: `<MyButton :isDisabled="isDisabled">{{ text }}</MyButton>`
|
88 | }));
|
89 | ```
|
90 |
|
91 | MyButton.vue:
|
92 | ```vue
|
93 | <template>
|
94 | <button :disabled="isDisabled">
|
95 | <slot></slot>
|
96 | </button>
|
97 | </template>
|
98 |
|
99 | <script>
|
100 | export default {
|
101 | props: {
|
102 | isDisabled: {
|
103 | type: Boolean,
|
104 | default: false
|
105 | }
|
106 | }
|
107 | }
|
108 | </script>
|
109 | ```
|
110 |
|
111 | ### With Angular
|
112 | ```js
|
113 | import { storiesOf } from '@storybook/angular';
|
114 | import { boolean, number, text, withKnobs } from '@storybook/addon-knobs';
|
115 |
|
116 | import { Button } from '@storybook/angular/demo';
|
117 |
|
118 | const stories = storiesOf('Storybook Knobs', module);
|
119 |
|
120 | // "withKnobs" decorator should be applied before the stories using knobs
|
121 | stories.addDecorator(withKnobs);
|
122 |
|
123 | // Knobs for Angular props
|
124 | stories.add('with a button', () => ({
|
125 | component: Button,
|
126 | props: {
|
127 | text: text('text', 'Hello Storybook'), // The first param of the knob function has to be exactly the same as the component input.
|
128 | },
|
129 | }));
|
130 |
|
131 | ```
|
132 |
|
133 | Categorize your knobs by assigning them a `groupId`. When a `groupId` exists, tabs will appear in the knobs storybook panel to filter between the groups. Knobs without a `groupId` are automatically categorized into the `ALL` group.
|
134 | ```js
|
135 | // Knob assigned a groupId.
|
136 | stories.add('as dynamic variables', () => {
|
137 | const groupId = 'GROUP-ID1'
|
138 | const name = text('Name', 'Arunoda Susiripala', groupId);
|
139 |
|
140 | const content = `My name is ${name}.`;
|
141 | return (<div>{content}</div>);
|
142 | });
|
143 | ```
|
144 |
|
145 | You can see your Knobs in a Storybook panel as shown below.
|
146 |
|
147 | ![](docs/demo.png)
|
148 |
|
149 | ## Available Knobs
|
150 |
|
151 | These are the knobs available for you to use. You can import these Knobs from the `@storybook/addon-knobs` module.
|
152 | Here's how to import the **text** Knob.
|
153 |
|
154 | ```js
|
155 | import { text } from '@storybook/addon-knobs';
|
156 | ```
|
157 |
|
158 | Just like that, you can import any other following Knobs:
|
159 |
|
160 | ### text
|
161 |
|
162 | Allows you to get some text from the user.
|
163 |
|
164 | ```js
|
165 | import { text } from '@storybook/addon-knobs';
|
166 |
|
167 | const label = 'Your Name';
|
168 | const defaultValue = 'Arunoda Susiripala';
|
169 | const groupId = 'GROUP-ID1';
|
170 |
|
171 | const value = text(label, defaultValue, groupId);
|
172 | ```
|
173 |
|
174 | ### boolean
|
175 |
|
176 | Allows you to get a boolean value from the user.
|
177 |
|
178 | ```js
|
179 | import { boolean } from '@storybook/addon-knobs';
|
180 |
|
181 | const label = 'Agree?';
|
182 | const defaultValue = false;
|
183 | const groupId = 'GROUP-ID1';
|
184 |
|
185 | const value = boolean(label, defaultValue, groupId);
|
186 | ```
|
187 | ### number
|
188 |
|
189 | Allows you to get a number from the user.
|
190 |
|
191 | ```js
|
192 | import { number } from '@storybook/addon-knobs';
|
193 |
|
194 | const label = 'Age';
|
195 | const defaultValue = 78;
|
196 | const groupId = 'GROUP-ID1';
|
197 |
|
198 | const value = number(label, defaultValue);
|
199 | ```
|
200 |
|
201 | For use with `groupId`, pass the default `options` as the third argument
|
202 | ```
|
203 | const value = number(label, defaultValue, {}, groupId);
|
204 | ```
|
205 | ### number bound by range
|
206 |
|
207 | Allows you to get a number from the user using a range slider.
|
208 |
|
209 | ```js
|
210 | import { number } from '@storybook/addon-knobs';
|
211 |
|
212 | const label = 'Temperature';
|
213 | const defaultValue = 73;
|
214 | const options = {
|
215 | range: true,
|
216 | min: 60,
|
217 | max: 90,
|
218 | step: 1,
|
219 | };
|
220 | const groupId = 'GROUP-ID1';
|
221 |
|
222 | const value = number(label, defaultValue, options, groupId);
|
223 | ```
|
224 |
|
225 | ### color
|
226 |
|
227 | Allows you to get a colour from the user.
|
228 |
|
229 | ```js
|
230 | import { color } from '@storybook/addon-knobs';
|
231 |
|
232 | const label = 'Color';
|
233 | const defaultValue = '#ff00ff';
|
234 | const groupId = 'GROUP-ID1';
|
235 |
|
236 | const value = color(label, defaultValue, groupId);
|
237 | ```
|
238 |
|
239 | ### object
|
240 |
|
241 | Allows you to get a JSON object or array from the user.
|
242 |
|
243 | ```js
|
244 | import { object } from '@storybook/addon-knobs';
|
245 |
|
246 | const label = 'Styles';
|
247 | const defaultValue = {
|
248 | backgroundColor: 'red'
|
249 | };
|
250 | const groupId = 'GROUP-ID1';
|
251 |
|
252 | const value = object(label, defaultValue, groupId);
|
253 | ```
|
254 |
|
255 | > Make sure to enter valid JSON syntax while editing values inside the knob.
|
256 |
|
257 | ### array
|
258 |
|
259 | Allows you to get an array of strings from the user.
|
260 |
|
261 | ```js
|
262 | import { array } from '@storybook/addon-knobs';
|
263 |
|
264 | const label = 'Styles';
|
265 | const defaultValue = ['Red'];
|
266 | const groupId = 'GROUP-ID1';
|
267 |
|
268 | const value = array(label, defaultValue);
|
269 | ```
|
270 |
|
271 | > While editing values inside the knob, you will need to use a separator.
|
272 | > By default it's a comma, but this can be override by passing a separator variable.
|
273 | >
|
274 | > ```js
|
275 | > import { array } from '@storybook/addon-knobs';
|
276 | >
|
277 | > const label = 'Styles';
|
278 | > const defaultValue = ['Red'];
|
279 | > const separator = ':';
|
280 | > const value = array(label, defaultValue, separator);
|
281 | > ```
|
282 |
|
283 | For use with `groupId`, pass the default `separator` as the third argument
|
284 | ```
|
285 | const value = array(label, defaultValue, ',', groupId);
|
286 | ```
|
287 |
|
288 | ### select
|
289 |
|
290 | Allows you to get a value from a select box from the user.
|
291 |
|
292 | ```js
|
293 | import { select } from '@storybook/addon-knobs';
|
294 |
|
295 | const label = 'Colors';
|
296 | const options = {
|
297 | Red: 'red',
|
298 | Blue: 'blue',
|
299 | Yellow: 'yellow',
|
300 | Rainbow: ['red', 'orange', 'etc'],
|
301 | None: null,
|
302 | };
|
303 | const defaultValue = 'red';
|
304 | const groupId = 'GROUP-ID1';
|
305 |
|
306 | const value = select(label, options, defaultValue, groupId);
|
307 | ```
|
308 |
|
309 | > You can also provide options as an array like this: `['red', 'blue', 'yellow']`
|
310 |
|
311 |
|
312 | ### radio buttons
|
313 |
|
314 | Allows you to get a value from a list of radio buttons from the user.
|
315 |
|
316 | ```js
|
317 | import { radios } from '@storybook/addon-knobs';
|
318 |
|
319 | const label = 'Fruits';
|
320 | const options = {
|
321 | Kiwi: 'kiwi',
|
322 | Guava: 'guava',
|
323 | Watermelon: 'watermelon',
|
324 | };
|
325 | const defaultValue = 'kiwi';
|
326 |
|
327 | const value = radios(label, options, defaultValue);
|
328 | ```
|
329 |
|
330 | ### options
|
331 |
|
332 | Configurable UI for selecting a value from a set of options.
|
333 |
|
334 | ```js
|
335 | import { optionsKnob as options } from '@storybook/addon-knobs';
|
336 |
|
337 | const label = 'Fruits';
|
338 | const valuesObj = {
|
339 | Kiwi: 'kiwi',
|
340 | Guava: 'guava',
|
341 | Watermelon: 'watermelon',
|
342 | };
|
343 | const defaultValue = 'kiwi';
|
344 | const optionsObj = {
|
345 | display: 'inline-radio'
|
346 | };
|
347 |
|
348 | const value = options(label, valuesObj, defaultValue, optionsObj);
|
349 | ```
|
350 | > The display property for `optionsObj` accepts:
|
351 | > - `radio`
|
352 | > - `inline-radio`
|
353 | > - `check`
|
354 | > - `inline-check`
|
355 | > - `select`
|
356 | > - `multi-select`
|
357 |
|
358 | ### files
|
359 |
|
360 | Allows you to get a value from a file input from the user.
|
361 |
|
362 | ```js
|
363 | import { files } from '@storybook/addon-knobs';
|
364 |
|
365 | const label = 'Images';
|
366 | const defaultValue = [];
|
367 |
|
368 | const value = files(label, accept, defaultValue);
|
369 | ```
|
370 |
|
371 | > 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)
|
372 |
|
373 | ### date
|
374 |
|
375 | Allow you to get date (and time) from the user.
|
376 |
|
377 | ```js
|
378 | import { date } from '@storybook/addon-knobs';
|
379 |
|
380 | const label = 'Event Date';
|
381 | const defaultValue = new Date('Jan 20 2017');
|
382 | const groupId = 'GROUP-ID1';
|
383 |
|
384 | const value = date(label, defaultValue, groupId);
|
385 | ```
|
386 |
|
387 | > Note: the default value must not change - e.g., do not do `date('Label', new Date())` or `date('Label')`
|
388 |
|
389 | The `date` knob returns the selected date as stringified Unix timestamp (e.g. `"1510913096516"`).
|
390 | If your component needs the date in a different form you can wrap the `date` function:
|
391 |
|
392 | ```js
|
393 | function myDateKnob(name, defaultValue) {
|
394 | const stringTimestamp = date(name, defaultValue)
|
395 | return new Date(stringTimestamp)
|
396 | }
|
397 | ```
|
398 |
|
399 | ### button
|
400 |
|
401 | Allows you to include a button and associated handler.
|
402 |
|
403 | ```js
|
404 | import { button } from '@storybook/addon-knobs';
|
405 |
|
406 | const label = 'Do Something';
|
407 | const handler = () => doSomething('foobar');
|
408 | const groupId = 'GROUP-ID1';
|
409 |
|
410 | button(label, handler, groupId);
|
411 | ```
|
412 |
|
413 | ### withKnobs options
|
414 |
|
415 | withKnobs also accepts two optional options as story parameters.
|
416 | Usage:
|
417 |
|
418 | ```js
|
419 | import { storiesOf } from '@storybook/react';
|
420 | import { withKnobs } from '@storybook/addon-knobs';
|
421 |
|
422 | const stories = storiesOf('Storybook Knobs', module);
|
423 |
|
424 | stories.addDecorator(withKnobs)
|
425 | stories.add('story name', () => ..., {
|
426 | knobs: {
|
427 | timestamps: true, // Doesn't emit events while user is typing.
|
428 | escapeHTML: true // Escapes strings to be safe for inserting as innerHTML. This option is true by default. It's safe to set it to `false` with frameworks like React which do escaping on their side.
|
429 | // You can still set it to false, but it's strongly unrecommendend in cases when you host your storybook on some route of your main site or web app.
|
430 | }
|
431 | });
|
432 | ```
|
433 |
|
434 | ## Typescript
|
435 |
|
436 | If you are using typescript, make sure you have the type definitions installed for the following libs:
|
437 |
|
438 | - node
|
439 | - react
|
440 |
|
441 | You can install them using:
|
442 | *assuming you are using Typescript >2.0.*
|
443 |
|
444 | ```sh
|
445 | yarn add @types/node @types/react --dev
|
446 | ```
|
447 |
|