replace inline code with code snippets

This commit is contained in:
domyen 2020-08-11 11:20:57 -04:00
parent 5b90eae9ae
commit 5d7731a230
6 changed files with 150 additions and 87 deletions

View File

@ -0,0 +1,18 @@
```js
// .storybook/preview.js
import { normal as NavigationNormal } from '../components/Navigation.stories';
import GlobalContainerContext from '../components/lib/GlobalContainerContext';
const context = {
NavigationContainer: NavigationNormal,
};
const AppDecorator = (storyFn) => {
return (
<GlobalContainerContext.Provider value={context}>{storyFn()}</GlobalContainerContext.Provider>
);
};
addDecorator(AppDecorator);
```

View File

@ -0,0 +1,24 @@
```js
// pages/profile.js
import ProfilePageContext from './ProfilePageContext';
import ProfilePageContainer from './ProfilePageContainer';
import UserPostsContainer from './UserPostsContainer';
import UserFriendsContainer from './UserFriendsContainer';
// Ensure that your context value remains referentially equal between each render.
const context = {
UserPostsContainer,
UserFriendsContainer,
};
const AppProfilePage = () => {
return (
<ProfilePageContext.Provider value={context}>
<ProfilePageContainer />
</ProfilePageContext.Provider>
);
};
export default AppProfilePage;
```

View File

@ -0,0 +1,35 @@
```js
// ProfilePage.stories.js
import ProfilePage from './ProfilePage';
import UserPosts from './UserPosts';
import { normal as UserFriendsNormal } from './UserFriends.stories';
export default {
title: 'ProfilePage',
};
const ProfilePageProps = {
name: 'Jimi Hendrix',
userId: '1',
};
const context = {
// We can access the `userId` prop here if required:
UserPostsContainer({ userId }) {
return <UserPosts {...UserPostsProps} />;
},
// Most of the time we can simply pass in a story.
// In this case we're passing in the `normal` story export
// from the `UserFriends` component stories.
UserFriendsContainer: UserFriendsNormal,
};
export const normal = () => {
return (
<ProfilePageContext.Provider value={context}>
<ProfilePage {...ProfilePageProps} />
</ProfilePageContext.Provider>
);
};
```

View File

@ -0,0 +1,9 @@
```js
// ProfilePageContext.js
import { createContext } from 'react';
const ProfilePageContext = createContext();
export default ProfilePageContext;
```

View File

@ -0,0 +1,20 @@
```js
// ProfilePage.js
import { useContext } from 'react';
import ProfilePageContext from './ProfilePageContext';
const ProfilePage = ({ name, userId }) => {
const { UserPostsContainer, UserFriendsContainer } = useContext(ProfilePageContext);
return (
<div>
<h1>{name}</h1>
<UserPostsContainer userId={userId} />
<UserFriendsContainer userId={userId} />
</div>
);
};
export default ProfilePage;
```

View File

