Heim

Brian

24. október 2018

Einangrað þróun­ar­um­hverfi fyrir React

React hefur farið um vefheima eins og eldur í sinu, ný og ný verkefni poppa upp á hverjum degi og fleiri og fleiri eru að bæta við React í verkfærasettið sitt. Samfélagið hefur stækkað og hægt er að finna npm pakka fyrir flest þekkt viðmótsvandamál. Einnig er hægt að nota React til að skrifa viðmót fyrir allt frá símaforriti yfir í skipunarlínutól.

Þar sem React er einingasinnað „framework“ býður það upp á marga möguleika þegar kemur að tólum til að einfalda okkur skipulag og vinnu.

Það sem mig langar að sýna ykkur er hvernig hægt er að nota React og nokkur tól frá samfélaginu til að setja upp einangrað þróunarumhverfi fyrir viðmótseiningar og leið til að sniðmáta endurtekinn kóða til að spara okkur tíma, það vita allir að tími === peningar!

Money

Hér á eftir ætlum við að setja upp React verkefni og nota Storybook.js til að halda utan um viðmótseiningar og skoða nokkrar storybook viðbætur (e. addons).

Svo setjum við upp Hygen til að sniðmáta endurtekinn kóða, sem í leiðinni leyfir okkur að viðhalda samkvæmni á uppbyggingu viðmótseininga í verkefninu

Storybook.js

Storybook er síða sem keyrir samhliða verkefninu, og sýnir viðmótseiningar í einangruðu umhverfi. Þar getur þú þróað einingarnar og stillt þeim upp í mismunandi ástandi.

Storybook er með mjög virkt samfélag og helling af viðbótum. Hér eru nokkrar viðbætur sem ég get mælt með

  • Actions
    Sýnir gögn sem skila sér úr event handlerum (sjá sýnishorn í dæminu hér á eftir)
  • Knobs
    Bætir við stillieiginleikum fyrir eininguna (sjá sýnishorn í dæminu hér á eftir)
  • Info
    Notað til að skjala einingu, hægt að skjala með Markdown og bætir einnig við töflu út frá propTypes (sjá sýnishorn í dæminu hér á eftir)
  • a11y
    Hjálpar með aðgengismál
  • Jest
    Sýnir niðurstöður úr jest prófum inni í sögunni

Halló Storybook.js!

Við skulum prófa að henda upp litlu verkefni og tengja okkur inn í sögubókina.

Til að auðvelda okkur lífið þá „bootströppum“ við verkefnið með Create React App

$ yarn create react-app storybook
$ cd storybook

Svo þurfum við að sækja skipunarlínu tólið fyrir sögubókina, og keyra það inni í verkefninu

$ yarn global add @storybook/cli
$ getstorybook
$ yarn run storybook

Nú ættum við að sjá sögubókina undir http://localhost:9009/

Initial storybook

Stillingar

Áður en við byrjum á fullu er gott að stilla aðeins sögubókina út frá okkar þörfum.

Sjálfgefið þá fáum við stories undir ./src/stories í okkar tilfelli viljum við hafa söguna í sömu möppu og einingin, þá breytum við .storybook/config.js

import { configure } from '@storybook/react'

function loadStories() {
  const req = require.context('../src/components', true, /\.story\.js$/)
  req.keys().forEach(filename => req(filename))
}

configure(loadStories, module)

Hér tókum við út require('../src/stories'); og notum require.context() sem leyfir okkur að leita að skjölum í undirmöppum út frá regex, og við skilum svo öllum sögunum inni í configure.

Þar sem við erum að nota Create React App v2 þá kemur það með css modules, til að virkja það í storybook þá þurfum við að bæta við css modules í webpack.

.storybook/webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: 'style-loader',
          },
          {
            loader: 'css-loader',
            options: {
              modules: true,
            },
          },
        ],
      },
    ],
  },
}

Viðmótseiningar

Þá er næst að gera components möppuna og bæta við fyrsta componentinum okkar.

./src/components/Button/index.js
./src/components/Button/Button.js
./src/components/Button/Button.module.scss
./src/components/Button/Button.story.js

