Storybook을 사용하여 React 구성 요소 라이브러리 구축, 테스트 및 게시

작업 중에 내부에서 구성 요소를 다시 사용해야 하거나 다음Material UI를 구축하려면 구성 요소 라이브러리를 구축해야 합니다.다행히도 Storybook 같은 도구는 React 구성 요소를 단독으로 설정하고 개발하며 보기 쉽다.비록 이것은 당신의 업무 목록에 많은 수작업을 증가시킬 수 있지만, 설정에 있어서 여전히 상당한 비용이 든다.
최근에 이 설정을 한 후에, 나는 너의 번거로움을 줄이고, 너에게 가능한 설정을 보여 주고 싶다.경고: 이것은 매우 자기 의견을 고집할 것이다. 나는 모든 결정이나 코드를 설명하지 않을 것이다.그것을 더욱 모범으로 삼으면 너는 채택하고 보완할 수 있다.
한 걸음 한 걸음 설정을 건너뛰고 싶으면 https://github.com/DennisKo/component-library-template으로 가서 완성된 코드를 얻을 수 있습니다.
사용할 주요 도구 및 라이브러리:
  • React
  • Storybook
  • Typescript
  • Jest
  • Testing-library/react
  • Rollup
  • Changesets
  • 처음부터


    git 저장소와 새 NPM 패키지를 초기화합니다.우리는 전체 설치 과정에서 실을 사용할 것이다. 물론 npm를 사용해도 모든 것을 실현할 수 있다.
    mkdir my-component-library  
    dev cd my-component-library
    git init
    yarn init -y
    
    이름 필드를 원하는 내용으로 변경하려면 package.json을 엽니다.저는 @dennisko/my-component-library을 선택했습니다..gitignore을 생성합니다.
    node_modules
    lib
    .eslintcache
    storybook-static
    
    reactreact-dom 추가:
    yarn add -D react react-dom
    
    - D의 용도는 React를 라이브러리와 묶고 싶지 않다는 것입니다. 개발에서 사용하고 대등한 의존항으로 사용하기만 하면 됩니다.따라서 package.json에 추가합니다.
    "peerDependencies": {
        "react": ">=17.0.1",
        "react-dom": ">=17.0.1"
     }
    
    Typescript를 설치하고 tsconfig.json을 추가합니다.yarn add -D typescripttsconfig.json
    {
      "compilerOptions": {
        "baseUrl": "./",
        "target": "es5",
        "lib": ["dom", "dom.iterable", "esnext"],
        "allowJs": true,
        "skipLibCheck": true,
        "esModuleInterop": true,
        "allowSyntheticDefaultImports": true,
        "strict": true,
        "forceConsistentCasingInFileNames": true,
        "noFallthroughCasesInSwitch": true,
        "module": "esnext",
        "moduleResolution": "node",
        "resolveJsonModule": true,
        "isolatedModules": true,
        "jsx": "react-jsx",
        "declaration": true,
        "outDir": "./lib"
      },
      "include": ["src/**/*"],
      "exclude": ["node_modules", "lib"]
    }
    
    현재 우리는 npx sb init을 실행할 수 있으며, 기본 이야기책 설정을 설치하고 추가할 수 있습니다.이것은 우리가 필요로 하지 않는 프레젠테이션 이야기도 만들었습니다. ./stories 폴더를 삭제하는 것을 권장합니다.다음과 같은 여러 구조를 사용합니다.
    .
    └── src/
        └── components/
            └── Button/
                ├── Button.tsx
                ├── Button.stories.tsx
                └── Button.test.tsx
    
    나는 구성 요소와 관련된 모든 것을 한 곳에 두는 것을 더 좋아한다. 테스트, 이야기 등이다.
    우리의 새로운 구조에 대한 이야기책을 이야기하기 위해서 우리는 반드시 .storybook/main.js에서 작은 변화를 해야 한다.
    "stories": [
        "../src/**/*.stories.mdx",
        "../src/**/*.stories.@(js|jsx|ts|tsx)"
      ]
    
    여기서도 ./storybook/preview.js을 편집하고 기본적으로 이야기책 DocsPage페이지를 표시합니다.
    이야기책js
    export const parameters = {
      actions: { argTypesRegex: '^on[A-Z].*' },
      viewMode: 'docs',
    };
    

    우리 첫 파트.


    이제 첫 번째 구성 요소를 인코딩하고 추가할 수 있습니다.
    src/구성 요소/단추.tsx
    import * as React from 'react';
    
    export interface ButtonProps {
      children: React.ReactNode;
      primary?: boolean;
      onClick?: () => void;
      backgroundColor?: string;
      color?: string;
    }
    
    export const Button = ({
      children,
      primary = false,
      onClick,
      backgroundColor = '#D1D5DB',
      color = '#1F2937',
    }: ButtonProps): JSX.Element => {
      const buttonStyles = {
        fontWeight: 700,
        padding: '10px 20px',
        border: 0,
        cursor: 'pointer',
        display: 'inline-block',
        lineHeight: 1,
        backgroundColor: primary ? '#2563EB' : backgroundColor,
        color: primary ? '#F3F4F6' : color,
      };
      return (
        <button type="button" onClick={onClick} style={buttonStyles}>
          {children}
        </button>
      );
    };
    
    
    이것은 아름다운 것이 아니다. 그것은 하드코딩된 색깔이다. 그것은 이미 마차일 수도 있지만, 우리의 시범 목적을 충족시킬 수 있을 것이다.
    버튼 구성 요소 가져오기/내보내기를 위한 index.ts 파일 두 개를 추가합니다.
    src/구성 요소/단추/인덱스.ts
    export { Button } from './Button';
    
    src/index.ts
    export { Button } from './components/Button';
    
    당신의 프로젝트는 지금 이렇게 해야 합니다.

    우리 첫 번째 사연.


    현재 yarn storybook을 실행하면 실제로 구축되지만, http://localhost:6006/을 켜면 심심한 화면이 표시됩니다.

    이것은 우리가 아직 단추 구성 요소에 어떤 이야기도 추가하지 않았기 때문이다.이야기는 구성 요소의 상태를 묘사한 다음에 그것과 단독으로 상호작용을 하도록 한다.
    사연 좀 추가합시다!
    src/구성 요소/단추/단추.고사 1tsx
    import * as React from 'react';
    import { Story, Meta } from '@storybook/react/types-6-0';
    import { Button, ButtonProps } from './Button';
    
    export default {
      title: 'Button',
      component: Button,
      description: `A button.`,
      argTypes: {
        backgroundColor: { control: 'color' },
        color: { control: 'color' },
        primary: { control: 'boolean' },
      },
    } as Meta;
    
    //👇 We create a “template” of how args map to rendering
    const Template: Story<ButtonProps> = (args) => <Button {...args}>Click me</Button>;
    
    //👇 Each story then reuses that template
    export const Default = Template.bind({});
    Default.args = {};
    
    export const Primary = Template.bind({});
    Primary.args = {
      primary: true,
    };
    
    export const CustomBackground = Template.bind({});
    CustomBackground.args = {
      backgroundColor: '#A78BFA',
    };
    
    export const CustomFontColor = Template.bind({});
    CustomFontColor.args = {
      color: '#1E40AF',
    };
    
    export const OnClick = Template.bind({});
    OnClick.args = {
      // eslint-disable-next-line no-alert
      onClick: () => alert('Clicked the button!'),
    };
    
    
    여기의 구조와 문법은 익숙해지는 데 시간이 좀 걸리지만, 보통 기본값은 *로 내보냅니다.이야기 파일은 우리의 이야기에 매개 변수 (React land의 도구) 와 설명 등원 정보를 추가하는 데 사용됩니다.모든 이름의 내보내기 (예: export const Primary) 는 하나의 이야기를 만들 것입니다.yarn storybook을 다시 실행하면 우리의 버튼과 휘황찬란한 이야기를 볼 수 있습니다!

    UI를 사용하여 버튼 스토리를 편집하고 매개 변수를 변경합니다(도구!)무슨 일이 일어날지 보자.

    테스트


    Storybook은 구성 요소를 수동으로 테스트하고 검토하는 데 매우 적합하지만, 우리는 여전히 자동 테스트를 진행할 수 있기를 희망합니다.Jest and React 테스트 라이브러리로 이동합니다.
    설치 테스트에 필요한 종속성:yarn add -D jest ts-jest @types/jest identity-obj-proxy @testing-library/react @testing-library/jest-domjest.config.jsjest-setup.ts을 생성합니다.
    농담입니다.배치하다.js
    module.exports = {
      preset: 'ts-jest',
      testEnvironment: 'jsdom',
      moduleNameMapper: {
        '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
          '<rootDir>/__mocks__/fileMock.js',
        '\\.(css|less|scss)$': 'identity-obj-proxy',
      },
      setupFilesAfterEnv: ['<rootDir>/jest-setup.ts'],
    };
    
    JSdom은 react-testing에 필요한 환경입니다. 이 설정에는 필요하지 않지만 module Name Mapper는 Jest를 이미지와 스타일과 함께 작동합니다.css 모듈을 사용할 계획이라면 identity-obj-proxy이 특히 유용합니다.
    농담입니다.ts
    import '@testing-library/jest-dom';
    
    __mocks__;/fileMocks.js
    module.exports = 'test-file-stub';
    
    테스트를 실행하기 위해 package.json에 두 개의 스크립트를 추가했습니다.
        "test": "jest",
        "test:watch": "jest --watch"
    
    현재 우리는 우리의 단추를 위해 테스트를 작성할 준비가 되어 있다.
    src/구성 요소/단추/단추.테스트tsx
    import * as React from 'react';
    import { render, fireEvent, screen } from '@testing-library/react';
    import { Button } from './Button';
    
    describe('Button', () => {
      test('renders a default button with text', async () => {
        render(<Button>Click me</Button>);
    
        expect(screen.getByText('Click me')).toBeInTheDocument();
        expect(screen.getByText('Click me')).toHaveStyle({
          backgroundColor: '#D1D5DB',
          color: '#1F2937',
        });
      });
      test('renders a primary button', async () => {
        render(<Button primary>Click me</Button>);
    
        expect(screen.getByText('Click me')).toHaveStyle({
          backgroundColor: '#2563EB',
          color: '#F3F4F6',
        });
      });
      test('renders a button with custom colors', async () => {
        render(
          <Button color="#1E40AF" backgroundColor="#A78BFA">
            Click me
          </Button>
        );
    
        expect(screen.getByText('Click me')).toHaveStyle({
          backgroundColor: '#A78BFA',
          color: '#1E40AF',
        });
      });
      test('handles onClick', async () => {
        const mockOnClick = jest.fn();
        render(<Button onClick={mockOnClick}>Click me</Button>);
        fireEvent.click(screen.getByText('Click me'));
    
        expect(mockOnClick).toHaveBeenCalledTimes(1);
      });
    });
    
    그리고 yarn test 또는 yarn test:watch을 사용하여 감시 모드에서 테스트를 실행합니다.

    포장 생산


    지금까지 우리는 매우 좋은 개발 설정을 가지고 있다.Storybook(Webpack을 배경으로 함)은 모든 번들 작업을 수행합니다.
    우리의 코드를 세계에 발표하기 위해서, 우리는 생산에 사용할 수 있는 패키지를 만들어야 한다.우리의 코드는 최적화, 코드 분해와 전송을 거쳤다.우리는 총결산을 사용할 것이다.또한 Webpack으로 실현할 수 있지만, 나는 여전히 '응용 프로그램의 Webpack, 라이브러리의 총결' 규칙을 따른다.웹 패키지 설정보다 통합 설정이 더 읽을 만하다고 생각합니다. 잠시 후에 보셨듯이.yarn add -D rollup rollup-plugin-typescript2 rollup-plugin-peer-deps-external rollup-plugin-cleaner @rollup/plugin-commonjs @rollup/plugin-node-resolve말아요.배치하다.js
    import typescript from 'rollup-plugin-typescript2';
    import peerDepsExternal from 'rollup-plugin-peer-deps-external';
    import cleaner from 'rollup-plugin-cleaner';
    import commonjs from '@rollup/plugin-commonjs';
    import resolve from '@rollup/plugin-node-resolve';
    import packageJson from './package.json';
    
    export default {
      input: 'src/index.ts',
      output: [
        {
          file: packageJson.main,
          format: 'cjs',
          sourcemap: true,
        },
        {
          file: packageJson.module,
          format: 'esm',
          sourcemap: true,
        },
      ],
      plugins: [
        cleaner({
          targets: ['./lib'],
        }),
        peerDepsExternal(),
        resolve(),
        commonjs(),
        typescript({
          exclude: ['**/*.stories.tsx', '**/*.test.tsx'],
        }),
      ],
    };
    
    package.json에서 출력 경로를 가져오므로 이 필드를 작성하고 구축 스크립트를 추가해야 합니다.
      "main": "lib/index.js",
      "module": "lib/index.esm.js",
      "scripts": {
         ...
         "build": "rollup -c"
       }
    

    NPM에 게시


    버전을 관리하고 NPM에 게시하기 위해 changesets이라는 라이브러리를 사용합니다.이 패키지는 자동 패치/부차적/주요 버전(SemVer)을 처리하고 NPM에 반자동 게시할 수 있도록 도와줍니다.yarn add --dev @changesets/cliyarn changeset init라이브러리를 공개하기 위해 .changeset/config.json에서 만든 변경 세트 구성을 access에서 public으로, baseBranch에서 main으로 변경할 수 있습니다.도서관에 비밀을 지키고 싶으면 access으로 전화하세요.
    현재 라이브러리에서 변경할 때마다 제출 또는 PR에 restricted을 입력하고 cli를 통해 변경 유형을 선택합니다(패치/부차적/주요적?)변경된 설명을 추가합니다.이에 따라 yarn changeset에서는 changesets의 업그레이드 방법을 결정합니다.따라서 package.json 스크립트를 추가하고 release 옵션 filespackage.json 출력 디렉토리로 가리킵니다.
    소포.json
    "files": [
        "lib"
      ],
     "scripts": {
        ...
        "release": "yarn build && changeset publish"
      }
    
    당신은 우리가 현재 lib을 실행하여 수동으로 발표한다고 생각할 수 있지만, yarn release은 심지어 더 나아가 모든 것을 자동화하는 Github 조작을 제공했다.
    생성 changesets:
    name: Release
    
    on:
      push:
        branches:
          - main
    
    jobs:
      release:
        name: Release
        runs-on: ubuntu-latest
        steps:
          - name: Checkout Repo
            uses: actions/checkout@master
            with:
              # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
              fetch-depth: 0
    
          - name: Setup Node.js 12.x
            uses: actions/setup-node@master
            with:
              node-version: 12.x
    
          - name: Install Dependencies
            run: yarn
    
          - name: Create Release Pull Request or Publish to npm
            id: changesets
            uses: changesets/action@master
            with:
              # This expects you to have a script called release which does a build for your packages and calls changeset publish
              publish: yarn release
            env:
              GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
              NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
    
    이를 위해서는 https://www.npmjs.com/settings/NPM_USER_NAME/tokens에 NPM 액세스 토큰을 만들어야 합니다.'자동화' 옵션을 선택하여 생성된 영패를 복제하고github 메모리 라이브러리에 추가합니다. (설정 -> 기밀 아래) 즉 .github/workflows/release.yml입니다.
    이러한 변경 사항을 제출하고 Github로 밀어넣으면 작업 흐름이 실행되고 초기 버전이 NPM에 게시됩니다.이것은github에 발표와 표시를 만들 것입니다.
    현재, 만약 우리가 라이브러리에서 작은 변경 사항을 했다면, 예를 들어 변경 단추에 대한 설명이다.우리는 코드를 수정하고 NPM_TOKEN을 실행합니다.

    변경 내용을 주 분기로 밀어넣으면 게시 워크플로우가 다시 트리거되지만 이번에는 NPM에 자동으로 게시되지 않고 올바른 라이브러리 버전의 PR을 작성합니다.PR은 주 브랜치를 변경하면서 업데이트될 수도 있습니다.

    준비가 되고 변경 사항이 만족스러우면 PR을 병합하여 적절한 버전을 사용하여 NPM에 다시 게시할 수 있습니다.
    이렇게우리는 React 구성 요소 라이브러리를 구축하고 테스트하며 발표했다.
    읽어주셔서 감사합니다!나는 즐겁게 질문에 대답하고 가능한 오류와 개선을 토론했다.
    트위터에서도 지켜봐 주세요.

    좋은 웹페이지 즐겨찾기