From 38a5435ec575884eda5a560a05a3a58be1da069a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=84=B8=EB=AF=BC?= <89172499+semnil5202@users.noreply.github.com> Date: Thu, 18 Jul 2024 23:11:15 +0900 Subject: [PATCH] =?UTF-8?q?chore(web,=20sds):=20Storybook=20&=20Chromatic?= =?UTF-8?q?=20=EC=84=B8=ED=8C=85=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: apps/web storybook init * storybook: 기본 컴포넌트 제거 및 Home 스토리북 추가 * chore: sds storybook init * chore: storybook log ignore * chore: rebase 이후 storybook lock 파일 변경사항 적용 * chore: Home, Button Storybook 작성 css prop 안 먹힘 react not defined 문제 * fix: eslint 에러 수정 * fix: react not defined, css props 오류 해결 * chore: pnpm 8.15.6 버전 적용 * refactor: storybook control 파라미터 추가 * feat: FirstFeatureOfFirstDomainTestButton Storybook 생성 * refactor: Props 오타 수정 * refactor: Storybook 디렉토리명 변경 * fix: FirstFeatureOfFirstDomainTestButton 'use client' 추가 * refactor: FirstFeatureOfFirstDomainTestButton client Component 적용 * refactor: 불필요한 use client 제거 * chore: chromatic 설정 * chore: chromatic deploy yml 작성 * refactor: chromatic yml log * refactor: chromatic yml log-2 * refactor: chromatic yml log-3 * refactor: chromatic yml deploy * refactor: chromatic yml deploy-2 * refactor: chromatic yml deploy-3 * refactor: chromatic yml deploy-4 * refactor: chromatic yml deploy-5 * refactor: chromatic yml deploy-6 * chore: chromatic deployment PR Comment * chore: chromatic deployment PR Comment-2 * chore: chromatic deployment PR Comment-3 * chore: chromatic deployment PR Comment-finally * chore: chromatic deployment PR Comment new line * chore: storybook sds port 번호 변경 --- .github/workflows/chromatic.yml | 61 + .gitignore | 2 + apps/web/.eslintrc.js | 2 +- apps/web/.gitignore | 2 + apps/web/.storybook/main.ts | 40 + apps/web/.storybook/preview.ts | 14 + apps/web/package.json | 24 +- ...FeatureOfFirstDomainTestButton.stories.tsx | 25 + packages/core/sds/.eslintrc.js | 2 +- packages/core/sds/.storybook/main.ts | 40 + packages/core/sds/.storybook/preview.ts | 14 + packages/core/sds/package.json | 19 +- packages/core/sds/src/components/Button.tsx | 5 +- .../core/sds/src/stories/Button.stories.tsx | 35 + .../FirstFeatureOfFirstDomainTestButton.tsx | 6 +- .../web-domains/src/first-domain/index.ts | 2 + pnpm-lock.yaml | 7754 +++++++++++++++-- 17 files changed, 7208 insertions(+), 839 deletions(-) create mode 100644 .github/workflows/chromatic.yml create mode 100644 apps/web/.storybook/main.ts create mode 100644 apps/web/.storybook/preview.ts create mode 100644 apps/web/stories/FirstFeatureOfFirstDomainTestButton.stories.tsx create mode 100644 packages/core/sds/.storybook/main.ts create mode 100644 packages/core/sds/.storybook/preview.ts create mode 100644 packages/core/sds/src/stories/Button.stories.tsx diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml new file mode 100644 index 00000000..e9e660f1 --- /dev/null +++ b/.github/workflows/chromatic.yml @@ -0,0 +1,61 @@ +name: 'Chromatic Deployment' + +on: + pull_request: + branches: [main, develop] + paths: + - '**.stories.tsx' + +jobs: + chromatic-deployment: + runs-on: ubuntu-latest + steps: + - uses: pnpm/action-setup@v4 + with: + version: 8 + + - name: Checkout Repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Cache Dependencies + id: cache + uses: actions/cache@v3 + with: + path: '**/node_modules' + key: ${{ runner.os }}-node-${{ hashFiles('**/pnpm-lock.yaml') }}-storybook + restore-keys: | + ${{ runner.os }}-node- + + - name: Install Dependencies + if: steps.cache.outputs.cache-hit != 'true' + run: pnpm install + + - name: Publish Project packages/core/sds to Chromatic + id: chromatic-sds + uses: chromaui/action@latest + with: + projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN_SDS }} + workingDir: packages/core/sds + onlyChanged: true + externals: packages/core/sds/assets/** + + - name: Publish Project apps/web to Chromatic + id: chromatic-web + uses: chromaui/action@latest + with: + projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN_WEB }} + workingDir: apps/web + onlyChanged: true + externals: packages/web-domains/assets/** + + - name: Storybook Deployment PR Comment + uses: thollander/actions-comment-pull-request@v1 + if: ${{ github.event_name == 'pull_request' }} + env: + GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} + with: + message: | + 📣 SDS Storybook Deployment: ${{ steps.chromatic-sds.outputs.storybookUrl }} + 📣 Web Storybook Deployment: ${{ steps.chromatic-web.outputs.storybookUrl }} diff --git a/.gitignore b/.gitignore index 96fab4fe..a341f14e 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,5 @@ yarn-error.log* # Misc .DS_Store *.pem + +*storybook.log \ No newline at end of file diff --git a/apps/web/.eslintrc.js b/apps/web/.eslintrc.js index bd52bc7f..dd0da5c5 100644 --- a/apps/web/.eslintrc.js +++ b/apps/web/.eslintrc.js @@ -1,7 +1,7 @@ /** @type {import("eslint").Linter.Config} */ module.exports = { root: true, - extends: ['@sambad/eslint-config/next.js'], + extends: ['@sambad/eslint-config/next.js', 'plugin:storybook/recommended'], parser: '@typescript-eslint/parser', parserOptions: { tsconfigRootDir: __dirname, diff --git a/apps/web/.gitignore b/apps/web/.gitignore index fd3dbb57..a112a9b0 100644 --- a/apps/web/.gitignore +++ b/apps/web/.gitignore @@ -34,3 +34,5 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +*storybook.log \ No newline at end of file diff --git a/apps/web/.storybook/main.ts b/apps/web/.storybook/main.ts new file mode 100644 index 00000000..f4b7c868 --- /dev/null +++ b/apps/web/.storybook/main.ts @@ -0,0 +1,40 @@ +import type { StorybookConfig } from '@storybook/nextjs'; + +import { dirname, join } from 'path'; + +function getAbsolutePath(value: string): any { + return dirname(require.resolve(join(value, 'package.json'))); +} +const config: StorybookConfig = { + stories: ['../stories/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + addons: [ + getAbsolutePath('@storybook/addon-onboarding'), + getAbsolutePath('@storybook/addon-links'), + getAbsolutePath('@storybook/addon-essentials'), + getAbsolutePath('@chromatic-com/storybook'), + getAbsolutePath('@storybook/addon-interactions'), + ], + framework: { + name: getAbsolutePath('@storybook/nextjs'), + options: {}, + }, + docs: { + autodocs: 'tag', + }, + staticDirs: ['../public'], + swc: () => ({ + jsc: { + parser: { + syntax: 'typescript', + tsx: true, + }, + transform: { + react: { + runtime: 'automatic', + importSource: '@emotion/react', + }, + }, + }, + }), +}; +export default config; diff --git a/apps/web/.storybook/preview.ts b/apps/web/.storybook/preview.ts new file mode 100644 index 00000000..adcda96b --- /dev/null +++ b/apps/web/.storybook/preview.ts @@ -0,0 +1,14 @@ +import type { Preview } from '@storybook/react'; + +const preview: Preview = { + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + }, +}; + +export default preview; diff --git a/apps/web/package.json b/apps/web/package.json index a95286e1..f62bae28 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -7,22 +7,36 @@ "build": "next build", "start": "next start", "lint": "next lint", - "type-check": "tsc --noEmit" + "type-check": "tsc --noEmit", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build" }, "dependencies": { "@sambad/web-domains": "workspace:*", + "next": "14.2.4", "react": "18.2.0", - "react-dom": "18.2.0", - "next": "14.2.4" + "react-dom": "18.2.0" }, "devDependencies": { + "@chromatic-com/storybook": "^1.6.1", "@sambad/eslint-config": "workspace:*", "@sambad/typescript-config": "workspace:*", - "typescript": "^5", + "@storybook/addon-essentials": "^8.2.2", + "@storybook/addon-interactions": "^8.2.2", + "@storybook/addon-links": "^8.2.2", + "@storybook/addon-onboarding": "^8.2.2", + "@storybook/blocks": "^8.2.2", + "@storybook/nextjs": "^8.2.2", + "@storybook/react": "^8.2.2", + "@storybook/test": "^8.2.2", "@types/node": "^20", "@types/react": "^18.2.0", "@types/react-dom": "^18.2.0", + "chromatic": "^11.5.5", "eslint": "^8", - "eslint-config-next": "14.2.4" + "eslint-config-next": "14.2.4", + "eslint-plugin-storybook": "^0.8.0", + "storybook": "^8.2.2", + "typescript": "^5" } } diff --git a/apps/web/stories/FirstFeatureOfFirstDomainTestButton.stories.tsx b/apps/web/stories/FirstFeatureOfFirstDomainTestButton.stories.tsx new file mode 100644 index 00000000..8d1aadd2 --- /dev/null +++ b/apps/web/stories/FirstFeatureOfFirstDomainTestButton.stories.tsx @@ -0,0 +1,25 @@ +import { FirstFeatureOfFirstDomainTestButton } from '@sambad/web-domains/first-domain'; + +import type { Meta, StoryObj } from '@storybook/react'; + +const meta = { + title: 'Components/FirstFeatureOfFirstDomainTestButton', + component: FirstFeatureOfFirstDomainTestButton, + tags: ['autodocs'], + argTypes: { + children: { + control: 'text', + description: 'FirstFeatureOfFirstDomainTestButton 컴포넌트가 표시할 내용을 정의합니다.', + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + children: 'Button', + }, + render: (args) => , +}; diff --git a/packages/core/sds/.eslintrc.js b/packages/core/sds/.eslintrc.js index ab6f4083..cb355149 100644 --- a/packages/core/sds/.eslintrc.js +++ b/packages/core/sds/.eslintrc.js @@ -1,7 +1,7 @@ /** @type {import("eslint").Linter.Config} */ module.exports = { root: true, - extends: ['@sambad/eslint-config/react-internal.js'], + extends: ['@sambad/eslint-config/react-internal.js', 'plugin:storybook/recommended'], parser: '@typescript-eslint/parser', parserOptions: { tsconfigRootDir: __dirname, diff --git a/packages/core/sds/.storybook/main.ts b/packages/core/sds/.storybook/main.ts new file mode 100644 index 00000000..6f264b5c --- /dev/null +++ b/packages/core/sds/.storybook/main.ts @@ -0,0 +1,40 @@ +import type { StorybookConfig } from '@storybook/react-webpack5'; + +import { dirname, join } from 'path'; + +function getAbsolutePath(value: string): any { + return dirname(require.resolve(join(value, 'package.json'))); +} +const config: StorybookConfig = { + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + addons: [ + getAbsolutePath('@storybook/addon-webpack5-compiler-swc'), + getAbsolutePath('@storybook/addon-onboarding'), + getAbsolutePath('@storybook/addon-links'), + getAbsolutePath('@storybook/addon-essentials'), + getAbsolutePath('@chromatic-com/storybook'), + getAbsolutePath('@storybook/addon-interactions'), + ], + framework: { + name: getAbsolutePath('@storybook/react-webpack5'), + options: {}, + }, + docs: { + autodocs: 'tag', + }, + swc: () => ({ + jsc: { + parser: { + syntax: 'typescript', + tsx: true, + }, + transform: { + react: { + runtime: 'automatic', + importSource: '@emotion/react', + }, + }, + }, + }), +}; +export default config; diff --git a/packages/core/sds/.storybook/preview.ts b/packages/core/sds/.storybook/preview.ts new file mode 100644 index 00000000..adcda96b --- /dev/null +++ b/packages/core/sds/.storybook/preview.ts @@ -0,0 +1,14 @@ +import type { Preview } from '@storybook/react'; + +const preview: Preview = { + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + }, +}; + +export default preview; diff --git a/packages/core/sds/package.json b/packages/core/sds/package.json index 7dc224a0..e2250617 100644 --- a/packages/core/sds/package.json +++ b/packages/core/sds/package.json @@ -8,18 +8,33 @@ }, "scripts": { "lint": "eslint . --max-warnings 0", - "type-check": "tsc --noEmit" + "type-check": "tsc --noEmit", + "storybook": "storybook dev -p 6007", + "build-storybook": "storybook build" }, "devDependencies": { + "@chromatic-com/storybook": "^1.6.1", "@sambad/eslint-config": "workspace:*", "@sambad/typescript-config": "workspace:*", + "@storybook/addon-essentials": "^8.2.2", + "@storybook/addon-interactions": "^8.2.2", + "@storybook/addon-links": "^8.2.2", + "@storybook/addon-onboarding": "^8.2.2", + "@storybook/addon-webpack5-compiler-swc": "^1.0.4", + "@storybook/blocks": "^8.2.2", + "@storybook/react": "^8.2.2", + "@storybook/react-webpack5": "^8.2.2", + "@storybook/test": "^8.2.2", "@turbo/gen": "^1.12.4", - "@types/node": "^20.11.24", "@types/eslint": "^8.56.5", + "@types/node": "^20.11.24", "@types/react": "^18.2.61", "@types/react-dom": "^18.2.19", + "chromatic": "^11.5.5", "eslint": "^8.57.0", + "eslint-plugin-storybook": "^0.8.0", "react": "^18", + "storybook": "^8.2.2", "typescript": "^5.3.3" } } diff --git a/packages/core/sds/src/components/Button.tsx b/packages/core/sds/src/components/Button.tsx index be74dda9..59f795b4 100644 --- a/packages/core/sds/src/components/Button.tsx +++ b/packages/core/sds/src/components/Button.tsx @@ -1,10 +1,9 @@ 'use client'; import { css } from '@emotion/react'; +import { HTMLAttributes, ReactNode } from 'react'; -import type { HTMLAttributes, ReactNode } from 'react'; - -interface ButtonProps extends HTMLAttributes { +export interface ButtonProps extends HTMLAttributes { children: ReactNode; className?: string; appName: string; diff --git a/packages/core/sds/src/stories/Button.stories.tsx b/packages/core/sds/src/stories/Button.stories.tsx new file mode 100644 index 00000000..46fe730e --- /dev/null +++ b/packages/core/sds/src/stories/Button.stories.tsx @@ -0,0 +1,35 @@ +import { Button } from '../components/Button'; + +import type { Meta, StoryObj } from '@storybook/react'; + +const meta = { + title: 'Components/Button', + component: Button, + tags: ['autodocs'], + argTypes: { + children: { + control: 'text', + description: 'Button 컴포넌트가 표시할 내용을 정의합니다.', + }, + appName: { + control: 'text', + description: 'Button 컴포넌트를 click시 표시할 alert 내용을 정의합니다.', + }, + className: { + control: 'text', + description: 'Button 컴포넌트의 className을 정의합니다.', + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + children: 'Button', + appName: 'undefined', + className: 'undefined', + }, + render: (args) =>