index.js

export { default } from './Button'

Button.js

import React from 'react'
import styles from './Button.module.css'

const Button = ({ children, className, ...props }) => {
  const classNames = (...args) => args.join(' ')
  const buttonClass = classNames(styles.button, className)
  return <button className={buttonClass} {...props}>{children}</button>
}

export default Button

Button.module.css

.button {
  background: transparent;
  border: 2px solid #AA72D8;
  color: #AA72D8;
  padding: 10px 20px;
  font-size: 16px;
}

Button.story.js

import React from 'react'
// Sögu fallið
import { storiesOf } from '@storybook/react'
import Button from './Button'

// Bætir við Button flokk í veftréð
storiesOf('Button', module)
  // Bætir við sögu undir Button
  .add('Default Button', () => (
    <Button>Button</Button>
  ))
}

Nú erum við komin með fyrstu viðmótseininguna okkar, og ætti þá sögubókin að líta svona út

First component

Nú getum við farið að nota viðbætur til að gera söguna gagnvirka.

Actions

Byrjum á því að bæta við Action logger til að sjá click eventið á takkanum

Button.story.js

...

import { action } from '@storybook/addon-actions'

storiesOf('Button', module)
  .add('Default Button', () => (
    <Button onClick={action('Clicked!')}>Button</Button>
  ))

Addon-action

Knobs

Bætum við biðstöðu eiginleika á takkann, og notum svo Knobs til að slökkva og kveikja á biðstöðu.

Fyrst þurfum við að sækja viðbótina yarn add -D @storybook/addon-knobs
Svo bætum við henni við í .storybook/addons.js

...
import '@storybook/addon-knobs/register'

Uppfærum takkann svo hann taki á móti isLoading

Button.js

...

const Button = ({ children, className, isLoading, ...props }) => {
  const classNames = (...args) => args.join(' ')
  const buttonClass = classNames(styles.button, className)
  return <button className={buttonClass} {...props}>{isLoading ? 'Loading...' : children}</button>
}

...

Button.story.js

...
import { withKnobs, boolean } from '@storybook/addon-knobs'

storiesOf('Button', module)
  // Bætum við decorator með Knobs
  .addDecorator(withKnobs)
  .add('Default Button', () => {
    // setjum upp boolean rofa
    const isLoading = boolean('isLoading', false)
    return <Button onClick={action('Clicked!')} isLoading={isLoading}>Button</Button>
  })

Addon-knobs

Info

Hvernig væri að henda smá skjölun á þetta?

Til þess ætlum við að nota info viðbótið yarn add -D @storybook/addon-info

Button.story.js

...
import { withInfo } from '@storybook/addon-info'

storiesOf('Button', module)
  // withInfo tekur á móti "options object" eða MD streng
  .addDecorator((story, context) => withInfo(`
    This is a **button**!
  `)(story)(context))
  .addDecorator(withKnobs)
  .add('Default Button', () => {
    const isLoading = boolean('isLoading', false)
    return <Button
      onClick={action('Clicked!')}
      isLoading={isLoading}
    >Button</Button>
  })

Addon-info

Eins og sést hér fyrir ofan þá er Button ekki með nein propTypes, við skulum bæta þeim við til að skoða props töfluna.

Byrjum á að sækja yarn add prop-types

Button.js

import React from 'react'
import styles from './Button.module.css'
import propTypes from 'prop-types'

const Button = ({ children, className, isLoading, ...props }) => {
  const classNames = (...args) => args.join(' ')
  const buttonClass = classNames(styles.button, className)
  return <button className={buttonClass} {...props}>{isLoading ? 'Loading...' : children}</button>
}

Button.propTypes = {
  /** Show button loading state */
  isLoading: propTypes.bool
}

Button.defaultProps = {
  isLoading: false
}

export default Button

Addon-info

Eins og sést hér á myndinni þá kemur taflan sjálfkrafa inn út frá propTypes á Button og með því að nota /** comment */ þá er hægt að fylla út í description dálkinn.

