{"id":7167,"date":"2019-06-11T20:45:30","date_gmt":"2019-06-11T20:45:30","guid":{"rendered":"http:\/\/putridparrot.com\/blog\/?p=7167"},"modified":"2019-06-11T20:45:30","modified_gmt":"2019-06-11T20:45:30","slug":"getting-started-with-storybook","status":"publish","type":"post","link":"https:\/\/putridparrot.com\/blog\/getting-started-with-storybook\/","title":{"rendered":"Getting started with Storybook"},"content":{"rendered":"<p><a href=\"https:\/\/www.learnstorybook.com\/\" rel=\"noopener noreferrer\" target=\"_blank\">Storybook<\/a> allows us to prototype and test UI components in isolation from your application. We&#8217;re going to &#8220;get started&#8221; using Storybook with React, but it also supports Vue and Angular.<\/p>\n<p>Let&#8217;s create the project and add required libraries. <\/p>\n<p>Note, that the <em>npx<\/em> command should not be run from VSCode (or other Atom based editor by the sounds of it), I got an error <a href=\"https:\/\/github.com\/storybookjs\/storybook\/issues\/1897\" rel=\"noopener noreferrer\" target=\"_blank\">EPERM: operation not permitted, unlink<\/a>. <\/p>\n<p>In the steps below we also install <a href=\"https:\/\/airbnb.io\/enzyme\/\" rel=\"noopener noreferrer\" target=\"_blank\">enzyme<\/a> for test rendering. We&#8217;ll also add material-ui, just for the fun of it.<\/p>\n<ul>\n<li>npx -p @storybook\/cli sb init<\/li>\n<li>yarn create react-app storybooksample &#8211;typescript<\/li>\n<li>yarn add @types\/storybook__react -D<\/li>\n<li>yarn add @types\/enzyme -D<\/li>\n<li>yarn add enzyme -D<\/li>\n<li>yarn add enzyme-adapter-react-16 -D<\/li>\n<li>yarn add @types\/enzyme-adapter-react-16 -D<\/li>\n<li>yarn add @material-ui\/core<\/li>\n<\/ul>\n<p>The storybook &#8220;getting started&#8221; web page suggests we now run the following commands to check everything was installed correctly, so let&#8217;s do this, run<\/p>\n<ul>\n<li>yarn test<\/li>\n<li>yarn storybook<\/li>\n<\/ul>\n<p>Let&#8217;s now remove the storybook generated code as we&#8217;ll replace this with our own code. So in the folder <em>src\/stories<\/em>, delete <em>index.js<\/em> and add a file named setupTests.ts to the src folder, here&#8217;s the code for this file<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nimport { configure } from 'enzyme';\r\nimport Adapter from 'enzyme-adapter-react-16';\r\n\r\nconfigure({ adapter: new Adapter() });\r\n<\/pre>\n<p>Before we go too much further let&#8217;s create a sample component that will use to demonstrate testing via storybook. In the <em>src<\/em> folder add a folder named <em>components\/LogButton<\/em> and in this create a file named <em>LogButton.tsx<\/em> &#8211; the name&#8217;s unimportant ofcourse, this is just a sample component which will be a new Button component, but hopefully will help demonstrate how we can use storybook.<\/p>\n<p>In LogButton.tsx place the following code<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nimport React from 'react';\r\nimport Button from '@material-ui\/core\/Button';\r\n\r\ninterface LogButtonProps {\r\n    onClick?: (e: React.MouseEvent&lt;HTMLButtonElement&gt;) =&gt; void;\r\n}\r\n\r\nexport class LogButton extends React.Component&lt;LogButtonProps, {}&gt;{    \r\n   render() {\r\n      return (\r\n         &lt;div&gt;\r\n            &lt;Button variant='contained' onClick={this.props.onClick}&gt;Log&lt;\/Button&gt;\r\n         &lt;\/div&gt;\r\n      );\r\n   }\r\n}\r\n<\/pre>\n<p>Let&#8217;s now create a standard unit test for this component, this is not required for storyboard, but it&#8217;s good practise. In the same <em>src\/components\/LogButton<\/em> folder add the file LogButton.test.tsx and here&#8217;s a very simple test to got into this file<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nimport React from 'react';\r\nimport { mount } from &quot;enzyme&quot;;\r\nimport { LogButton } from &quot;.\/LogButton&quot;;\r\n\r\ntest('Loading component should not throw error', () =&gt; {\r\n    const logButton = mount(&lt;LogButton \/&gt;);\r\n    expect(logButton.exists()).toBe(true);\r\n});\r\n<\/pre>\n<p>This will just check that the component, when loaded, loads successfully. We can now run <em>yarn test<\/em> to verify this code works.<\/p>\n<p>Now let&#8217;s get storybook up and running with our component.<\/p>\n<p>In the .storybook folder, replace the config.js code with the following<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nimport { configure } from '@storybook\/react';\r\n\r\nconst req = require.context('..\/src', true, \/.stories.(ts|tsx|js)$\/)\r\nconst loadStories = () =&gt; req.keys().forEach(filename =&gt; req(filename));\r\nconfigure(loadStories, module)\r\n<\/pre>\n<p>This will automatically load stories based upon the filename\/extensions <em>.stories.<\/em><\/p>\n<p>Alongside LogButton.tsx and LogButton.test.tsx, add LogButton.stories.tsx. This will supply the code required to integrate into storybook and also allow us to write code to allow us to test the UI component via storybook.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nimport React from 'react';\r\nimport { storiesOf } from '@storybook\/react';\r\nimport { LogButton } from '.\/LogButton';\r\nimport { action } from '@storybook\/addon-actions';\r\n\r\nstoriesOf('LogButton', module)\r\n    .add(&quot;Log&quot;, () =&gt; &lt;LogButton onClick={action('clicked')} \/&gt;);\r\n<\/pre>\n<p>Now run <em>yarn storybook<\/em> and the storybook server and UI should display along with our LogButton now being the only component available. Selecting the &#8220;Log&#8221; story, when the LogButton is clicked the action will display the text &#8220;clicked&#8221;.<\/p>\n<p>So what we&#8217;ve done is create a very simply component based upon a Button which we also declared some properties &#8211; well in this case we&#8217;ve created a single property, an onClick event. The story created for the button then hooks into the button&#8217;s click event and when used via storybook allows us to test the control in isolation via storybook&#8217;s UI. This is not a unit test or automated test so much as an interactive UI test where, as developers, we can verify our functionality. Let&#8217;s say the Button&#8217;s text changed when clicked, now we could start interacting with the button via storybook and confirm everything works as expected.<\/p>\n<p>There&#8217;s a lot more to storyboard than the above, but this is a good starting point.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Storybook allows us to prototype and test UI components in isolation from your application. We&#8217;re going to &#8220;get started&#8221; using Storybook with React, but it also supports Vue and Angular. Let&#8217;s create the project and add required libraries. Note, that the npx command should not be run from VSCode (or other Atom based editor by [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[243,251,46],"tags":[],"class_list":["post-7167","post","type-post","status-publish","format-standard","hentry","category-react","category-storybook","category-typescript"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/7167","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/comments?post=7167"}],"version-history":[{"count":5,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/7167\/revisions"}],"predecessor-version":[{"id":7176,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/7167\/revisions\/7176"}],"wp:attachment":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/media?parent=7167"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/categories?post=7167"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/tags?post=7167"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}