Skip to content

Commit 31a7033

Browse files
Roshan JossyRoshanjossey
authored andcommitted
setup storybook
make storybook work with tailwind implement storybook for component with relay fragment
1 parent a2e16ca commit 31a7033

19 files changed

+9529
-129
lines changed

.eslintrc.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
},
66
"extends": [
77
"plugin:@typescript-eslint/recommended",
8+
"plugin:storybook/recommended",
89
"next",
910
"next/core-web-vitals",
1011
"prettier"

.storybook/RelayStorybook.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { ReactNode, useEffect, useState } from 'react'
2+
import { createMockEnvironment, MockPayloadGenerator } from 'relay-test-utils'
3+
4+
import { IEnvironment } from 'relay-runtime'
5+
import RelayEnvironmentProvider from '../src/lib/RelayEnvironmentProvider'
6+
import RelayModernEnvironment from 'relay-runtime/lib/store/RelayModernEnvironment'
7+
8+
type RelayStorybookProps = {
9+
children: ReactNode
10+
mockResolvers?: Record<string, unknown>
11+
}
12+
export const RelayStorybook = ({
13+
children,
14+
mockResolvers = {},
15+
}: RelayStorybookProps) => {
16+
const [environment] = useState(() => createMockEnvironment())
17+
18+
useEffect(() => {
19+
try {
20+
environment.mock.resolveMostRecentOperation((operation) =>
21+
MockPayloadGenerator.generate(operation, mockResolvers)
22+
)
23+
} catch (err) {
24+
// handle no operation
25+
// eslint-disable-next-line
26+
console.log({
27+
err,
28+
})
29+
}
30+
}, [])
31+
32+
return (
33+
<RelayEnvironmentProvider environment={environment}>
34+
{children}
35+
</RelayEnvironmentProvider>
36+
)
37+
}

.storybook/connectionFromArray.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
export const connectionFromArray = <T extends object>(arr: T[] = []) => {
2+
if (!arr || arr.length === 0) {
3+
return {
4+
edges: [],
5+
totalCount: 0,
6+
count: 0,
7+
endCursorOffset: 0,
8+
startCursorOffset: 0,
9+
pageInfo: {
10+
hasNextPage: false,
11+
hasPreviousPage: false,
12+
},
13+
}
14+
}
15+
16+
return {
17+
edges: arr.map((node) => ({ node })),
18+
totalCount: arr.length,
19+
count: arr.length,
20+
endCursorOffset: arr.length,
21+
startCursorOffset: 0,
22+
pageInfo: {
23+
hasNextPage: false,
24+
hasPreviousPage: false,
25+
},
26+
}
27+
}

.storybook/main.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
module.exports = {
2+
stories: ['../**/*.stories.mdx', '../**/*.stories.@(js|jsx|ts|tsx)'],
3+
/** Expose public folder to storybook as static */
4+
staticDirs: ['../public'],
5+
addons: [
6+
'@storybook/addon-links',
7+
'@storybook/addon-essentials',
8+
'@storybook/addon-interactions',
9+
'storybook-css-modules-preset',
10+
{
11+
/**
12+
* Fix Storybook issue with PostCSS@8
13+
* @see https://github.com/storybookjs/storybook/issues/12668#issuecomment-773958085
14+
*/
15+
name: '@storybook/addon-postcss',
16+
options: {
17+
postcssLoaderOptions: {
18+
implementation: require('postcss'),
19+
},
20+
},
21+
},
22+
],
23+
framework: '@storybook/react',
24+
core: {
25+
builder: '@storybook/builder-webpack5',
26+
},
27+
}

.storybook/preview.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import '../src/styles/globals.css'
2+
import * as NextImage from 'next/image'
3+
4+
const BREAKPOINTS_INT = {
5+
xs: 375,
6+
sm: 600,
7+
md: 900,
8+
lg: 1200,
9+
xl: 1536,
10+
}
11+
12+
const customViewports = Object.fromEntries(
13+
Object.entries(BREAKPOINTS_INT).map(([key, val], idx) => {
14+
console.log(val)
15+
return [
16+
key,
17+
{
18+
name: key,
19+
styles: {
20+
width: `${val}px`,
21+
height: `${(idx + 5) * 10}vh`,
22+
},
23+
},
24+
]
25+
})
26+
)
27+
28+
// Allow Storybook to handle Next's <Image> component
29+
const OriginalNextImage = NextImage.default
30+
31+
Object.defineProperty(NextImage, 'default', {
32+
configurable: true,
33+
value: (props) => <OriginalNextImage {...props} unoptimized />,
34+
})
35+
36+
export const parameters = {
37+
actions: { argTypesRegex: '^on[A-Z].*' },
38+
controls: {
39+
matchers: {
40+
color: /(background|color)$/i,
41+
date: /Date$/,
42+
},
43+
},
44+
viewport: { viewports: customViewports },
45+
}

package.json

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
"check-lint": "eslint . --ext ts --ext tsx --ext js",
1111
"check-types": "tsc --pretty --noEmit",
1212
"format": "prettier --write .",
13-
"prepare": "husky install"
13+
"prepare": "husky install",
14+
"storybook": "start-storybook -p 3333",
15+
"build-storybook": "build-storybook"
1416
},
1517
"dependencies": {
1618
"@editorjs/code": "^2.7.0",
@@ -39,6 +41,16 @@
3941
"string-strip-html": "^9.1.12"
4042
},
4143
"devDependencies": {
44+
"@babel/core": "^7.18.6",
45+
"@storybook/addon-actions": "^6.5.9",
46+
"@storybook/addon-essentials": "^6.5.9",
47+
"@storybook/addon-interactions": "^6.5.9",
48+
"@storybook/addon-links": "^6.5.9",
49+
"@storybook/addon-postcss": "^2.0.0",
50+
"@storybook/builder-webpack5": "^6.5.9",
51+
"@storybook/manager-webpack5": "^6.5.9",
52+
"@storybook/react": "^6.5.9",
53+
"@storybook/testing-library": "^0.0.13",
4254
"@tailwindcss/typography": "^0.5.2",
4355
"@types/formidable": "^2.0.5",
4456
"@types/multiparty": "^0.0.33",
@@ -50,12 +62,14 @@
5062
"@typescript-eslint/eslint-plugin": "^5.28.0",
5163
"@typescript-eslint/parser": "^5.28.0",
5264
"autoprefixer": "^10.4.2",
65+
"babel-loader": "^8.2.5",
5366
"babel-plugin-relay": "^13.0.2",
5467
"eslint": "^8.18.0",
5568
"eslint-config-next": "12.0.10",
5669
"eslint-config-prettier": "^8.5.0",
5770
"eslint-import-resolver-typescript": "^2.7.1",
5871
"eslint-plugin-prettier": "^4.0.0",
72+
"eslint-plugin-storybook": "^0.5.13",
5973
"graphql": "^16.3.0",
6074
"husky": "^8.0.0",
6175
"lint-staged": "^13.0.2",
@@ -64,6 +78,8 @@
6478
"relay-compiler": "^13.0.2",
6579
"relay-compiler-language-typescript": "^15.0.1",
6680
"relay-config": "^12.0.1",
81+
"relay-test-utils": "^14.0.0",
82+
"storybook-css-modules-preset": "^1.1.1",
6783
"tailwindcss": "^3.0.23",
6884
"typescript": "4.5.5"
6985
},
@@ -81,5 +97,8 @@
8197
"*.json": [
8298
"prettier --write"
8399
]
100+
},
101+
"resolutions": {
102+
"webpack": "^5"
84103
}
85104
}

