ãã°ã説æãã
ã¹ããŒãªãŒã§ããã¯ãçŽæ¥äœ¿çšããããšã¯ã§ããŸããã Hooks can only be called inside the body of a function component
ã§å€±æããŸãã
åçŸããã«ã¯
ã³ãŒãäŸïŒ
import React from 'react'
import { storiesOf } from '@storybook/react'
const stories = storiesOf('Hooks test', module)
const TestComponent: React.FC = () => {
const [state, setState] = React.useState(5)
return (
<button onClick={() => setState(state + 1)}>
{state}
</button>
)
}
stories.add('this story works', () => <TestComponent />)
stories.add('this story fails', () => {
const [state, setState] = React.useState(5)
return (
<button onClick={() => setState(state + 1)}>
{state}
</button>
)
})
äºæ³ãããè¡å
æåã®ã¹ããŒãªãŒã¯æ£åžžã«æ©èœãã2çªç®ã®ã¹ããŒãªãŒã¯æåã®ã¬ã³ããªã³ã°ã§å€±æããŸã
ããŒãžã§ã³@storybook/[email protected]
[email protected]
[email protected]
ããããã°ãã©ããã¯ããããŸããã stories.add
ã®2çªç®ã®åŒæ°ã¯ãé¢æ°ãå®éã®Reactã³ã³ããŒãã³ãã§ã¯ãªããã³ã³ããŒãã³ããè¿ãããšãæåŸ
ããŠãããšç§ã¯ä¿¡ããŠããŸãã é¢æ°ã³ã³ããŒãã³ããå€åŽã«ç§»åããŠã¿ãŠãã ãããããçšåºŠæåããã¯ãã§ãã
äŸ
function SomeComponent() {
const [blah] = React.useState('blah');
return <div> {blah}</div>;
}
stories.add('BlahComponent', () => <SomeComponent />);
stories.add
ã®2çªç®ã®åŒæ°ã¯ãé¢æ°ãå®éã®Reactã³ã³ããŒãã³ãã§ã¯ãªããã³ã³ããŒãã³ããè¿ãããšãæåŸ ããŠãããšç§ã¯ä¿¡ããŠããŸãã
AFAIKã¯ããã»ã©éèŠã§ã¯ãããŸããããåžžã«è©Šã䟡å€ããããŸãã ãŸãã @ sargantã¯äœããšã©ãŒãã¹ããŒããŸããïŒ ã¹ããŒãªãŒããã¯ãŸãã¯åã·ã¹ãã ïŒ
@Keraitoãããããšã©ãŒã¯æ£ãããšç¢ºä¿¡ããŠããŸãã
ããã¯ãreactã®ã³ã³ããã¹ãããé¢æ°ãåŒã³åºããŠããããã§ããå¥åãã¹ããŒãªãŒããã¯ã¯æ¬¡ã®ããã«é¢æ°ãåŒã³åºããŸãã
const element = storyFn();
ãªã
const element = <StoryFn />
ããããããããæ©èœããããã«éå§ããå Žåã§ãã
ä»ã®ãšãã@gabefromutahã®ã¢ããã€ã¹ã¯æ£ããã§ãã
å®éã®ã³ãŒãè¡ã¯æ¬¡ã®ãšããã§ãã
https://github.com/storybooks/storybook/blob/next/app/react/src/client/preview/render.js#L24
誰ãããã®äœåãäœãããã«å®éšãããã®ãªãããããåºçºç¹ã ãšæããŸãã
@sargant @gabefromutahã®ææ¡ã¯ããªãã®ããã«åããŸããïŒ
@ndelangen @gabefromutahã®ææ¡ã¯ãç§ã®æåã®ããã®ã¹ããŒãªãŒã¯æ©èœããããšããäŸãšåãã ãšæããŸããïŒ
ããããã°ã§ã¯ãªãå Žåã§ããç¶æ ã«ãµãŒãããŒãã£ã®ãã©ã°ã€ã³ã䜿çšããå¿ èŠããªãããã«ããããã®äŸ¿å©ãªæ¡åŒµæ©èœã§ããå¯èœæ§ããããŸãã
ããããããããæ©èœããããã«éå§ããå Žåã§ãã
@ndelangenã¯ããããä»ã®ç¹å®ã®ã¢ããªã³ãç¹ã«åºã«ãªãã¬ã³ããªã³ã°ãããã³ã³ããŒãã³ãã®å°éå
·ãªã©ã®æ
å ±ãå¿
èŠãšããaddon-info
ãšã©ã®ããã«çžäºäœçšããããå®å
šã«ã¯ç¢ºä¿¡ããŠããŸããã
åãåé¡ãçºçããŸããããããã¯åé¿çãšããŠè¡ãç°¡åãªããã¯ã§ãã
stories.add('this story fails', () => React.createElement(() => {
const [state, setState] = React.useState(5)
return (
<button onClick={() => setState(state + 1)}>
{state}
</button>
)
}))
ãããã³ã°ãªãã§ãã€ãã£ãã«ãµããŒããããæ¹ãã¯ããã«è¯ããšæããŸãã ã¡ã³ãããåæããã°ãPRãèãåºãæéãèŠã€ããããšãã§ãããããããŸããðã
@ kevin940726ããã¯çŽ æŽãããã§ãããð
ç§ã®.storybook / config.jsã«ä»¥äžãè¿œå ãããšããŸããããŸãã
addDecorator((Story) => <Story />)
ç§ã¯äŒŒããããªãã®ãå®è£ ããŸããããããã¯ããããã®ã¢ããªã³ãç¹ã«å°éå ·ã®ããã¥ã¡ã³ããåºåããããã®ã¢ããªã³æ å ±ãå£ããšæããŸãã ç§ã¯ããã¯ã䜿çšããããšããã®æ å ±ãããéèŠã§ãããšå€æããŸããïŒãšã«ãããããå¥ã ã«çæããã®ã§ïŒãããããäžè¬çãªã¹ããŒãªãŒããã¯ã®ãã¹ãŠã«åœãŠã¯ãŸããšã¯æããŸããã reactã®å€ã§apiã®ãããªããã¯ãã©ã®ããã«åŠçãããããããŸããã
ãœãŒã¹ã³ãŒãã«å®è£ ããããšããŸããããstoryshot-addonã§åäœãããã®ã«èŠåŽããŠããŸãã ãã¹ãŠã®ã¹ãããã·ã§ããã§æ°ãã芪ããŒããçæããããããããã¯é倧ãªå€æŽã«ãªããšæããŸãã ãŠãŒã¶ãŒãéžæããã¬ã³ãã©ãŒãå¶åŸ¡ã§ããªãããã1ã¬ãã«æ·±ãæœãããšã¯ã§ããŸããã ãœãªã¥ãŒã·ã§ã³ãææžåãããŠãŒã¶ãŒããªããã€ã³ããããã®ãã«ããŒAPIãæäŸãããªã©ãä»ã®ä»£æ¿æ¡ãæ€èšããå¿ èŠããããšæããŸãã
ç§ã®.storybook / config.jsã«ä»¥äžãè¿œå ãããšããŸããããŸãã
addDecorator((Story) => <Story />)
@emjaksaãã®ã¹ãããããæäŸããŠ
ããã¯ããã®åé¡ã«å¯Ÿããç§ã®åé¿çã§ããã
import React, { useState } from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { withInfo } from '@storybook/addon-info';
import SelectField from 'component-folder/SelectField';
/**
* special wrapper that replaces the `value` and `onChange` properties to make
* the component work hooks
*/
const SelectFieldWrapper = props => {
const [selectValue, setValue] = useState('');
return (
<SelectField
{...props}
value={selectValue}
onChange={e => {
setValue(e.target.value);
action('onChange')(e.target.value);
}}
/>
);
};
SelectFieldWrapper.displayName = 'SelectField';
const info = {
text: SelectField.__docgenInfo.description,
propTables: [SelectField],
propTablesExclude: [SelectFieldWrapper]
};
storiesOf('Controls/SelectField', module)
.addDecorator(withInfo)
// ... some stories
// this example uses a wrapper component to handle the `value` and `onChange` props, but it should
// be interpreted as a <SelectField> component
.add('change handler', () =>
<SelectFieldWrapper
id="employment-status"
placeholder="some placeholder"
value={//selectValue}
onChange={e => {
// setValue(e.target.value);
}}
/>, { info });
ç§ãè¿°ã¹ãããã«ãããã§ãåé¿çã§ãããããã¯æ©èœãã info
ã¢ããªã³ãå£ããŸããã ïŒç§ã¯ä»ã®ã¢ããªã³ããã¹ãããŠããŸããïŒ
ã¹ããŒãªãŒãã³ã¬ãŒã¿ãæåŸã«è¿œå ããå Žåãæ å ±ã¢ããªã³ã¯å£ããŸããã
import React from 'react'
import { configure, addDecorator } from '@storybook/react'
import { withInfo } from '@storybook/addon-info'
import { withKnobs } from '@storybook/addon-knobs'
const req = require.context('../src', true, /\.stories\.js$/)
function loadStories() {
req.keys().forEach(filename => req(filename))
}
addDecorator(
withInfo({
header: false,
}),
)
addDecorator(withKnobs)
addDecorator((Story) => (
<Story />
))
configure(loadStories, module)
ããã«å¥ã®åé¿çïŒ
UseState
ãšãããŠãŒãã£ãªãã£ã³ã³ããŒãã³ãã次ã®ããã«å®çŸ©ãããŠããŸãã
export const UseState = ({ render, initialValue }) => {
const [ variable, setVariable ] = useState(initialValue)
return render(variable, setVariable)
}
ãããŠãç§ã¯ãã®ãããªç©èªã§ããã䜿çšããŸãïŒ
.add('use state example', () => (
<UseState
initialValue={0}
render={(counter, setCounter) => (
<button onClick={() => setCounter(counter + 1)} >Clicked {counter} times</button>
)}
/>
)
ããããç§ã¯@ kevin940726ã®åé¿çãäžçªå¥œãã§ã
ç¶æ ã®å€åã«ã€ããŠã¹ããŒãªãŒãåã¬ã³ããªã³ã°ããããšãã§ããŸããã 匷å¶çãªåã¬ã³ããªã³ã°ãæ©èœããŸããã
ãã®ã³ãŒãã¯Reactããã¯ã«ã¯ããŸãæ©èœããŸãã
storiesOf("Dropdowns", module).add("Basic", () => <DropdownBasicStory />);
@storybook/addon-info
ã§ã¯ããŸãæ©èœããŸããïŒ
ããã«ããããã®åé¿çã¯äœ¿çšã§ããªããªããŸãã äœãæ¡ã¯ïŒ ã¹ããŒãªãŒããã¯5.1.0-beta.0
@artyomtrityak addon-info
ã®propTables
ãªãã·ã§ã³ãäžæžãã§ããŸããïŒ ä»åŸã®addon-docs
ãããé©åã«è§£æ±ºããŸãïŒ https ïŒ
@shilmanã«ã¯ã <DropdownBasicStory />
ãœãŒã¹ãå«ãŸããŸããïŒ
@artyomtrityakç§ã«äœãã§ãããèŠãŠãããŸãð
çããããã«ã¡ã¯ïŒ æè¿ããã®åé¡ã¯ããŸãé²ãã§ããªãããã§ãã ããã§ã質åãã³ã¡ã³ãããã°ãããå Žåã¯ãé æ ®ãªãè°è«ãç¶ããŠãã ããã æ®å¿µãªããããã¹ãŠã®åé¡ã«åãçµãæéã¯ãããŸããã ç§ãã¡ã¯ãã€ã§ãå¯ä»ãåãä»ããŠããŸãã®ã§ããµããŒããå¿ èŠãªå Žåã¯ãã«ãªã¯ãšã¹ããéä¿¡ããŠãã ããã éã¢ã¯ãã£ããªåé¡ã¯30æ¥åŸã«ã¯ããŒãºãããŸãã ããããšãïŒ
ä»ã®èª°ãããŸã ãããååŸããŠããå Žåãåé¡ã¯æ©èœã³ã³ããŒãã³ãã«å°éå ·ã¿ã€ããè¿œå ããŠããããã§ãã
const Dropdown = () => (
// component content
);
Dropdown.propTypes = {
...
}
äœããã®çç±ã§.propTypes
ã³ã¡ã³ãã¢ãŠããããšãããŸãããããã§ãã ããã¥ã¡ã³ãã®è§£æãªã©ã®å°éå
·ã®çš®é¡ã«åé¡ããããã©ããã¯ããããŸããã
ããã¯ä»ãã®ã³ã³ããŒãã³ãã®ãœãŒã¹ã³ãŒãã衚瀺ããã«ã¯
function WithState({ children }) {
const [value, setValue] = React.useState([]);
return React.cloneElement(children, {
value,
onChange: event => setValue(event.target.value),
});
}
storiesOf(`${__dirname}`, module).add('Basic', () => (
<WithState>
<select value="[parent state]" onChange="[parent func]">
<option value="Australia">Australia</option>
<option value="Cambodia">Cambodia</option>
</select>
</WithState>
));
çããããã«ã¡ã¯ïŒ æè¿ããã®åé¡ã¯ããŸãé²ãã§ããªãããã§ãã ããã§ã質åãã³ã¡ã³ãããã°ãããå Žåã¯ãé æ ®ãªãè°è«ãç¶ããŠãã ããã æ®å¿µãªããããã¹ãŠã®åé¡ã«åãçµãæéã¯ãããŸããã ç§ãã¡ã¯ãã€ã§ãå¯ä»ãåãä»ããŠããŸãã®ã§ããµããŒããå¿ èŠãªå Žåã¯ãã«ãªã¯ãšã¹ããéä¿¡ããŠãã ããã éã¢ã¯ãã£ããªåé¡ã¯30æ¥åŸã«ã¯ããŒãºãããŸãã ããããšãïŒ
äžèšã®React.createElement
ãš<Story />
ã¢ãããŒãã«ã¯æ³šæããŠãã ãããå®éã«ã¯ãreactã³ã³ããŒãã³ãïŒã¹ããŒãªãŒã³ã³ããŒãã³ãïŒãè¿ãã®ã§ã¯ãªããåäœæããŠãããããæ£ãããªãå ŽåããããšæããŸããã¬ã³ããªã³ã°ãããèŠçŽ ïŒããã¥ã¡ã³ãã«ç€ºãããŠããããã«ãã¹ããŒãªãŒããã¯config.js
story()
ã䜿çšããŠããéïŒã
ããšãã°ãã¹ããŒãªãŒã«ã³ã³ããŒãã³ãã®ããŒã«ã«ç¶æ
ã®æŽæ°ãããªã¬ãŒããKnobs.button
ããå Žåããã¿ã³ãã¯ãªãã¯ããåŸãçŸåšã®ã³ã³ããŒãã³ãã®ç¶æ
ãæŽæ°ãã代ããã«ãæ°ããã¹ããŒãªãŒã³ã³ããŒãã³ããäœæããŠããå¯èœæ§ããããŸãã
ãã®åçŽãªã³ãŒãã¹ããããã§ç§ã®ä»®å®ã確èªã§ããŸãããã®ã¹ããããã§ã¯ããããã¿ã³ãã¯ãªãã¯ããŠãå€ã¯æŽæ°ãããŸããã
storiesOf('Test', module).add('with text', () => {
return React.createElement(() => {
const [value, setValue] = React.useState(1);
Knobs.button('Increase', () => setValue(prev => prev + 1));
return <span>{value}</span>;
});
});
ãããæ©èœãããã«ã¯ã次ã®ããšãã§ããŸãã
function MyStory() {
const [value, setValue] = React.useState(1);
Knobs.button('Increase', () => setValue(prev => prev + 1));
return <span>{value}</span>;
}
storiesOf('Test', module).add('with text', () => <MyStory />);
ãããã£ãŠãåé¿çãšããŠãã©ãããŒãäœæããŸããã
import {
DecoratorParameters,
Story,
StoryDecorator,
storiesOf as origStoriesOf,
} from '@storybook/react';
class ReactStory {
private readonly story: Story;
constructor(name: string, module: NodeModule) {
this.story = origStoriesOf(name, module);
}
public add(
storyName: string,
Component: React.ComponentType,
parameters?: DecoratorParameters
): this {
this.story.add(storyName, () => <Component />, parameters);
return this;
}
public addDecorator(decorator: StoryDecorator): this {
this.story.addDecorator(decorator);
return this;
}
public addParameters(parameters: DecoratorParameters): this {
this.story.addParameters(parameters);
return this;
}
}
new ReactStory('Test', module).add('with text', () => {
const [value, setValue] = React.useState(1);
Knobs.button('Increase', () => setValue(prev => prev + 1));
return <span>{value}</span>;
});
ãªãã·ã§ã³ã§ãã¹ããŒãªãŒããã¯APIãæš¡å£ããŠãªãã¡ã¯ã¿ãªã³ã°ã®ãªãŒããŒããããåæžããåé¡ãä¿®æ£ãããåŸã§å ã«æ»ãããšãã§ããŸãã
export function storiesOf(name: string, module: NodeModule) {
return new ReactStory(name, module);
}
ç§ã¯ééã£ãŠãããããããŸããããããããªããç§ã«ç¥ãããŠãã ããã 倧å€æè¬ããããŸãïŒ
ã¿ã!! ãã®åé¡ãåç §ããPRïŒ7571ãå«ãhttps://github.com/storybookjs/storybook/releases/tag/v5.2.0-beta.10ããªãªãŒã¹ããŸããã ä»ããã¢ããã°ã¬ãŒãããŠãè©Šããã ããïŒ
ãã®ãã¬ãªãªãŒã¹ã¯@next
NPMã¿ã°ã«ãããŸãã
ãã®åé¡ãéããŸãã ãŸã ãŸã ããããšããããšæãããå Žåã¯ãå床éããŠãã ããã
@shilmanããããšãïŒ äœ¿çšæ¹æ³ã®ã³ãŒãã¹ããããã®äŸã¯ãããŸããïŒ
@shilmanããããšãããããŸãïŒ
ããããããã§ããšã©ãŒãçºçããŸãïŒ
React Hook "useState"ã¯ãReacté¢æ°ã³ã³ããŒãã³ãã§ãã«ã¹ã¿ã ReactHooké¢æ°ã§ããªãé¢æ° "component"ã§åŒã³åºãããŸãã
@shiranZeæè¿ã®5.2ããŒã¿çã䜿çšããŠããŸããïŒ
@shilmanã¯ã...
stories / components / Menu / index.jsã®ç§ã®ã³ãŒãïŒ
constã³ã³ããŒãã³ã=ïŒïŒ=> {
const [buttonElãsetButtonEl] = useStateïŒnullïŒ
const handleClick = (event) => {
console.log('the event', event)
setButtonEl(event.currentTarget)
}
return (
<>
<IconButton iconName={"menu-hamburger"} size="s" onClick={handleClick} color={"midGray"}></IconButton>
ããã©ã«ãã®ãšã¯ã¹ããŒã[readmeãcomponent];
ããã³stories / components / index.jså
ïŒ
storiesOf('Components', module)
.addDecorator(withKnobs)
.add('Text', withReadme(...Menu))
@shiranZe addon-readme
ã®åé¡ã ãšæããŸã-å€åããã«åé¡ãæåºããŸããïŒ
è¿ä¿¡ããããšãããããŸã@shilman ã ãããåé¡ãã©ããã¯ããããŸããã addon-readme
ãªãã§è©ŠããŸããããåããšã©ãŒãçºçããŸãã 5.2ããŒã¿çã®ã¹ããŒãªãŒããã¯ã®URLã¯ãããŸããïŒ
https://storybooks-official.netlify.com/
ïŒãã®ä»|ãã¢/ãã¿ã³ïŒãèŠãŠã¿ãŸãã
ããããreactããã¯ã®äŸã¯èŠã€ãããŸããã
5.2.0-beta.30
ã«ã¯ãŸã åé¡ããããŸãããããã¢ããªã³ã䜿çšããŠãããããåé¿çãæ©èœããŠããŸããã
@shiranZeç§ãã¡ã®netlifyãããã€ã¯ãªãã«ãªã£ãŠããŸãïŒcc @ndelangenïŒãã next
ãã©ã³ãã®ãªããžããªããã§ãã¯ããŠããã§è©Šãããšãã§ããŸãã
@sourcesoftããã«é¢é£ããããã¬ãã¥ãŒããã¯ãã®åé¡ïŒcc @HypnosphiïŒã¯ããã®åé¡ãšã¯äœã®é¢ä¿ããªããšæããŸããå ±éããŠããã®ã¯ããã¯ã®æŠå¿µã ãã§ãã
@shilmanããããšããç§ã¯ããªããæ£ãããšæããŸãã ãšããã§ãè©Šè¡é¯èª€ã§ä¿®æ£ããæ¹æ³ãèŠã€ããã«ã¹ã¿ã ããã¯ãå€æŽããŸããã
export const useField = (id, updateField) => {
const onChange = useCallback((event) => {
const {
target: { value },
} = e;
updateField(id, value);
};
return {
onChange,
};
}, []);
ã«
export const useField = (id, updateField) => {
const onChange = (event) => {
const {
target: { value },
} = e;
updateField(id, value);
};
return {
onChange,
};
};
åºæ¬çã«ãããã§useCallback
ã®äœ¿çšãåé€ããŸããã æåã®ããŒãžã§ã³ãæå¹ãªããã¯ã§ãã£ããã©ããã¯ããããŸãããã以åã¯æ©èœããŠããŸããã ãŸãããšã©ãŒãHooks can only be called inside the body of a function component
å ŽåããReactã®è€æ°ã®ããŒãžã§ã³ãããå Žåã¯ãå°ãæ··ä¹±ããŸãã
äžèšã®äŸã§ã¯ã useCallback
åé€ããåŸãå®éã«useField
ããã¯ãç»é²ããããš
ããä»ãã®ããã¯ã®äœ¿çšã«åé¡ãããããã§ãã 誰ããç°¡åãªåçŸãæäŸã§ãããªããç§ã¯ãããèŠãŠåãã§ããŸã
@shilman以åã«æçš¿ããäŸãè©ŠããŸãããïŒ ã¹ããããã¯æ¬¡ã®ãšããã§ãã
storiesOf('Test', module).add('with text', () => {
return React.createElement(() => {
const [value, setValue] = React.useState(1);
Knobs.button('Increase', () => setValue(prev => prev + 1));
return <span>{value}</span>;
});
});
å€ãAPIã䜿çšããŠããŸããããã¹ãçšã«ææ°ã®APIã«ç°¡åã«å€æã§ããã¯ãã§ãã å ã®æçš¿ããäºæ³ãããåäœãèŠã€ããããšãã§ããŸãã
@zhenwencåèãŸã§ã«ãã³ãŒãã¯æ©èœããŸããã react-docgen-typescript-webpack-plugin
ã«ãã£ãŠã¬ã³ããªã³ã°ãããããã¥ã¡ã³ãã®äœ¿çšãäžæããŸãã
åé¿çã¯å°ãè匱ã§ãä»ã®ã©ã€ãã©ãªãšç«¶åããŠããããã§ãã å¥ã®æ¹æ³ãšããŠã誰ãã䜿çšããçµéšããããŸããïŒïŒ https ïŒ
ããã§ãšã©ãŒã¡ãã»ãŒãžãã°ãŒã°ã«ã§æ€çŽ¢ããŠãã人ã®ããã«ïŒ
ã¯ã©ã¹ã³ã³ããŒãã³ããããã¯ä»ãã®æ©èœã³ã³ããŒãã³ãã«å€æŽãã useState
ã@storybook/addons
ãã誀ã£ãŠã€ã³ããŒãããããšãã«ããã®ãšã©ãŒãçºçããŸããã ç§ã¯ããæ¥ãŠããããå¿
èŠãšreact
ã§ã¯ãªã@storybook/addons
èªåã€ã³ããŒãã倱æããŸã...ã
@orpheusã®ã¹ããããã®ãªã¯ãšã¹ãã«çããã ãã§ã
ç§ã®.storybook / config.jsã«ä»¥äžãè¿œå ãããšããŸããããŸãã
addDecorator((Story) => <Story />)
@emjaksaãã®ã¹ãããããæäŸããŠ
diff --git a/.storybook/config.js b/.storybook/config.js
--- a/.storybook/config.js
+++ b/.storybook/config.js
@@ -1,6 +1,9 @@
-import { configure } from '@storybook/react';
+import { configure, addDecorator } from '@storybook/react';
+import React from 'react';
// automatically import all files ending in *.stories.js
configure(require.context('../stories', true, /\.stories\.js$/), module);
+
+addDecorator((Story) => <Story />);
çµæïŒå®å
šãª.storybook/config.js
ïŒïŒ
import { configure, addDecorator } from '@storybook/react';
import React from 'react';
// automatically import all files ending in *.stories.js
configure(require.context('../stories', true, /\.stories\.js$/), module);
addDecorator((Story) => <Story />);
ãããã¹ããŒãªãŒããã¯å
ã§reactããã¯ãæ©èœãããããã®æè¯ã®æ¹æ³ãã©ããã¯ããããŸããããã¹ããŒãªãŒããã¯ãç¬èªã®ã³ã³ããŒãã³ãã§ã©ãããã<Story />
ã³ã³ããŒãã³ãã¯ããã§"@storybook/react": "^5.2.6",
ã
ãããè¡ãåã«ãããïŒã€ãŸããããŒã«å€ïŒãæŽæ°ãããã³ã«ãæåã®ã¬ã³ããªã³ã°ã§æ©èœããŸãããããã®åŸã¬ã³ããªã³ã°ãåæ¢ããŸããã äžèšã®è§£æ±ºçã¯ãããä¿®æ£ããŸããã ãšããã§ãã¹ããŒãªãŒããã¯ã§åå¿ããã¯ã䜿çšããã®ãè¯ãç¿æ £ãã©ããã¯ããããŸãããå¯èœã§ããã°ãã¹ãŠãã¢ãã¯ããã ãã§ãããããããã®æ¹æ³ã®æ¹ãè¯ãã§ãããã
ãã°ã説æãã
ã¹ããŒãªãŒã§ããã¯ãçŽæ¥äœ¿çšããããšã¯ã§ããŸããã
Hooks can only be called inside the body of a function component
ã§å€±æããŸããåçŸããã«ã¯
ã³ãŒãäŸïŒ
import React from 'react' import { storiesOf } from '@storybook/react' const stories = storiesOf('Hooks test', module) const TestComponent: React.FC = () => { const [state, setState] = React.useState(5) return ( <button onClick={() => setState(state + 1)}> {state} </button> ) } stories.add('this story works', () => <TestComponent />) stories.add('this story fails', () => { const [state, setState] = React.useState(5) return ( <button onClick={() => setState(state + 1)}> {state} </button> ) })
äºæ³ãããè¡å
æåã®ã¹ããŒãªãŒã¯æ£åžžã«æ©èœãã2çªç®ã®ã¹ããŒãªãŒã¯æåã®ã¬ã³ããªã³ã°ã§å€±æããŸãããŒãžã§ã³
@storybook/[email protected]
[email protected]
[email protected]
======================================
ç¢å°æ©èœã䜿çšããŠæ©èœã³ã³ããŒãã³ããäœæããªãã§ãã ããã
以äžã®äŸã®1ã€ãšããŠå®è¡ããŸãã
function MyComponent(props) {
const [states, setStates] = React.useState({ value: '' });
return (
<input
type="text"
value={states.value}
onChange={(event) => setStates({ value: event.target.value })}
/>
);
}
ãŸãã¯
//IMPORTANT: Repeat the function name
const MyComponent = function MyComponent(props) {
const [states, setStates] = React.useState({ value: '' });
return (
<input
type="text"
value={states.value}
onChange={(event) => setStates({ value: event.target.value })}
/>
);
};
ãrefãïŒããããã«ãŒãå ïŒã«åé¡ãããå Žåã解決çã¯forwardRefïŒïŒã䜿çšããããšã§ãã
// IMPORTANT: Repeat the function name
// Add the "ref" argument to the function, in case you need to use it.
const MyComponent = React.forwardRef( function MyComponent(props, ref) {
const [states, setStates] = React.useState({ value: '' });
return (
<input
type="text"
value={states.value}
onChange={(event) => setStates({ value: event.target.value })}
/>
);
});
ããã¯MDXã§ã©ã®ããã«éæã§ããŸããïŒ
ããããããã§ããšã©ãŒãçºçããŸãïŒ
React Hook "useState"ã¯ãReacté¢æ°ã³ã³ããŒãã³ãã§ãã«ã¹ã¿ã ReactHooké¢æ°ã§ããªãé¢æ° "component"ã§åŒã³åºãããŸãã
https://github.com/storybookjs/storybook/issues/5721#issuecomment -518225880
ç§ã¯ãããšãŸã£ããåãåé¡ãæ±ããŠããŸããã ä¿®æ£ã¯ãååä»ãã¹ããŒãªãŒã®ãšã¯ã¹ããŒãã倧æåã«ããããšã§ãã
'react'ããReactãã€ã³ããŒãããŸãã './Foo'ããFooãã€ã³ããŒãããŸãã ããã©ã«ãã®ãšã¯ã¹ããŒã{ ã¿ã€ãã«ïŒ 'Foo'; }; export const Basic =ïŒïŒ=> <Foo />
ããã¥ã¡ã³ãã«ã¯æ䟡ç·é¡ãæšå¥šããããšæžãããŠããŸããããã®èŠåãæ¶ãããå Žåã¯å¿ èŠã§ãã
ç§ã®å Žåãåé¡ã¯ã䜿çšããŠããããã¯ãã€ã³ããŒãããã®ãå¿ããããšã§ããã
ç§ã®.storybook / config.jsã«ä»¥äžãè¿œå ãããšããŸããããŸãã
addDecorator((Story) => <Story />)
@emjaksaããããšãïŒ Storybook v5.3ã§ãã¹ããããæ£åžžã«åäœããŠããŸãã
åãåé¡ãçºçããŸãããæ©èœçãªreactã³ã³ããŒãã³ãã«useState(value)
ããã value
ãKnobsããæ°ããå€ãåã¬ã³ããªã³ã°ããŸãã
Reactããã¥ã¡ã³ãããïŒ
å®çšçãªãœãªã¥ãŒã·ã§ã³Storybookv5.3ïŒ
Preview.jsãæŽæ°ããŸã
.storybook/preview.js
import React from 'react'; // Important to render the story
import { withKnobs } from '@storybook/addon-knobs';
import { addDecorator } from '@storybook/react';
addDecorator(withKnobs);
addDecorator(Story => <Story />); // This guy will re-render the story
ãŸããmain.jsãæŽæ°ããŸã
.storybook/main.js
module.exports = {
stories: ['../src/**/*.stories.jsx'],
addons: [
'@storybook/preset-create-react-app',
'@storybook/addon-knobs/register', // Attention to this guy
'@storybook/addon-actions',
'@storybook/addon-links',
],
webpackFinal: async config => {
return config;
},
};
â
ãã®åŒã³åºããã³ã¬ãŒã¿ãããã¯ãæ©èœãããçç±ã誰ããç¥ã£ãŠããŸããïŒ
`` `tsx
SomeComponent.decorators = [ïŒStoryïŒ=>
@tmikeschuããã¯ã <Story />
ãããã¯ã«å¿
èŠãªReact.createElement
ã§ã³ãŒããã©ããããŠããããã§ãã
@shilmanããããšãããããŸãïŒ
æãåèã«ãªãã³ã¡ã³ã
åãåé¡ãçºçããŸããããããã¯åé¿çãšããŠè¡ãç°¡åãªããã¯ã§ãã
ãããã³ã°ãªãã§ãã€ãã£ãã«ãµããŒããããæ¹ãã¯ããã«è¯ããšæããŸãã ã¡ã³ãããåæããã°ãPRãèãåºãæéãèŠã€ããããšãã§ãããããããŸããðã