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