src/components/issue/Issue.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { graphql, useFragment } from 'react-relay'
22
import { Issue_node$key } from '../../queries/__generated__/Issue_node.graphql'
33

4-
type IssueProps = {
4+
export type IssueProps = {
55
issue: Issue_node$key
66
}
77

src/lib/RelayEnvironmentProvider.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { useMemo } from 'react'
2+
3+
import { ReactRelayContext } from 'react-relay'
4+
5+
import { Environment } from 'relay-runtime'
6+
7+
type Props = {
8+
children: React.ReactNode
9+
environment: Environment
10+
}
11+
const RelayEnvironmentProvider = (props: Props) => {
12+
const { children, environment } = props
13+
// TODO(T39494051) - We're setting empty variables here to make Flow happy
14+
// and for backwards compatibility, while we remove variables from context
15+
// in favor of fragment ownership
16+
const context = useMemo(() => ({ environment, variables: {} }), [environment])
17+
18+
return (
19+
<ReactRelayContext.Provider value={context}>
20+
{children}
21+
</ReactRelayContext.Provider>
22+
)
23+
}
24+
25+
export default RelayEnvironmentProvider

src/stories/Button.stories.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import React from 'react';
2+
import { ComponentStory, ComponentMeta } from '@storybook/react';
3+
4+
import Button from '../components/Button';
5+
6+
export default {
7+
title: 'Atoms/Button',
8+
component: Button,
9+
} as ComponentMeta<typeof Button>;
10+
11+
const Template: ComponentStory<typeof Button> = (args) => <Button {...args} />;
12+
13+
export const Primary = Template.bind({});
14+
Primary.args = {
15+
children: 'Button',
16+
};

0 commit comments

Comments
 (0)