Slide 1

Slide 1 text

෗Βͳ͍UIςετͷͨΊͷ Storybook + Storyshots react.kyoto v0.3.0 ฏ໺ণ࢜ @shisama

Slide 2

Slide 2 text

ฏ໺ ণ࢜ / Masashi Hirano @shisama_ @shisama Node.js Core Collaborator ؔ੢NodeֶԂOrganizer

Slide 3

Slide 3 text

એ఻

Slide 4

Slide 4 text

Agenda • ίϯϙʔωϯτҰཡΛ֬ೝ͢Δ(Storybook) • ίϯϙʔωϯτͷڍಈΛ֬ೝ͢Δ(Storybook) • εφοϓγϣοτςετΛߦ͏(Storyshots) • ϝϯςφϯεҡ࣋ՄೳͳUIςετΛ
 Storybook + StoryshotsͰ࣮ݱ͢Δ

Slide 5

Slide 5 text

Ұ౓͸ࢥͬͨ͜ͱ͕ͳ͍Ͱ͔͢…? • ૿͑͗ͨ͢ίϯϙʔωϯτΛҰཡͰݟ͍ͨ • ౉͢propsʹԠͨ͡දࣔΛνΣοΫ͍ͨ͠ • ݸʑͷίϯϙʔωϯτͷදࣔΛνΣοΫ͠ͳ͕ ΒϨϏϡʔ͕͍ͨ͠

Slide 6

Slide 6 text

https://storybook.js.org/

Slide 7

Slide 7 text

Storybook • ίϯϙʔωϯτΛҰཡදࣔ͢Δπʔϧ • ݸʑͷίϯϙʔωϯτͷද͕ࣔݟΕΔ • ౉͢propsʹԠͨ͡ද͕ࣔݟΕΔ • React, Vue, AngularʹରԠ

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

ίϯϙʔωϯτ͕Ұཡ දࣔ͞ΕΔ ֤ίϯϙʔωϯτͷςετέʔε ίϯϙʔωϯτͷදࣔ݁Ռ

Slide 10

Slide 10 text

import React from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faHeart } from '@fortawesome/free-regular-svg-icons'; type Props = { count: number; onClick: React.MouseEventHandler; }; export const Like = ({ count, onClick }: Props) => { return ( <>
{count}
> ); }; e.g. Likeίϯϙʔωϯτ

Slide 11

Slide 11 text

Storybookಋೖ • @storybook/cliΛ࢖͏ • BabelͷઃఆΛߦ͏ • Storybook༻ͷwebpack.config.jsΛ࡞੒ • StorybookͷઃఆϑΝΠϧΛฤू

Slide 12

Slide 12 text

@storybook/cli • npx -p @storybook/cli sb init --type react • StorybookͷઃఆΛࣗಈͰߦͬͯ͘ΕΔCLI • npm-scripts௥Ճ • StorybookͷઃఆϑΝΠϧ࡞੒ • Storybookͷαϯϓϧίʔυ࡞੒

Slide 13

Slide 13 text

@storybook/cli TUPSZCPPLͷઃఆϑΝΠϧ TUPSZCPPLͷίʔυ JOEFYTUPSJFTKTαϯϓϧ

Slide 14

Slide 14 text

Babelͷઃఆ • Storybook͸BabelΛ࢖͍ͬͯΔ • TypeScript΍React༻ʹpresetͳͲ͕ඞཁ • @babel/core • @babel/preset-typescript • @babel/preset-react

Slide 15

Slide 15 text

module.exports = ({ config }) => { config.module.rules.push({ test: /\.tsx?$/, use: [ { loader: require.resolve('ts-loader') }, { loader: require.resolve('react-docgen-typescript-loader') } ] }); config.resolve.extensions.push('.ts', '.tsx'); return config; }; .storybook/webpack.config.js OQNJ%UTMPBEFS OQNJ%SFBDU EPDHFOUZQFTDSJQU

Slide 16

Slide 16 text

import { configure } from '@storybook/react'; // ͜ͷrequire.contextͰಡΈࠐΉର৅ͷ֦ுࢠΛ.js͔Β.tsxʹฤू // const req = require.context('../stories', true, /\.stories\.js?$/); const req = require.context('../stories', true, /\.stories\.tsx?$/); function loadStories() { req.keys().forEach(filename => req(filename)); } configure(loadStories, module); .storybook/config.js ॳظ஋Ͱ͸KTΛࢦఆ͍ͯ͠Δ ͷͰUTY ͳͲʹมߋ

Slide 17

Slide 17 text

import React from "react"; import { storiesOf } from "@storybook/react"; import { Like } from "../src/components/Like"; storiesOf("Like", module).add("0", () => ( {}} /> )).add("1", () => ( {}} /> )); Storybook༻ͷίʔυΛॻ͘ FHDPVOU͕AAͱAAͷ৔߹ ͷTUPSJFTΛ௥Ճ͢Δ

Slide 18

Slide 18 text

࣮ߦ (npm run storybook)

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

Storybook՝୊఺ • ίϯϙʔωϯτΛमਖ਼ͨ͠ͱ͖ʹStorybookͷ ίʔυ͸ޙճ͠͞Εϝϯς͞Εͳ͘ͳ͍ͬͯ͘ • ࣗಈςετ͢Δ΋ͷͰ͸ͳ͍ͨΊ໨ࢹͷνΣο Ϋ͕ඞཁ