@ -50,7 +50,7 @@ In such cases it is natural to use [args composition](../writing-stories/args.md
<CodeSnippets
paths={[
'react/page-story-with-args-composition.js.mdx',
'react/page-story-with-args-composition.ts.mdx',
'react/page-story-with-args-composition.ts.mdx',
]}
/>
@ -157,80 +157,57 @@ ProfilePageContainer.js
ProfilePageContext.js
```
> Its also often useful to setup a “global” container context, (perhaps named `GlobalContainerContext`) for container components that may be rendered on every page of your app, and adding it to the top level of your application. While its possible to place every container within this global context, it should only provide containers that are required globally.
<div class="aside">
Its also often useful to setup a “global” container context, (perhaps named `GlobalContainerContext`) for container components that may be rendered on every page of your app, and adding it to the top level of your application. While its possible to place every container within this global context, it should only provide containers that are required globally.
</div>
Lets look at an example implementation of this approach.
First well need to create a React context, and we can name it `ProfilePageContext`. It does nothing more than export a React context:
```js
import { createContext } from 'react';
<!-- prettier-ignore-start -->
const ProfilePageContext = createContext();
<CodeSnippets
paths={[
'react/mock-context-create.js.mdx',
]}
/>
export default ProfilePageContext;
```
<!-- prettier-ignore-end -->
`ProfilePage` is our presentational component. It will use the `useContext` hook to retrieve the container components from `ProfilePageContext`:
```js
import { useContext } from 'react';
import ProfilePageContext from './ProfilePageContext';
<!-- prettier-ignore-start -->
const ProfilePage = ({ name, userId }) => {
const { UserPostsContainer, UserFriendsContainer } = useContext(ProfilePageContext);
<CodeSnippets
paths={[
'react/mock-context-in-use.js.mdx',
]}
/>
return (
<div>
<h1>{name}</h1>
<UserPostsContainer userId={userId} />
<UserFriendsContainer userId={userId} />
</div>
);
};
export default ProfilePage;
```
<!-- prettier-ignore-end -->
#### Mocking containers in Storybook
In the context of Storybook, instead of providing container components through context, well instead provide their mocked counterparts. In most cases, the mocked versions of these components can often be borrowed directly from their associated stories.
> If the same context applies to all `ProfilePage` stories, we can also use a Storybook decorator, See: https://storybook.js.org/docs/basics/writing-stories/#decorators
<!-- prettier-ignore-start -->
```js
import ProfilePage from './ProfilePage';
import UserPosts from './UserPosts';
import { normal as UserFriendsNormal } from './UserFriends.stories';
<CodeSnippets
paths={[
'react/mock-context-container.js.mdx',
]}
/>
export default {
title: 'ProfilePage',
};
<!-- prettier-ignore-end -->
const ProfilePageProps = {
name: 'Jimi Hendrix',
userId: '1',
};
<div class="aside">
const context = {
// We can access the `userId` prop here if required:
UserPostsContainer({ userId }) {
return <UserPosts {...UserPostsProps} />;
},
// Most of the time we can simply pass in a story.
// In this case we're passing in the `normal` story export
// from the `UserFriends` component stories.
UserFriendsContainer: UserFriendsNormal,
};
If the same context applies to all `ProfilePage` stories, we can also use a [decorator](../writing-stories/decorators.md).
export const normal = () => {
return (
<ProfilePageContext.Provider value={context}>
<ProfilePage {...ProfilePageProps} />
</ProfilePageContext.Provider>
);
};
```
</div>
#### Providing containers to your application
@ -238,46 +215,26 @@ Now, in context of your application, youll need to provide `ProfilePage` with
For example, in Next.js, this would be your `pages/profile.js` component.
```js
import ProfilePageContext from './ProfilePageContext';
import ProfilePageContainer from './ProfilePageContainer';
import UserPostsContainer from './UserPostsContainer';
import UserFriendsContainer from './UserFriendsContainer';
<!-- prettier-ignore-start -->
// Ensure that your context value remains referentially equal between each render.
const context = {
UserPostsContainer,
UserFriendsContainer,
};
<CodeSnippets
paths={[
'react/mock-context-container-provider.js.mdx',
]}
/>
const AppProfilePage = () => {
return (
<ProfilePageContext.Provider value={context}>
<ProfilePageContainer />
</ProfilePageContext.Provider>
);
};
export default AppProfilePage;
```
<!-- prettier-ignore-end -->
#### Mocking global containers in Storybook
If youve setup `GlobalContainerContext`, in order to provide context to all stories youll need to set up a decorator within Storybooks `preview.js`. For example:
```js
import { normal as NavigationNormal } from '../components/Navigation.stories';
import GlobalContainerContext from '../components/lib/GlobalContainerContext';
<!-- prettier-ignore-start -->
const context = {
NavigationContainer: NavigationNormal,
};
<CodeSnippets
paths={[
'react/mock-context-container-global.js.mdx',
]}
/>
const AppDecorator = (storyFn) => {
return (
<GlobalContainerContext.Provider value={context}>{storyFn()}</GlobalContainerContext.Provider>
);
};
addDecorator(AppDecorator);
```
<!-- prettier-ignore-end -->