Hygen

Við erum búin að skoða hvernig við getum bætt Storybook.js við React verkefni. Næst ætlum við að skoða hvernig við getum minnkað endurtekningar með því að nota kóðasmið (e. code generator).

Hygen er skipunarlínu tól sem er auðvelt í notkun og býr til kóða út frá sniðmáti (e. template) sem við látum fylgja verkefninu. Sniðmátin eru skrifuð í EJS sem þýðir að við getum notað javascript lógík inni í sniðmátinu

Til að setja upp hygen þá getum við annaðhvort set það upp „globally“

$ yarn global add hygen

Eða keyrt það beint með npx

$ npx hygen ...

Til að byrja með þá frumstillum við sniðmátið okkar með því að keyra eftirfarandi frá rótinni á verkefninu

$ hygen init self 

Þá setur hygen upp generator sniðmát sem þú getum notað til að búa til önnur sniðmát

Inception

Í okkar tilfelli ætlum við að setja upp generator frá grunni.
Hygen möppustrúktúr er settur upp „_templates/ nafn á generator / nafn á sniðmáti / sniðmát“
Byrjum á að bæta við eftirfarandi skjölum

/_templates/gen/cmp/index.ejs.t
/_templates/gen/cmp/cmp.ejs.t
/_templates/gen/cmp/story.ejs.t
/_templates/gen/cmp/prompt.js

index.ejs.t

---
// Stillingar fyrir sniðmátið
// Stilling hvert skjalið á að fara, hér er hægt að nota ejs syntax og nálgast breytur sem eru settar í smiðinn
to: src/components/<%= name %>/index.js
---
export { default } from './<%= name %>'

cmp.ejs.t

---
to: src/components/<%= name %>/<%= name %>.js
---
import React from 'react'

const <%= name %> = () => {
  return (
    <div>
      <h2><%= name %> component</h2>
    </div>
  )
}

export default <%= name %>

story.ejs.t

---
// hér notum við ternary til að skila slóðini, ef við skilum null þá verður skjalið ekki til
to: "<%= story ? 'src/components/' + name  + '/' + name + '.story.js' : null %>"
---
import React from 'react'
import { storiesOf } from '@storybook/react'
import { withInfo } from '@storybook/addon-info'
import { withKnobs } from '@storybook/addon-knobs'
import <%= name %> from './<%= name %>'

storiesOf('<%= name %>', module)
  .addDecorator((story, context) => withInfo(`<%= name %> component`)(story)(context))
  .addDecorator(withKnobs)
  .add('<%= name %>', () => (
    <<%= name %> />
  ))

prompt.js

// see types of prompts:
// https://github.com/SBoudrias/Inquirer.js#prompt-types
//
// and for examples for prompts:
// https://github.com/SBoudrias/Inquirer.js/tree/master/examples
module.exports = [
  {
    type: 'input',
    name: 'name',
    message: "What's the name of your component?"
  },
  {
    type: 'list',
    name: 'story',
    message: 'Include story component?',
    choices: [{ name: 'Yes', value: true }, { name: 'No', value: false }]
  }
]

Hér erum við búin að taka endurtekningarnar út fyrir sviga og fyllum uppí með upplýsingum sem við sækjum úr prompt.js

Nú getum við prófað að keyra hygen

hygen demo

Hugleiðingar

  • Storybook nýtist okkur ekki einungis sem þróunarumhverfi og einingasafn, heldur er mjög góð handbók fyrir alla sem koma að verkefninu.
  • Hygen er mjög öflugt verkfæri sem getur flýtt fyrir þegar unnið er með mikið af skjölum sem eru bygð upp með sama ramma. Og er tilvalið til að halda uppi samkvæmni út verkefnið
  • Bæði Storybook og Hygen stækka með verkefninu og verða gagnlegri og verðmætari því meira sem þau eru notuð og uppfærð.
  • Sveigjanleiki er einn af stærstu eiginleikum þessara tóla, sem gefur manni kost á að stíla verkefnið algjörlega út frá sínu eigin höfði.

Allan kóða má finna hér