Slide 22

Slide 22 text

https://github.com/storybookjs/storybook/tree/master/addons/storyshots/storyshots-core

Slide 23

Slide 23 text

Storyshots • StorybookͷίʔυΛ࢖͍ࣗಈςετ(εφοϓ γϣοτςετ)Λߦ͏ • ίϯϙʔωϯτΛमਖ਼࣌ʹStorybookͷίʔυ ΋मਖ਼͠ͳ͍ͱεφοϓγϣοτςετ͕ࣦഊ ͢Δ

Slide 24

Slide 24 text

Storyshotsಋೖ • StoryshotsΠϯετʔϧ • JestΠϯετʔϧ • Jestͷઃఆ • Babelͷઃఆ

Slide 25

Slide 25 text

StoryshotsΠϯετʔϧ • StoryshotsΛΠϯετʔϧ • @storybook/addon-storyshots • Storyshotsͷ࣮ߦʹඞཁͳύοέʔδΛΠϯε τʔϧ • babel-plugin-require-context-hook • react-test-renderer

Slide 26

Slide 26 text

JestΠϯετʔϧ • jestͱbabel-jestΛΠϯετʔϧ • babel-jestͰ͸ͳ͘ts-jestΛ࢖͏ͱ࣮ߦ࣌ʹܕ νΣοΫ΋ͯ͘͠ΕΔ • babel-jestͷํ͕ಋೖ͸؆୯ • ܕνΣοΫ͸tscͰߦ͏ • λεΫ͸ࡉ෼Խͯ͠ฒྻॲཧͰ͖ΔΑ͏ʹ

Slide 27

Slide 27 text

Jestͷઃఆ • Jest࣮ߦʹඞཁͳsetupFilesΛ࡞੒ • εφοϓγϣοτςετͷίʔυΛ࡞੒ • jest.config.jsΛ࡞੒

Slide 28

Slide 28 text

Jestͷઃఆ - setupFiles࡞੒ // .jest/register-context.ts import registerRequireContextHook from “babel-plugin- require-context-hook/register"; registerRequireContextHook();

Slide 29

Slide 29 text

Jestͷઃఆ - ςετϑΝΠϧ࡞੒ // stories/__tests__/Like.test.ts import initStoryshots from "@storybook/addon-storyshots"; initStoryshots();

Slide 30

Slide 30 text

Jestͷઃఆ - jest.config.js࡞੒ module.exports = { setupFiles: ["/.jest/register-context.ts"], testMatch: ["/stories/__tests__/*.test.ts"] }; ϑΝΠϧͷ৔ॴ͸೚ҙ

Slide 31

Slide 31 text

Babelͷઃఆ { "presets": [ [ "@babel/preset-env", { "targets": { "node": "current" } } ], "@babel/preset-typescript", "@babel/preset-react" ], "env": { "test": { "plugins": ["require-context-hook"] } } } KFTUDPOpHKTͷઃఆϑΝΠ ϧͷͨΊʹQSFTFUFOWΛΠ ϯετʔϧ UFTUͷͱ͖͚ͩSFRVJSF DPOUFYUIPPL͕ݺ͹ΕΔΑ ͏ʹ͓ͯ͘͠

Slide 32

Slide 32 text

࣮ߦ (npx jest) 4OBQTIPUϑΝΠϧ͕࡞੒͞ Εͨ

Slide 33

Slide 33 text

࣮ߦ݁Ռ (εφοϓγϣοτϑΝΠϧ) 4OBQTIPUϑΝΠϧ͕࡞੒͞ Εͨ

Slide 34

Slide 34 text

࣮ߦ݁Ռ (εφοϓγϣοτϑΝΠϧ)

Slide 35

Slide 35 text

ίϯϙʔωϯτΛमਖ਼ͨ͠ͱ͖ ྫ͑͹ɺQSPQTͷ໊લ͕ม Θͬͨͱ͖

Slide 36

Slide 36 text

࣮ߦ (npx jest) લճςετ͔࣌Β݁Ռ͕ม ΘͬͨͨΊςετࣦഊ

Slide 37

Slide 37 text

Storybook՝୊఺͕ղܾ • ίϯϙʔωϯτΛमਖ਼ͨ͠ͱ͖ʹStorybookͷίʔυ͸ ޙճ͠͞Εϝϯς͞Εͳ͘ͳ͍ͬͯ͘
 ⇛Storybookͷίʔυ΋ม͑ͳ͍ͱςετ͕௨Βͳ͍ͷ ͰϝϯςඞਢͱͳΔ
 • ࣗಈςετ͢Δ΋ͷͰ͸ͳ͍ͨΊ໨ࢹͷνΣοΫ͕ඞཁ
 ⇛テスト自動化

Slide 38

Slide 38 text

·ͱΊ • StorybookͰίϯϙʔωϯτ͝ͱͷUIΛνΣο ΫͰ͖Δ • StoryshotsͰςετࣗಈԽ͠ɺStorybookΛ෗Β ͞ͳ͍Α͏ʹ͢Δ

Slide 39

Slide 39 text

https://github.com/shisama/react-storybook-storyshots-example ࠓճͷσϞ

Slide 40

Slide 40 text

Thanks. @shisama_ shisama