Storybook ã¯ã¤ã³ã¿ã©ã¯ãã£ãã«éçºããã¦ã¼ã¶ã¼ ã¤ã³ã¿ã¼ãã§ã¤ã¹ ã³ã³ãã¼ãã³ããã¢ããªã±ã¼ã·ã§ã³ãå®è¡ããªãã§ãã¹ãã§ãã¾ããStorybook ã¯ç¬èªã® Webpack æ§æã§ã³ã³ãã¼ãã³ã ã©ã¤ãã©ãªã¨ãã¦ã®å½¹ç®ãæããã®ã§ãããã¸ã§ã¯ãä¾åé¢ä¿ãè¦ä»¶ãæ°ã«ããã«åå¥ã«éçºã§ãã¾ãã
æ¬æç¨¿ã§ã¯ããã¼ã ã¡ã¼ãã® Steve Hobbs æ°ã使ãã人æ°ã®ã«ã³ãã³ãã¼ã ããã°ã¬ãã·ã Web ã¢ããªã±ã¼ã·ã§ã³ï¼PWAï¼ï¼GitHub ã§å ¥æå¯è½ï¼ã使ã£ã¦ãStorybook ãæ¢åã® Vue.js ããã¸ã§ã¯ãã«çµ±åããæ¹æ³ãå¦ãã§ããã¾ãããã®ããã»ã¹ã¯æ°ãã Vue ããã¸ã§ã¯ãã«ã使ãã¾ãã
ã«ã³ãã³ãã¼ãã«ããããã¸ã§ã¯ããå®è¡ãã
以ä¸ã®ã³ãã³ããå®è¡ãã¦ãã«ã³ãã³ãã¼ãã«ããããã¸ã§ã¯ããèµ·åãã¦ãã¼ã«ã«ã§å®è¡ãã¾ãã
git clone [email protected]:elkdanger/kanban-board-pwa.git
cd kanban-board-pwa/
npm install
npm run dev
ã¢ããªã±ã¼ã·ã§ã³ãèµ·åãã¦ããã確èªããã«ã¯ããã©ã¦ã¶ã¼ã§ http://localhost:8080
ãéãã¾ãã
Storybook ã使ãããã«ã¢ããªãå®è¡ããå¿ è¦ã¯ããã¾ãããã叿ã§ããã°ãããã忢ãã¦ãã©ã¦ã¶ã¼ ã¿ããéãã¾ãã
æå¾ã«ãkanban-board-pwa
ããã¸ã§ã¯ãããå¸æã® IDE ã¾ãã¯ã³ã¼ã ã¨ãã£ã¿ã§éãã¾ãã
Storybook ã Vue ã§è¨å®ãã
ç¾è¡ã®ä½æ¥ãã£ã¬ã¯ããªã¨ã㦠kanban-board-pwa
ãæ¬¡ã®ã³ãã³ãã§å®è¡ããnpm
ã使ã£ã¦ Storybook ãã¤ã³ã¹ãã¼ã«ãã¾ãã
npm i --save-dev @storybook/vue
Storybook 㯠vue
㨠babel-core
ãã¤ã³ã¹ãã¼ã«ããã¦ããå¿
è¦ãããã¾ããkanban-board-pwa
㯠Vue CLI ã使ã£ã¦ä½æãããã®ã§ãããã2ã¤ã®ä¾åé¢ä¿ã¯ãã§ã«ã¤ã³ã¹ãã¼ã«ããã¦ãã¾ãã
æå¾ã«ãStorybook ãç°¡åã«å§ãã¦å®è¡ã§ãã npm
ã¹ã¯ãªãããä½ãã¾ããpackage.json
ãã¡ã¤ã«ã® scripts
ã»ã¯ã·ã§ã³ã®ä¸ã«ã次ã追å ãã¾ãã
{ // ... "scripts": { // ... "storybook": "start-storybook -p 9001 -c .storybook" } // ... }
-p
ã³ãã³ã弿°ã¯ Storybook ããã¼ã«ã«ã§å®è¡ãããã¼ãããã®å ´å㯠9001
ãæå®ãã¾ãã-c
ã³ãã³ã弿°ã¯ Storybook ã«æ§æè¨å®ã® .storybook
ãã£ã¬ã¯ããªãæ¢ãããã«ä¼ãã¾ããããã¯æ¬¡ã§å®è¡ãã¾ãã
Storybook ã Vue ã§æ§æãã
Storybook ã¯å¤ãã®ç°ãªãæ¹æ³ã§æ§æããã¾ãããã¹ããã©ã¯ãã£ã¹ã¨ãã¦ããã®æ§é 㯠.storybook
ã¨å¼ã°ãããã£ã¬ã¯ããªã«ä¿åãã¾ããã«ã¼ã ãã©ã«ãã¼ã®ä¸ã«ãã®ãã£ã¬ã¯ããªãä½ãã¾ãã
. âââ .babelrc âââ .dockerignore âââ .editorconfig âââ .eslintignore âââ .eslintrc.js âââ .git âââ .gitignore âââ .idea âââ .postcssrc.js âââ .storybook // Storybook config directory âââ Dockerfile âââ LICENSE âââ README.md âââ build âââ config âââ index.html âââ node_modules âââ package-lock.json âââ package.json âââ screenshots âââ src âââ static
.storybook
å
ã«ãã¹ã¦ã®æ§æè¨å®ãä¿çãã config.js
ãã¡ã¤ã«ãä½ãã¾ããããã§ãStorybook 対å¿ã® Vue ã¢ããªã±ã¼ã·ã§ã³ãä½ãéè¦ãªè¦ç´ ãå®ç¾©ãã¾ãã
Vue ãå®ç¾©ãã
src/main.js
ãã¡ã¤ã«ã¨åæ§ã«ãvue
ãã¤ã³ãã¼ãããå¿
è¦ãããã¾ããconfig.js
ãæ¬¡ã®ããã«æ´æ°ãã¾ãã
// .storybook/config.js import Vue from 'vue';
Vue ã³ã³ãã¼ãã³ããå®ç¾©ãã
ä¸åº¦ Vue ããã¸ã§ã¯ãã§è¡ã£ãããã«ãã°ãã¼ãã« ã«ã¹ã¿ã ã³ã³ãã¼ãã³ãã® Vue.component
ã§ã¤ã³ãã¼ããã¦ã°ãã¼ãã«ã«ç»é²ããå¿
è¦ãããã¾ããconfig.js
ã¤ã³ãã¼ããæ´æ°ããTaskLaneItem
ã³ã³ãã¼ãã³ããç»é²ãã¾ãã
// .storybook/config.js import Vue from 'vue'; // ã«ã¹ã¿ã ã³ã³ãã¼ãã³ããã¤ã³ãã¼ããã¾ãã import TaskLaneItem from '../src/components/TaskLaneItem'; // ã«ã¹ã¿ã ã³ã³ãã¼ãã³ããç»é²ãã¾ãã Vue.component('item', TaskLaneItem);
ããã¯ãStorybook 㯠Vue ã¢ããªã±ã¼ã·ã§ã³ã¨ã¯åå¥ã«å®è¡ããã®ã§ãå®è¡ããªããã°ãªãã¾ããããã¼ã«ã«ã«ç»é²ãããã³ã³ãã¼ãã³ãã¯èªåçã«æã¡è¾¼ã¾ããã®ã§ã注æãã ãããããã㯠Vue ã³ã³ãã¼ãã³ã ãªãã¸ã§ã¯ãã® components
ããããã£ã使ã£ã¦ç»é²ãããã³ã³ãã¼ãã³ãã§ããä¾ãã°ï¼
new Vue({ el: '#app', components: { 'component-a': ComponentA, 'component-b': ComponentB } });
ComponentA
ããã³ ComponentB
㯠#app
ã®ä¸ã«ãã¼ã«ã«ã«ç»é²ããã¾ãã
TaskLaneItem
ã¯ã°ãã¼ãã« ã«ã¹ã¿ã ã³ã³ãã¼ãã³ããªã®ã§ Storybook å
ã«ç¬ç«ãã¦ã¤ã³ã¹ã¿ã³ã¹ã使ããããã«ãããã¤ã³ãã¼ãã㦠Vue ã§ç»é²ããªããã°ãªãã¾ããã
ã¹ãã¼ãªã¼ãæ§æãèªã¿è¾¼ã
@storybook/vue
ãã configure
ã¡ã½ãããã¤ã³ãã¼ãã㦠Storybook ãå®è¡ããã¹ãã¼ãªã¼ï¼ã¹ãã¼ãªã¼ã«ã¤ãã¦ã¯ä»¥ä¸ã§å¦ã³ã¾ãï¼ãèªã¿è¾¼ãããã«ãããå®è£
ãã¾ãã
// .storybook/config.js import { configure } from '@storybook/vue'; import Vue from 'vue'; // ã«ã¹ã¿ã ã³ã³ãã¼ãã³ããã¤ã³ãã¼ããã¾ãã import TaskLaneItem from '../src/components/TaskLaneItem'; // ã«ã¹ã¿ã ã³ã³ãã¼ãã³ããç»é²ãã¾ãã Vue.component('item', TaskLaneItem); function loadStories() { // å¿ è¦ãªã ãã®ã¹ãã¼ãªã¼ãè¦æ±ã§ãã¾ãã } configure(loadStories, module);
Storybook ã¯ãã¼ã«ããã¹ãããã®ã¨åãããã«æ©è½ãã¾ããConfig.js
ãã¡ã¤ã«ã configure
ã¡ã½ãããå®è¡ããããã«ãã£ã¦ loadStories
ã¨ãã颿°ã¨ module
ã弿°ã¨ãã¦åãã¾ããloadStories
ã¯ãã®ããã£ã«å®ç¾©ãããã¹ãã¼ãªã¼ãããã¾ãã
ã¹ãã¼ãªã¼ã¯æå®ãããç¶æ
ã§ã³ã³ãã¼ãã³ãã説æãã¾ããã³ã³ãã¼ãã³ãã«ã¯ active
ãinactive
ãloading
ãªã©ã®åç¶æ
ã®ã¹ãã¼ãªã¼ãæ¸ãè¾¼ã¿ã¾ãããã®å¾ãStorybook ã§ã¯ã¤ã³ã¿ã©ã¯ãã£ããªã³ã³ãã¼ãã³ãã©ã¤ãã©ãªã§ãã®æå®ãããç¶æ
ã®ã³ã³ãã¼ãã³ãããã¬ãã¥ã¼ã§ãã¾ããããã¯å¾ã»ã©è¨å®ãã¦ããã¾ãã
ããè¯ãããã¸ã§ã¯ã ããã¸ã¡ã³ãã®ããããã®ã¹ãã¼ãªã¼ã¯ã³ã³ãã¼ãã³ãã®é£ãã«ä¿åããã®ãçæ³çã§ããsrc
å
ã« stories.js
ãã¡ã¤ã«ãçæãã¦ä½¿ç¨ãããã¹ã¦ã®ã¹ãã¼ãªã¼ããã¹ããã¾ãããããã stories.js
ãã¡ã¤ã«ã使ã£ã¦ã次ã®ããã« config.js
ãã¡ã¤ã«ã«ãã®ã¹ãã¼ãªã¼ãç´ æ©ããã¼ããã¾ãã
// .storybook/config.js // ... function loadStories() { // å¿ è¦ãªã ãã®ã¹ãã¼ãªã¼ãè¦æ±ã§ãã¾ãã require('../src/stories'); } configure(loadStories, module);
loadStories
ãå®è¡ããã¨ããStorybook 㯠src/stories.js
ã«ãããã¹ã¦ã®ã¹ãã¼ãªã¼ãã¤ã³ãã¼ããã¦å®è¡ãã¾ãããã®ããã« Storybook ã®æ§æããããã®å®è£
ã«ã¨ãã¨ãéä¸ãããã¨ã§ config.js
ãã¡ã¤ã«ã®ã¡ã³ããã³ã¹ããã£ã¨ç°¡åã«ãªãã¾ããç¾æç¹ã§ã¯ãã¹ã¦ã®ã¢ã¯ã·ã§ã³ã src/stories.js
ãã¡ã¤ã«å
ã§çºçãã¾ãã
ãã¹ã¦ã®ã«ã¹ã¿ã ã³ã³ãã¼ãã³ã㨠Vue ãã©ã°ã¤ã³ã¯ configure() ãå¼ã³åºãåã«ç»é²ãã¾ãã
âãStorybook ã¯ã¤ã³ã¿ã©ã¯ãã£ãã«éçºããã¦ã¼ã¶ã¼ ã¤ã³ã¿ã¼ãã§ã¤ã¹ ã³ã³ãã¼ãã³ããã¢ããªã±ã¼ã·ã§ã³ãå®è¡ããªãã§ãã¹ãã§ãã¾ãããã㨠VueJS ãçµ±åããæ¹æ³ã«ã¤ãã¦å¦ã³ã¾ãããããâ
Tweet This
Vue ã® Storybook ã¹ãã¼ãªã¼ãæ¸ãè¾¼ã
Storybook ã¹ãã¼ãªã¼ãæ¸ãã¦ãã³ã³ãã¼ãã³ãã©ã¤ãã©ãªãç¾å®ã®ãã®ã«ãã¾ããããsrc/stories.js
ã«ç§»åããæ¬¡ã®ããã«å§ãã¾ãã
// src/stories.js import { storiesOf } from '@storybook/vue'; storiesOf('TaskLaneItem', module);
ããã¾ã§ã§ storiesOf
ã¡ã½ãããã¤ã³ãã¼ããã¾ããããã®ã¡ã½ããã¯ã³ã³ãã¼ãã³ãã®ã¹ãã¼ãªã¼ãä½ãã®ã«å½¹ç«ã¡ããã®å ´åãTaskLaneItem
ã³ã³ãã¼ãã³ãã使ã£ã¦ãã®å½¹å²ãæããã¾ããTaskLaneItem
㯠Vue.component
ã§ãã§ã«ã°ãã¼ãã«ã«ç»é²ããã¦ããã®ã§ãããã src/stories.js
ã«ã¤ã³ãã¼ãããå¿
è¦ã¯ããã¾ãããUsing, Storybook ã®å®£è¨åè¨èªã使ã£ã¦ TaskLaneItem
ã®ã¹ãã¼ãªã¼ã次ã®ããã«ä¼ãã¾ãã
storiesOf('TaskLaneItem', module);
ãã®ããã»ã¹ãå®éã®æ¸ç±ã§èããã¨ãããã¯æ¸ç±ã®è£½æ¬ã表ç´ã«ãªãã¾ããã§ã¯ããããã¹ãã¼ãªã¼ããã¼ã¸ã«åãã¾ããããã宣è¨çã« add
ã¡ã½ããã使ã£ã¦è¡ãã¾ãã
// src/stories.js import { storiesOf } from '@storybook/vue'; storiesOf('TaskLaneItem', module).add('Default TaskLaneItem', () => ({ template: '<item :item="{id: 10, text: \'This is a test\'}"></item>' }));
add
ã¯ã¹ãã¼ãªã¼ãããæ¸ç±ã«ç« ã追å ããã®ã¨åããããªå½¹ç®ããã¾ããåç« ã«ã¿ã¤ãã«ãä»ãã¾ãããã®å ´åãDefault TaskLaneItem
ã¨ããã¿ã¤ãã«ã®ã¹ãã¼ãªã¼ãä½ãã¾ããadd
ã¯ãã®ã¹ãã¼ãªã¼ã¿ã¤ãã«ã¨ã段éçãªã³ã³ãã¼ãã³ããã¬ã³ããªã³ã°ãã颿°ã弿°ã¨ãã¦åãã¾ãããã®å ´åãã³ã³ãã¼ãã³ã㯠template
ãªãã·ã§ã³ãæå®ãã Vue ã³ã³ãã¼ãã³ãå®ç¾©ãªãã¸ã§ã¯ãã§ãã
éè¦ãªæ³¨æäºé
ï¼ã³ã³ãã¼ãã³ãã表ãããã³ãã¬ã¼ãã§ä½¿ç¨ãããã¿ã°å㯠Vue.component
ã§ .storybook/config.js
ã«ã³ã³ãã¼ãã³ããç»é²ããã¨ãã«ä½¿ç¨ããååã§ãã
// ã«ã¹ã¿ã ã³ã³ãã¼ãã³ããç»é²ãã¾ãã Vue.component('item', TaskLaneItem);
ãã® Vue ã³ã³ãã¼ãã³ãå®ç¾©ãªãã¸ã§ã¯ã㯠id
㨠text
ãæ§ãã㪠props
ã«ãã¦ã¢ã¸ã¥ã©ã¼ãå¢ããããã«ãªãã¡ã¯ã¿ãªã³ã°ã§ãã¾ãã
// src/stories.js import { storiesOf } from '@storybook/vue'; storiesOf('TaskLaneItem', module).add('Default TaskLaneItem', () => ({ data() { return { item: { id: 10, text: 'This is a test' } }; }, template: '<item :item="item"></item>' }));
ã¹ãã¼ãªã¼ãæ¸ãåºç¤ãã§ãã¾ãããã§ã¯ Storybook ãå®è¡ãã¦ãã®æ©è½ã確èªãã¾ãããã
âãStorybook ã¯ã³ã³ãã¼ãã³ã ã©ã¤ãã©ãªã«ã³ã³ãã¤ã«ãããã¹ãã¼ãªã¼ãéãã¦åå¥ã®ã³ã³ãã¼ãã³ãã®ãã¬ã¼ã³ãã¼ã·ã§ã³ã¨ç¶æ ã説æãã¾ããVueJS ã® Storybook ã¹ãã¼ãªã¼ãæ¸ãæ¹æ³ãå¦ã³ã¾ãããããâ
Tweet This
Storybook ãå®è¡ãã
ã³ã³ãã¥ã¼ã¿ã¼ã§æ¬¡ã®ã³ãã³ããå®è¡ãã¾ãã
npm run storybook
ãã¹ã¦ã®å®è¡ãæåããããã³ã³ã½ã¼ã«ã«æ¬¡ã®ã¡ãã»ã¼ã¸ã表示ããã¾ãã
info Storybook started on => http://localhost:9001/
ãã©ã¦ã¶ã¼ã§ URL http://localhost:9001/
ãéãã¾ãããã¼ããããããèªåã® Storybook ããæ¥½ãã¿ãã ããã
ãã®ç¶æ
ã§ã¯ TaskLaneItem
ã³ã³ãã¼ãã³ãã¯ãã¾ãè¯ãããã¾ãããããã¯ã©ã¤ã ã¢ããªã±ã¼ã·ã§ã³ã§è¦ãã¨ãã¨æ¯è¼ãã¦ããã¹ããèªã¿ã«ããããã§ãã
TaskLaneItem
ã³ã³ãã¼ãã³ãã®å®ç¾©ãä¿çãã src/components/TaskLaneItem.vue
ãã¡ã¤ã«ãéãã¾ããèæ¯è²ãå®ç¾©ããã¦ãã以å¤ã¯ã¹ã¿ã¤ã«æå®ãããã¦ããªããã¨ãåããã¾ãããã®ã³ã³ãã¼ãã³ãã®å®å
¨ãªã¹ã¿ã¤ã«æå®ã¯ Bootstrap ããæ¥ã¾ãããã£ã¦ã次ã®ã¹ããã㯠Storybook ã« Bootstrap ã使ç¨ããããã¨ã§ãã
ã«ã¹ã¿ã ãããã¿ã°ã Storybook ã«å ãã
index.html
ãéãã¨ãkanban-board-pwa
ã¢ããªã¯ <head>
è¦ç´ å
ã§ç°ãªãã¿ã°ã使ã£ã¦ãããã¨ãåããã¾ããStorybook ã§æ£ç¢ºã«ã³ã³ãã¼ãã³ãããã¬ãã¥ã¼ããããã®2ã¤ã®é¢é£ã¿ã°ã¯ Bootstrap 㨠FontAwesome ãããã¸ã§ã¯ãã«ç´¹ä»ãã <link>
ã¿ã°ã§ãã
Storybook ã¯ãã®ã¢ããªã¨ã¯åå¥ã«å®è¡ããã®ã§ãindex.html
å
ã§å®ç¾©ããããããã¿ã°ãè¦ãã使ç¨ããããããã¨ã¯ã§ãã¾ãããã½ãªã¥ã¼ã·ã§ã³ã¨ãã¦ã.storybook
æ§æãã£ã¬ã¯ããªã®ä¸ã« preview-head.html
ãã¡ã¤ã«ã使ãã¦å¿
è¦ãª <link>
ã¿ã°ã次ã®ããã«è¿½å ãã¾ãã
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootswatch/4.0.0-beta.2/superhero/bootstrap.min.css"> <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
ããã忢ã㦠Storybook ãåèµ·åãã¦ãã npm run storybook
ãåã³å®è¡ãã¾ããããã§ Storybook ãæ´æ°ããæ§æã使ç¨ã§ããããã«ãªãã¾ããåã³ãã©ã¦ã¶ã¼ã§ http://localhost:9001/
ãéãã¨ãåãã¹ã¿ã¤ã«è¨å®ã§ãã£ã¨è¯ã TaskLaneItem
ããã«ãã¼ã¸ã§ã³ã®ã¢ããªã§ãã¬ãã¥ã¼ãããã¨ãã§ãã¾ãã
ã³ã³ãã¼ãã³ã夿´ã Storybook ã§ãã¬ãã¥ã¼ãã
ããã¾ã§æ¢åã®å®ç¾©ã¨æ§æã§ã³ã³ãã¼ãã³ããæ®µéçã«å®è¡ãã¦ãã¾ãããStorybook ã®æãå¼·åãªæ©è½ã¯ã¢ããªã±ã¼ã·ã§ã³ãå®è¡ããã«ã夿´ãã©ã¤ãã§è¦ããã¨ãã§ãããã¨ã§ããTaskLaneItem
ãªã¬ã³ã¸è²å
ã®ããã¹ãã®è²ã«ãããã¨ããã¨ãããã®ããã£ã³ã°ãå¢ããããªã¬ã³ã¸è²ã®å¢çç·ã追å ãã¾ãããããã§ããï¼TaskLaneItem.vue
ã«ãã <style>
ã¿ã°å
ã§å¤æ´ãã¾ãã
// src/components/TaskLaneItem.vue <template> // ... Template definition </template> <script> // ... Script definition </script> <style> .card.task-lane-item { background: #627180; border: solid orange 5px; } .card-title { color: orange; } .card-block { padding: 20px; } </style>
ãã®ãã¡ã¤ã«ãä¿åããã¨ãStorybook ãæ´æ°ããã³ã³ãã¼ãã³ãã®ãã¬ãã¥ã¼ãããã«è¦ããã¨ãã§ãã¾ãï¼
åå¥ã®ã³ã³ãã¼ãã³ãã®å¤è¦³ãå°è±¡ãå®é¨ãããã¨ã§æéã®ç¯ç´ã«ããªãã¾ããUI ããºã«å ¨ä½ãçµã¿ç«ã¦ã代ããã«ãã²ã¨ã¤ã®ãã¼ã¿ã ãããã¬ãã¥ã¼ã§ãã¾ãããã ã Storybook ã¯ã³ã³ãã¸ã·ã§ã³ã®ä¸ã§ã³ã³ãã¼ãã³ããå®è¡ã§ãã¾ãã
次ã«é²ãåã«ã夿´ãã TaskLaneItem
ã®ã¹ã¿ã¤ã«ããªãã¼ã¹ããã ãã§ããªãè¯ããªãã¾ãã
Storybook ã§ Vuex ã使ç¨ãã
ããã§ã¯ç°¡åã«æç¤ºããã³ã³ãã¼ãã³ãããã¬ãã¥ã¼ããæ¹æ³ãå¦ã³ã¾ããããã§ã¯ãããã«è¤éãªæ§é ã§ããã¼ã¿ã¨ä¸ç·ã«ãã¤ãã¬ã¼ãããããã« Vuex ã¹ãã¢ã使ã£ã¦ TaskLane
ã³ã³ãã¼ãã³ãã®ã¹ãã¼ãªã¼ãä½ãã¾ããããã¾ã .storybook/config.js
ãæ´æ°ãã¦TaskLane
ãã¤ã³ãã¼ããã¦ç»é²ãã¾ãã
// .storybook/config.js // ... // ã«ã¹ã¿ã ã³ã³ãã¼ãã³ããã¤ã³ãã¼ããã¾ãã import TaskLaneItem from '../src/components/TaskLaneItem'; import TaskLane from '../src/components/TaskLane'; // ... // ã«ã¹ã¿ã ã³ã³ãã¼ãã³ããç»é²ãã¾ãã Vue.component('item', TaskLaneItem); Vue.component('lane', TaskLane); // ...
次ã«ãsrc/stories.js
ã«æ»ã£ã¦ TaskLane
ã®ã¹ãã¼ãªã¼ãä½ãã¾ãã
// src/stories.js // ... TaskLaneItem stories storiesOf('TaskLane', module).add('Default TaskLane', () => ({ data() { return { doneItems: [ { id: 10, text: 'This is a test' }, { id: 12, text: 'This is another test' }, { id: 14, text: 'This is yet another a test' }, { id: 16, text: 'This is one more test' } ] }; }, template: ` <div class="col-md"> <lane id="done" title="Done" :items="doneItems"></lane> </div> ` }));
ããã¯åã« TaskLaneItem
ã§å®è¡ããæ¹æ³ã¨åæ§ã§ã主ãªéã㯠col-md ã¯ã©ã¹ã§ div
å
ã« lane
ãæãè¿ã doneItems
ã®é
åã items
ããããã£ãéã㦠TaskLane
ã«ãã¹ãããã¨ã§ãã
åã«ã
Vue.component
ã§ã³ã³ãã¼ãã³ããç»é²ããããã«ä½¿ã£ãååãªã®ã§ãã³ã³ãã¬ã¼ãå ã®ã³ã³ãã¼ãã³ãã¿ã°åã¨ãã¦lane
ã使ã£ã¦ãã¾ããã
src/components/TaskLane.vue
ãã¡ã¤ã«ã§è¦ãããã« TaskLane
㯠TaskLaneItem
ããã¼ã«ã«ã§ç»é²ããã®ã§ãStorybook ã¯èªåçã« TaskLaneItem
ããã®ã¹ãã¼ãªã¼ã® TaskLane
ã®ã¹ã³ã¼ãã«æã¡è¾¼ã¿ã¾ããDefault TaskLane
ã¹ãã¼ãªã¼ã®ã³ã³ãã¼ãã³ãå®ç¾©ãªãã¸ã§ã¯ãå
ã« components
ããããã£ãä½ãå¿
è¦ã¯ããã¾ããã
ãã®ãã¡ã¤ã«ãä¿åãã¾ãããã®å¤æ´ã¯ãã®ãã¼ã¸ããªãã¬ãã·ã¥ããã¾ã§ Storybook ã«æ£ãã表示ãããªããããããªãã®ã§ããªãã¬ãã·ã¥ãã¦ãã ãããTaskLane
ã¡ãã¥ã¼é
ç®ãã¯ãªãã¯ãã¦ãããæ¡å¤§ããããã Default TaskLane
ãã¯ãªãã¯ãã¾ããTaskLane
ã³ã³ãã¼ãã³ãã®ãã¬ãã¥ã¼ã次ã®ããã«è¡¨ç¤ºããã¾ãã
TaskLane
ã¯ããã°ããã¼ãã«ã¿ã¹ã¯ããªã¹ãããããã« TaskLaneItem
ã使ç¨ãã¾ããããã TaskLaneItem
ã³ã³ãã¼ãã³ãã¯ãã®ã¢ããªã§æ§æããã¦ããããã«ãTaskLane
ã³ã³ãã¼ãã³ãã®éã«ãã©ãã° ã¢ã³ã ãããããã¾ããStorybook TaskLane
ãã¬ãã¥ã¼ãå®å
¨ã«ã¤ã³ã¿ã©ã¯ãã£ãã«ãªã£ããã¨ãåããã¾ããã¬ã¼ã³å
ã«è¡¨ç¤ºããã¦ããé
ç®ã¯ãã©ãã°ãã¦åãããã¨ãã§ãã¾ãã
ãã ããã¬ã¼ã³å ã«ã³ã³ãã¼ãã³ãããã©ãã°ããã¨ããã®ä½ç½®ã¯åºå®ããã¾ãããéçºè ã³ã³ã½ã¼ã«ãéãã¨ã次ã®ãããªã¨ã©ã¼ãè¦ãã¾ãã
vue.esm.js:591 [Vue warn]: Property or method "$store" is not defined on the instance but referenced during render.
ã©ãããã®ã§ããããï¼src/components/TaskLane.vue
ãã¡ã¤ã«ã«ãã TaskLane
ã®å®ç¾©ã«ç§»åãã¾ãããã®ã³ã³ãã¼ãã³ãã¯ãã®ããã¸ã§ã¯ãã§ä½æããã vuex
ã¹ãã¢ã使ç¨ãã¦é
ç®ãæ´æ°ãããã¨ãåããã¾ãã
// src/components/TaskLane.vue // ... Template tag <script> // ... Script imports export default { // ... Other properties
computed: { itemCount() { if (!this.items) return ''; if (this.items.length === 1) return '1 task'; return `${this.items.length} tasks`; }, draggables: { get() { return this.items; }, set(items) { this.$store.commit('updateItems', { items, id: this.id }); } } } }; </script> // ... Style tag
vuex
ã¹ãã¢ã®ã¤ã³ã¹ã¿ã³ã¹ã¯ $store
ãéãã¦åå¨ãã¾ããããã¯ã¬ã¼ã³ãã¿ã¹ã¯ã¬ã¼ã³é
ç®ãå±ãããã®ã«æ´æ°ãã updateItems
夿´ãã³ãããããããã«ä½¿ç¨ããã¾ãããã ããã¹ãã¼ãªã¼å
ã® TaskLane
ã®ã¤ã³ã¹ã¿ã³ã¹ã¯ãã®ã¹ãã¢ã®åå¨ã«é対å¿ã§ããããã«éè¦ãªã®ã¯ãé
ç®ã®é
åãã¬ã³ããªã³ã°ããããã«æåã§ TaskLane
ã«ãã¹ãã¦ãã¾ãããªããã®ãããªåé¡ãçºçããã®ã§ããããã
kanban-board-pwa
ã¢ããªã±ã¼ã·ã§ã³ã®ã¢ã¼ããã¯ãã£ã¯ã¢ããªã±ã¼ã·ã§ã³ã®ç¶æ
ã«å¯¾ããä¿¡é ¼ã§ããå¯ä¸ã®æ
å ±æºã¨ã㦠Vuex ã¹ãã¢ã使ç¨ãã¾ãããã¼ã¿ãã¬ã³ããªã³ã°ããå¿
è¦ãããã³ã³ãã¼ãã³ãã¯ã¹ãã¢ããå
¥æããªããã°ãªãã¾ãããVuex ã¹ãã¢ã¯åæå¹åããã¦ããã®ã§ãã¹ãã¢ã®ã¹ãã©ã¯ãã£ã夿´ãããã¨ãå½±é¿ãåãããã¼ã¿ã«ãµãã¹ã¯ã©ã¤ããããã³ã³ãã¼ãã³ãã¯æ´æ°ããã¾ãã
åé¡ã¯ Storybook ãµã³ãããã¯ã¹å
ã«ãããã¹ãã¢ã¯ãã¼ã¿ã§åæåããã¦ãã¾ãããã¾ããTaskLane
ã¯ãã® items
ãã¼ã¿ããã®è¦ªã³ã³ãã¼ãã³ãã§ãã KanbanBoard
ããåå¾ãã¾ããKanbanBoard
ã³ã³ãã¼ãã³ãå®ç¾©ã¯ src/components/KanbanBoard.vue
ã§ã覧ããã ãã¾ãããã®è¦ªã³ã³ãã¼ãã³ã㯠TaskLane
ã³ã³ãã¼ãã³ãã«ãã¹ããç®å®ãããããããã£ãä½ãã¹ãã¢ãã¯ã¨ãªãã¾ãã
// src/components/KanbanBoard.vue <template> <div class="board"> <div class="row"> <div class="col-md"> <task-lane id="todo" title="Todo" :items="todoItems"></task-lane> </div> <div class="col-md"> <task-lane id="inProgress" title="In progress" :items="inProgressItems"></task-lane> </div> <div class="col-md"> <task-lane id="done" title="Done" :items="doneItems"></task-lane> </div> </div> </div> </template> <script> import { mapState } from 'vuex'; import TaskLane from './TaskLane' export default { name: 'KanbanBoard', components: { 'task-lane': TaskLane }, computed: mapState({ todoItems: s => s.items.todo, inProgressItems: s => s.items.inProgress, doneItems: s => s.items.done }) }; </script>
åå¥ã« Tasklane
ã¤ã³ã¹ã¿ã³ã¹ã使ãããã¨ã Storybook ã使ç¨ããç®çã§ããã¹ãã¢ããã®é
ç®ããããã£ã Tasklane
ã«ãã¹ã§ããããã« KanbanBoard
å
¨ä½ã®ã¤ã³ã¹ã¿ã³ã¹ãä½ããã¨ã¯ã½ãªã¥ã¼ã·ã§ã³ã§ããã¹ãã¢ãã¾ã 空ã ããã¨ããçç±ã§ãããã¾ããããã®åé¡ã解決ããæåã®ã¹ããã㯠Storybook ããã¸ã§ã¯ãã使ãããã¨ãã«ãé
ç®ãã¹ãã¢ã«è¿½å ãããã¨ã§ãã
.storybook/config.js
ã«ç§»åããæ¬¡ã®ããã«ãã¡ã¤ã«ãæ´æ°ãã¾ãã
// ...ãã®ä»ã®ã¤ã³ãã¼ã import store from '../src/store'; // ..ã«ã¹ã¿ã ã³ã³ãã¼ãã³ããã¤ã³ãã¼ããã¾ãã store.commit('addItem', { text: 'This is a test' }); store.commit('addItem', { text: 'This is another test' }); store.commit('addItem', { text: 'This is one more test' }); store.commit('addItem', { text: 'This is one more test' }); // ...ã«ã¹ã¿ã ã³ã³ãã¼ãã³ããç»é²ãã¾ãã // ...
ããã§ Storybook ããã¸ã§ã¯ããæ§ç¯ãããããã¹ãã¢ã¯ãããï¼ã¤ã®é ç®ã§èªã¿è¾¼ã¾ãã¾ãããã ããStorybook ããã¸ã§ã¯ãã®ãã¡ã¤ã«ã«å¤æ´ããã¨ããã®é ç®ã¯ãã©ã¦ã¶ã¼ã®ãã¼ã«ã« ã¹ãã¬ã¼ã¸ã«ä¿åããã¦ããã®ã§ãéè¤ã®é ç®ã表示ããã¾ãããã㯠Storybook ããã¹ãããã¦ãããã©ã¦ã¶ã¼ ã¦ã£ã³ãã¦ãå度èªã¿è¾¼ã¿ãã¦è§£æ±ºãã¾ãã
åé
ç®ã® id
ã¯ãã®ã¹ãã¢ã«ãã£ã¦èªåçã«ä½æããã¾ããæ¬¡ã«ãã¹ãã¢é
ç®ãããããã£ã¨ã㦠TaskLane
ã³ã³ãã¼ãã³ãã«ãã¹ããããã« src/stories.js
å
ã® Default TaskLane
ã¹ãã¼ãªã¼ãæ´æ°ãã¾ãã
// src/stories.js import { mapState } from 'vuex'; import { storiesOf } from '@storybook/vue'; import store from '../src/store'; // ...TaskLaneItem ã¹ãã¼ãªã¼ storiesOf('TaskLane', module).add('Default TaskLane', () => ({ computed: mapState({ items: s => [...s.items.todo, ...s.items.inProgress, ...s.items.done] }), store, template: ` <div class="col-md"> <lane id="todo" title="Todo" :items="items"></lane> </div> ` }));
ã¹ãã¢ããè¨ç®ãããã²ãã¿ã¼é¢æ°ãçæããããã« src/components/KanbanBoard.vue
å
ã«ãããã¸ãã¯ã¨åæ§ã«ãmapState
ã使ãã¾ãã
é
ç®ã追å ããã¨ããkanban-board-pwa
ã¢ããªã®æ©è½ãçè§£ãã¦ãããã¨ãéè¦ã§ããKanbanBoard
ãã³ãã¬ã¼ãã«æ»ãã¨ãåã¬ã¼ã³ã« id
å¤ãä¸ãããã¦ãããã¨ãåããã¾ãã
// src/components/KanbanBoard.vue <template> <div class="board"> <div class="row"> <div class="col-md"> <task-lane id="todo" title="Todo" :items="todoItems"></task-lane> </div> <div class="col-md"> <task-lane id="inProgress" title="In progress" :items="inProgressItems"></task-lane> </div> <div class="col-md"> <task-lane id="done" title="Done" :items="doneItems"></task-lane> </div> </div> </div> </template>
ãã® id
ã¯ã¹ãã¢ã®é
åè¨å·ã¨ï¼æå³çã«ï¼ä¸è´ããã®ã§ ããã¼ã¿ã®ãªã¼ãã¼ãããããã¾ããªãããªã¹ããåçã«æ´æ°ããç°¡æå¯¾å¿ã«ãªãããã®ãã¢ã®ã¹ã³ã¼ãã«ã¯ã´ã£ããã®ã½ãªã¥ã¼ã·ã§ã³ã§ãã
使¥ãä¿åã㦠Storybook ããªãã¬ãã·ã¥ãã¾ããæ¬¡ãã覧ãã ããã
é ç®ãã¬ã¼ã³å ã«ä¸¦ã¹æ¿ããããããæ°ããä½ç½®ãè¦ãã¦ãããã覧ãã ããã
vuex
ãã£ã¬ã¯ããªã使ãå¿
è¦ã¯ããã¾ããããã¹ãã¢å
ã«ä¸ããã¹ãã¢ã使ãããå ´åãvuex
ãã¤ã³ãã¼ãããããæ¬¡ã®ããã«ã¤ã³ã¹ãã¼ã«ãã¾ãã
// .storybook/config.js import Vuex from 'vuex'; // Vue plugins // Vue ãã©ã°ã¤ã³ãã¤ã³ã¹ãã¼ã«ãã¾ãã Vue.use(Vuex);
ããããæ°ããã¹ãã¢ã®ã¤ã³ã¹ã¿ã³ã¹ã次ã®ããã«ã¹ãã¼ãªã¼å ã«ä½ãã¾ãã
import Vuex from 'vuex'; storiesOf('Component', module) .add('Default Component', () => ({ store: new Vuex.Store({...}), template: `...` }));
vuex
ã®ãããªå¿
é Vue ãã©ã°ã¤ã³ã¯ Vue.use ã使ã£ã¦ã¤ã³ã¹ãã¼ã«ããå¿
è¦ãããã¾ãã
è¤åã³ã³ãã¼ãã³ãã Storybook ã«çµã¿ç«ã¦ã
é
ç®ãã¬ã¼ã³å
ã«ãã©ãã° ã¢ã³ã ãããããã¦ããããã©ã®ããã«ä¸¦ã¹æ¿ããããããå¦ã¶ãã¨ã¯ããã£ãã§ãããé
ç®ãã¬ã¼ã³ã®éã«ãã©ãã° ã¢ã³ã ããããã§ãããã¨ã¯ããã«ç´ æ´ããããã¨ã§ããã§ã¯ããã«ãã¼ã¸ã§ã³ã®ã¢ããªã«åå¨ãããTodoãããIn ProgressãããDoneãã®3ã¤ã®ã¬ã¼ã³ã表ãã¹ãã¼ãªã¼ãä½ãã¾ãããããã㯠KanbanBoard
ã³ã³ãã¼ãã³ãã®ã¤ã³ã¹ã¿ã³ã¹ãä½ããªãã§å®äºãã¾ãã src/stories.js
ã«ç§»åããThree TaskLanes
ã¹ãã¼ãªã¼ãä½ãã¾ãã
// src/stories.js // ... ã¤ã³ãã¼ããã¾ã // ...TaskLaneItem ã¹ãã¼ãªã¼ storiesOf('TaskLane', module) .add('Default TaskLane', () => ({ computed: mapState({ items: s => [...s.items.todo, ...s.items.inProgress, ...s.items.done] }), store, template: ` <div class="col-md"> <lane id="todo" title="Todo" :items="items"></lane> </div> ` })) .add('Three TaskLanes', () => ({ computed: mapState({ todoItems: s => s.items.todo, inProgressItems: s => s.items.inProgress, doneItems: s => s.items.done }), store, template: ` <div class="row"> <div class="col-md"> <lane id="todo" title="Todo" :items="todoItems"></lane> </div> <div class="col-md"> <lane id="inProgress" title="In progress" :items="inProgressItems"></lane> </div> <div class="col-md"> <lane id="done" title="Done" :items="doneItems"></lane> </div> </div> ` }));
ããã§ã¯ãã¬ã¼ã³é
ç®ã®åã«ãã´ãªç¨ã«ç®åºãããã²ãã¿ã¼é¢æ°ãçæããããã« mapState
ã使ã KanbanBoard
ã«åå¨ããåãç®åºãããããããã£ã使ãã¾ãã3ã¤ã®ã¬ã¼ã³ã¯ã¹ãã¢ã®é
åã·ã³ãã«ã«ä¸è´ãããTodo
ãããInProgress
ãããDone
ãã§ id
å¤ã¨ãã¦ä½æããã¾ãã ãã®ä½æ¥ãä¿åããStorybook ã«æ»ãã¾ããTaskLane
ã¡ãã¥ã¼ã®ã¿ããã¯ãªãã¯ãããããã Three Tasklanes
ãã¯ãªãã¯ãã¾ããããããã¨ã次ã®ãããªãã®ã表示ããã¾ãã
éè¤é ç®ããã£ãã Storybook ã¦ã£ã³ãã¦ããªãã¬ãã·ã¥ãã¾ãã
ããã§ãé ç®ãã¬ã¼ã³éã«ãã©ãã° ã¢ã³ã ãããããã¾ããåé ç®ã¯ãã®æ°è¦ã¬ã¼ã³ã¨ã¬ã¼ã³ã®æ°è¦ä½ç½®ãè¨æ¶ãã¾ããè¯ãæ©è½ã§ããï¼
ä¸é¨ããä¸é¨ã«ã³ã³ãã¼ãã³ããä½ãç·´ç¿ããã¾ããããå°ããªkæç¤ºå¯è½ãªã³ã³ãã¼ãã³ãããå§ãããã®ã¹ã¿ã¤ã«ãã³ã³ãã³ãã調æ´ãããããããã¬ã¼ã³ãã¼ã·ã§ã³ã®ã³ã³ãã¼ãã³ãæ§æã«ä¾åãã大ããã®ã³ã³ãã¼ãã³ãã«ç§»åãã¾ããStorybook ã§ã¯ UI ããºã«ã®çã«ã¢ã¸ã¥ã©ã¼éä¾ååãã¼ã¿ã®ãããªåã³ã³ãã¼ãã³ããå¦çãããã¨ã«éä¸ã§ãã¾ãã
å¤§è¦æ¨¡ãªãã¼ã ã«ã¨ã£ã¦ã®ããã²ã¨ã¤ã®å©ç¹ã¯ãã³ã³ãã¼ãã³ã ã©ã¤ãã©ãªãä½ããã¨ã§ããã¸ã§ã¯ãå ã ãã§ãªãã广çã«ãã©ã³ãã£ã³ã°ãè¡ãããã«ä¸è²«ããå¤è¦³ã¨å°è±¡ãå¿ è¦ã¨ããçµç¹ã®ããã¸ã§ã¯ãå ¨ä½ã§ã³ã³ãã¼ãã³ããåå©ç¨ã§ãããã¨ã§ãã
ã¾ã¨ã
vuex
ã®ãã㪠Vue ãã©ã°ã¤ã³ã«ã¢ã¯ã»ã¹ããã使ç¨ãããããªãã Storybook ã®ã³ã³ãã¼ãã³ã ã©ã¤ãã©ãªãéãã¦ã¤ã³ã¿ã©ã¯ãã£ããªæ¹æ³ã§åºæ¬çãªãã¬ã¼ã³ãã¼ã·ã§ã³ãè¤éãªã³ã³ãã¼ãã³ãããã¬ãã¥ã¼ããæ¹æ³ãå¦ã³ã¾ãããStorybook ã¯ãã¿ã³ãã¯ãªãã¯ãã¦æ©è½ãèµ·åããã UI ãã¹ãã®ãããªã©ã«ãã³ã³ãã¼ãã³ããéãã¦ããªã¬ã¼ãããã¢ã¯ã·ã§ã³ããã¬ãã¥ã¼ããããã«ã使ããã¨ãã§ãã¾ãããããã¯é«åº¦ãªä½¿ç¨äºä¾ã§ããã¤ãã³ãå¦çããã¹ããæ±ãããã«ãVue ç¨ã« Storybook ãæ¡å¼µãããã¨ã«ã¤ãã¦ã®ããã°æç¨¿ã«ã¤ãã¦ãé¢å¿ã®ããæ¹ã¯ä»¥ä¸ã®ã³ã¡ã³ãæ¬ã§ãç¥ãããã ããã
Auth0 ã§ã¯ãStorybook ãåºç¯å²ã«ããã£ã¦ä½¿ã£ã¦ãã¾ãããStorybook ã§ã³ã³ãã¼ãã³ã ã©ã¤ãã©ãªãä½ããã¨ã«ã¤ãã¦ã覧ã«ãªãããæ¹ãç§ãã¡ãè¦ã¤ããå©ç¹ãç¥ãããæ¹ã¯ãReact ããã³ Storybook ã§ã³ã³ãã¼ãã³ã ã©ã¤ãã©ãªãè¨å®ããã ã«ã¤ãã¦ã®æç¨¿ãã覧ãã ããã
Auth0ï¼çµ¶å¯¾ã« ID ã§å¦¥åããªã
ã¢ããªã±ã¼ã·ã§ã³ã®æ§ç¯ã«ã¤ãã¦ãé¢å¿ãããã¾ããï¼ãã®ããã«ã¯ã¦ã¼ã¶ã¼èªè¨¼ãè¨å®ããå¿ è¦ãããã¾ããä¸ããå®è£ ããã®ã¯è¤éã§æéããããããããã¾ããããAuth0 ã使ãã¨æ°åã§ã§ãã¾ãã詳細ã«ã¤ãã¦ã¯ãhttps://auth0.com ãã覧ããã ããã@auth0 on Twitter ããã©ãã¼ãã¦ãã ããã
About the author
Dan Arias
Software Engineer