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!
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 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
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/
Áð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,
},
},
],
},
],
},
}
Þá 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
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>
))
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>
})
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>
})
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
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.
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
Í 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
-
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