“This is the second day of my participation in the First Challenge 2022, for more details: First Challenge 2022”.
preface
I used storybook to try to build a set of component library “Storybook from Zero to One Building component Library (Part 1)”, but after a whole operation, I found that the learning cost of Storybook is relatively high and the configuration is complex, and the most important thing is that the page is ugly. Then I started to use Dumi, and found that it was really fragrant. It was not only easy to get started, just like normal development of an ordinary project, but also did not have complicated configuration. The most important thing was that the page was beautiful, simple and generous. A component library like ANTD that you deserve.
Learn how to build a React+TS component library, write a complete component, deploy it to the Github&Gitee static Web site, and publish the NPM public package.
Technology used: Dumi: responsible for component development and component document generation (based on UMI, students who have used UMI are friendly and easy to use) Github: configure automatic deployment static Web Gitee: synchronize Github GH Pages
Features:
- 📦 Out of the box, focus on component development and documentation
- 📋 rich Markdown extension, beyond rendering component demo
- 🏷 automatically generates component apis based on TypeScript type definitions
- The 🎨 theme is easy to customize and you can also create your own Markdown components
- 📱 support mobile component library development, built-in mobile hd rendering scheme
- 📡 one line of command datafies component assets and concatenates them with downstream productivity tools
Environment to prepare
node
: v10.13.0 or later versions
The installation
Build a library of site-pattern components
# $yarn create @umijs/dumi-lib --site # $yarn create @umijs/dumi-lib --siteCopy the code
The project directory structure is roughly as follows:Install dependencies, start up, and you’ll see a front page similar to the official website and a concise component page.Home page:
The home page corresponds to root/docs/index.md in the project
Component page:
The component page corresponds to root/ SRC /index.ts in the project
configuration
Mainly navigation and menu configuration:.umirc.ts
import { defineConfig } from 'dumi'; function getMenus(opts: { lang? : string; Base: '/ components' |'/docs' {}) const menus = {'/docs: [{title: 'Introduce', 'title_zh - CN' : 'introduction, the path: '/ docs/guide,}, {title:' FAQ ', 'title_zh - CN' : 'the problem', path: '/ docs/FAQ'},], '/ components: [{title: 'Common', 'title_zh-cn ': 'Common', children: ['/components/button', '/components/icon', '/components/typography'], }, { title: 'Layout', 'title_zh-CN': 'Layout ', children: [ '/components/layout/Divider', '/components/layout/Grid', '/components/layout/Layout', '/components/space', ], }, ], }; return (menus[opts.base] as [])? .map((menu: any) => { if (! opts.lang) return menu; return { ... menu, title: menu[`title_${opts.lang}`] || menu.title, }; }); } export default defineConfig({ title: 'fish-ui', hash: true, base: '/fish-ui', publicPath: '/fish-ui/', favicon: 'https://img.alicdn.com/tfs/TB1YHEpwUT1gK0jSZFhXXaAtVXa-28-27.svg', logo: 'https://img.alicdn.com/tfs/TB1YHEpwUT1gK0jSZFhXXaAtVXa-28-27.svg', outputPath: 'docs-dist', mode: 'site', mfsu: {}, dynamicImport: {}, navs: [/ / null, {title: 'document' path: '/ docs'}, {title:' components' path: '/components', }, { title: 'GitHub', path: 'https://github.com/yingliyu/fish-ui', }, ], menus: { '/zh-CN/docs': getMenus({ lang: 'zh-CN', base: '/docs' }), '/docs': getMenus({ base: '/docs' }), '/zh-CN/components': getMenus({ lang: 'zh-CN', base: '/components' }), '/components': getMenus({ base: '/components' }), }, // more config: https://d.umijs.org/config lessLoader: {javascriptEnabled: true}, / / on-demand loaded antd extraBabelPlugins: [ [ 'babel-plugin-import', { libraryName: 'antd', libraryDirectory: 'es', style: true, }, ], ], });Copy the code
Menus and NAVs in this configuration correspond to the following page layout:
Completing a component
Based on the Ant Design Button component, for example: / components/Button/index. The TSX components (write)
import React from 'react'; import { Button as AntdButton } from 'antd'; import classNames from 'classnames'; import './index.less'; declare type ButtonHTMLType = 'submit' | 'button' | 'reset'; declare const ButtonTypes: ['default', 'primary', 'ghost', 'dashed', 'link', 'text']; export declare type ButtonType = typeof ButtonTypes[number]; interface IABSButtonProps { loading? : boolean; danger? : boolean; className? : string; type? : ButtonType; style? : React.CSSProperties; icon? : React.ReactNode; children? : React.ReactNode; disabled? : boolean; block? : boolean; large? : boolean; htmlType? : ButtonHTMLType; color? : 'blue' | 'red' | 'yellow' | 'green' | 'white'; onClick? : React.MouseEventHandler<HTMLButtonElement>; } const Button: React.FC<IABSButtonProps> = (props: IABSButtonProps) => { const { danger, loading, style, children, large = false, className, onClick, type = 'primary', icon, block = false, disabled, htmlType, color, } = props; let classes = classNames('fish-btn', className, { 'fish-btn-large': large, 'fish-btn-block': block, 'fish-btn-link': type === 'link', }); const displayStyle = block ? 'block' : 'inline-block'; let newTpye = type; let isDanger = danger; if (color) { switch (color) { case 'blue': newTpye = 'primary'; break; case 'red': isDanger = true; break; case 'white': newTpye = 'default'; break; case 'yellow': classes = classNames(classes, 'fish-yellow-btn'); break; case 'green': classes = classNames(classes, 'fish-green-btn'); break; default: break; } } return ( <div className={classes} style={{ display: displayStyle, ... style }}> <AntdButton icon={icon} onClick={onClick} disabled={disabled} type={newTpye} block={block} htmlType={htmlType} danger={isDanger} loading={loading} > {children} </AntdButton> </div> ); }; export default Button;Copy the code
/ components/button/index. Less (component style)
.color(@bg, @bgHover, @bgActive) when (default()) { .ant-btn { color: @text-color-inverse; background: @bg; border-color: @bg; &:hover, &:focus { color: @text-color-inverse; background: @bgHover; border-color: @bgHover; } &:active { color: @text-color-inverse; background: @bgActive; border-color: @bgActive; } } } .fish-btn { &.fish-btn-block { width: 200px; } &.fish-btn-link { .ant-btn-link { padding: 0; } } &.fish-yellow-btn { .color(@warning-color, @gold-5, @gold-7); } &.fish-green-btn { .color(@success-color, @green-5, @green-7); }}Copy the code
/ components/button/index. The md (component documentation)
-- Title: Button group: PATH: / Components Order: 1 -- ## Button responds to user click behavior and triggers corresponding business logic. TSX import React from 'React '; import { Button, Space } from 'fish-ui'; export default () => ( <Space> <Button>Button</Button> <Button danger>Button</Button> <Button large>Button</Button> </Space> );Copy the code
The button color
import React from 'react';
import { Button, Space } from 'fish-ui';
export default() = > (<Space>
<Button color="white">Button</Button>
<Button color="blue">Button</Button>
<Button color="red">Button</Button>
<Button color="yellow">Button</Button>
<Button color="green">Button</Button>
<Button>Button</Button>
</Space>
);
Copy the code
API
Page display:
So far, the antD-based Button component and documentation have been written in embryonic form, but a very important part of the API of the document has not been added. Using MD syntax to write API is tedious and violates the original intention of focusing on component development. Wouldn’t it be nice if our TS API could be generated automatically based on type declarations and code comments, and yes it can, via JS Doc annotations + TypeScript type definitions.
Automatic generation API
Prerequisite: Make sure Dumi is able to derive API content from TypeScript type definitions + annotations. The type resolution tool behind Dumi is react-Docgen-typescript. See its documentation for more types and annotations
The installation
npm install --save-dev react-docgen-typescript
Copy the code
configuration
The project root directory creates the configuration file styleguide.config.js
module.exports = {
propsParser: require('react-docgen-typescript').withDefaultConfig([parserOptions]).parse,
};
Copy the code
Modifying component code
The components/button/index. The TSX add comments as follows:
Interface IABSButtonProps {/** Set button loading state */ loading? : boolean; /** * set danger button * @default false */ danger? : boolean; className? : string; /** Button type */ type? : ButtonType; style? : React.CSSProperties; /** Set button icon component */ icon? : React.ReactNode; children? : React.ReactNode; /** * Button invalid status * @default false */ disabled? : boolean; block? : boolean; large? : boolean; htmlType? : ButtonHTMLType; /** button color */ color? : 'blue' | 'red' | 'yellow' | 'green' | 'white'; onClick? : React.MouseEventHandler<HTMLButtonElement>; }Copy the code
Components/button/index. The md in the location you want to display the API page reference API
<API></API>
Copy the code
Results show
Based on the above code, the following API table is automatically generated:
Automated deployment
The component library is automatically deployed on Github GH-Pages. Since Github is slow to access, a copy of the component library is also deployed on Gitee GH-Pages.
For example, to access my component libraries: yingliyu.github. IO /fish-ui, ylyubook.giitee. IO /fish-ui need to be modified if not deployed in the root directory.
// .umirc.ts
export default defineConfig({
base: '/fish-ui',
publicPath: '/fish-ui/'
...
})
Copy the code
Github /workflows/gh-pages. Yml was created in the project root directory
Name: Deploy Github Pages # Actions name on: # Trigger condition push: Branches: -master # Trigger jobs: build: # job id name: Build and publish # job id run-on: Ubuntu - Latest # runtime environment Optional ubuntu-latest, ubuntu-18.04, ubuntu-16.04, Windows-latest, Windows-2019, Windows-2016, MacOS-latest, Macos-10.14 Steps: - uses: actions/checkout@v2 - name: Use node.js 14.x uses: actions/setup-node@v2 with: node-version: 14.x - name: Setup env run: | npm install - name: Generate public files run: | NPM run docs: build # release to making pages - name: Auto Deploy env: GH_REF: ACCESS_TOKEN: ${{secrets.ACCESS_TOKEN}} # github token GITEE_REF: Git # gitee usename = GITEE_TOKEN = GITEE_TOKEN = GITEE_TOKEN = gitee usename = GITEE_TOKEN ${{secrets.GITEE_TOKEN}} # gitee private token run: | git config --global user.name "your name" git config --global user.email "your email" git clone https://${GH_REF} .deploy_git cd .deploy_git git checkout gh-pages cd .. / mv.deploy_git /.git/./docs-dist # docs-dist CD./docs-dist git add. Git commit -m ":construction_worker:CI built at `date +"%Y-%m-%d %H:%M:%S"`" # GitHub Pages git push --force --quiet "https://${ACCESS_TOKEN}@${GH_REF}" gh-pages:gh-pages # Gitee Pages git push --force --quiet "https://[gitee usename]:${GITEE_TOKEN}@${GITEE_REF}" gh-pages:gh-pagesCopy the code
The above GITEE_TOKEN is a private token created in Gitee. Here we obtain it from Gitee. The specific address is gitee.com/profile/per… . Generate and copy tokens and add them to the corresponding Github repository. ACCESS_TOKEN is Github Secrets.
Release NPM package
Note: NPM package is completely released public, that is all the people who use NPM can be downloaded in NPM warehouse you release the package, but the actual projects, the department between utility package may involve the business secret, so can’t released on the NPM, the company needs to set up their own private package management, warehouse, then can use CNPM.
This paper only records the steps of NPM package release and the construction of CNPM private warehouse. Please click here for reference. Before publishing, you need to configure package.json to add the necessary description information.
//package.json
"private": false."name": "fish-ui-pro"."version": "1.0.0"."description": "A library of react components"."author": "yingliyu"."license": "MIT"."keywords": [
"React"."Component"]."homepage": "https://yingliyu.github.io/fish-ui"."repository": {
"type": "git"."url": "https://github.com/yingliyu/fish-ui.git"
},
"files": [
"docs-dist"."es"].Copy the code
- will
private
Set the field to false to indicate a non-private package. - add
description
、author
,license
、keywords
And other related fields; - add
homepage
Field, the project home page URL; - add
repository
Field, that is, project warehouse address URL; - Add a files field that indicates which files to upload to NPM. If you write nothing, the default is
.gitignore
The information inside. But be careful, no matter.gitignore
How to configure that some files will always be published topackage
These documents includepackage.json
,README.md
,LICENSE
And so on;
/ / package. Json "peerDependencies" : {" react ":" > = 16.9.0 ", "the react - dom" : "> = 16.9.0", "antd" : "> = 4.18.0"},Copy the code
Run NPM login, enter username, password, and email for NPM registration, and run NPM whoami. If the username is displayed, the login is successful. Finally, run NPM publish and send packets. Note: Build before publish to make sure the DIST package is up to date.
Error: 403: NPM package name already used
The last
This is the end of the task, congratulations on your own component library and NPM package!!
Reference: Dumi official website, juejin.cn/post/684490…
Copyright belongs to author Fan Xue, all rights reserved.