├── .babelrc ├── .browserslistrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.cjs ├── .github └── workflows │ ├── ci.yaml │ ├── deploy.yaml │ └── release.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .storybook ├── main.ts ├── preview-body.html └── preview.ts ├── .stylelintignore ├── .stylelintrc.cjs ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── __tests__ ├── __snapshots__ │ └── dom.test.tsx.snap ├── dom.test.tsx ├── js.spec.tsx └── mockfn.spec.ts ├── app.d.ts ├── commitlint.config.cjs ├── docs ├── .gitignore ├── README.md ├── babel.config.js ├── docs │ ├── get-started │ │ ├── _category_.json │ │ ├── installation.md │ │ └── markdown-features.mdx │ └── intro.md ├── docusaurus.config.js ├── package.json ├── sidebars.js ├── src │ ├── components │ │ └── HomepageFeatures │ │ │ ├── index.tsx │ │ │ └── styles.module.css │ ├── css │ │ └── custom.css │ └── pages │ │ ├── index.module.css │ │ ├── index.tsx │ │ └── markdown-page.md ├── static │ ├── .nojekyll │ └── img │ │ ├── docusaurus.png │ │ ├── favicon.ico │ │ ├── logo.svg │ │ ├── undraw_docusaurus_mountain.svg │ │ ├── undraw_docusaurus_react.svg │ │ └── undraw_docusaurus_tree.svg └── tsconfig.json ├── package.json ├── playground ├── index.html ├── package.json ├── public │ └── favicon.ico ├── src │ ├── main.tsx │ ├── pages │ │ └── home │ │ │ └── Home.tsx │ └── routers.tsx ├── tsconfig.json ├── vite-env.d.ts └── vite.config.ts ├── postcss.config.cjs ├── setupTests.ts ├── src ├── components │ ├── Demo.tsx │ └── index.ts ├── index.ts ├── presets │ └── index.ts ├── stories │ ├── Button.stories.ts │ ├── Button.tsx │ ├── Configure.mdx │ ├── Header.stories.ts │ ├── Header.tsx │ ├── Page.stories.ts │ ├── Page.tsx │ ├── Pkg.stories.tsx │ ├── assets │ │ ├── accessibility.png │ │ ├── accessibility.svg │ │ ├── addon-library.png │ │ ├── assets.png │ │ ├── context.png │ │ ├── discord.svg │ │ ├── docs.png │ │ ├── figma-plugin.png │ │ ├── github.svg │ │ ├── share.png │ │ ├── styling.png │ │ ├── testing.png │ │ ├── theming.png │ │ ├── tutorials.svg │ │ └── youtube.svg │ ├── button.less │ ├── header.scss │ └── page.css ├── styles │ └── main.scss └── types.ts ├── tsconfig.json ├── tsup.config.ts └── vite.config.ts /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "usage", 7 | "modules": false, // preserve ES modules. 8 | "corejs": { "version": 3, "proposals": true } 9 | } 10 | ], 11 | "@babel/preset-typescript" 12 | ], 13 | "plugins": [ 14 | "@babel/plugin-transform-runtime", 15 | // https://babel.dev/docs/en/babel-plugin-transform-react-jsx 16 | [ 17 | "@babel/plugin-transform-react-jsx", 18 | { 19 | // "runtime": "automatic" 20 | } 21 | ] 22 | ], 23 | "exclude": ["core-js"] 24 | } 25 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | >1% 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/* 2 | build/* 3 | node_modules/* 4 | src/assets 5 | src/**/*.css 6 | src/stories 7 | .eslintrc.cjs 8 | .stylelintrc.cjs 9 | babel.config.cjs 10 | setupTests.ts 11 | vite.config.ts 12 | postcss.config.cjs 13 | commitlint.config.cjs 14 | tsconfig.json 15 | **/*.css 16 | **/*.scss 17 | **/*.less 18 | **/*.module.css 19 | **/*.module.scss 20 | **/*.module.less 21 | __unconfig_vite.config.ts 22 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * https://www.npmjs.com/package/eslint-config-airbnb-typescript 3 | */ 4 | module.exports = { 5 | extends: [ 6 | 'airbnb-base', 7 | 'airbnb-typescript', 8 | 'plugin:react-hooks/recommended', 9 | 'plugin:@typescript-eslint/recommended', 10 | 'plugin:@typescript-eslint/recommended-requiring-type-checking', 11 | 'plugin:jsx-a11y/strict', 12 | 'plugin:react/recommended', 13 | 'plugin:react/jsx-runtime', 14 | 'plugin:storybook/recommended', 15 | ], 16 | env: { 17 | es6: true, 18 | browser: true, 19 | }, 20 | plugins: [ 21 | '@typescript-eslint', 22 | ], 23 | parserOptions: { 24 | project: './tsconfig.json', 25 | ecmaFeatures: { 26 | jsx: true, 27 | tsx: true, 28 | }, 29 | ecmaVersion: 2020, 30 | useJSXTextNode: true, 31 | sourceType: 'module', 32 | }, 33 | settings: { 34 | react: { 35 | createClass: 'createReactClass', 36 | pragma: 'React', 37 | fragment: 'Fragment', 38 | version: 'detect', 39 | flowVersion: '0.53', 40 | }, 41 | }, 42 | rules: { 43 | // react-hooks -> https://reactjs.org/docs/hooks-rules.html 44 | 'react-hooks/rules-of-hooks': 'error', 45 | 'react-hooks/exhaustive-deps': 'off', 46 | // 代码风格 47 | 'import/prefer-default-export': 'off', 48 | 'default-param-last': 'off', 49 | 'no-else-return': 'off', 50 | 'import/extensions': 'off', 51 | 'import/order': 'off', 52 | 'import/no-cycle': 'off', 53 | 'import/no-extraneous-dependencies': 'off', 54 | 'import/no-named-as-default-member': 'off', 55 | 'linebreak-style': 'off', 56 | 'no-console': 'off', 57 | 'class-methods-use-this': 'off', 58 | 'max-classes-per-file': 'off', 59 | 'consistent-return': 'off', 60 | 'default-case': 'off', 61 | 'global-require': 'off', 62 | 'import/no-dynamic-require': 'off', 63 | 'generator-star-spacing': 'off', 64 | 'max-len': ['error', { 65 | 'code': 130 66 | }], 67 | // typescript 68 | '@typescript-eslint/no-this-alias': 'off', 69 | '@typescript-eslint/no-unsafe-argument': 'off', 70 | '@typescript-eslint/no-var-requires': 'off', 71 | '@typescript-eslint/no-inferrable-types': 'off', 72 | '@typescript-eslint/naming-convention': 'off', 73 | '@typescript-eslint/no-unnecessary-type-assertion': 'off', 74 | '@typescript-eslint/no-unused-vars': 'off', 75 | '@typescript-eslint/no-useless-constructor': 'off', 76 | '@typescript-eslint/no-use-before-define': 'off', 77 | '@typescript-eslint/no-unsafe-assignment': 'off', 78 | '@typescript-eslint/no-unsafe-member-access': 'off', 79 | '@typescript-eslint/no-unsafe-call': 'off', 80 | '@typescript-eslint/no-unsafe-return': 'off', 81 | '@typescript-eslint/restrict-template-expressions': 'off', 82 | '@typescript-eslint/await-thenable': 'off', 83 | '@typescript-eslint/unbound-method': 'off', 84 | '@typescript-eslint/restrict-plus-operands': 'off', 85 | '@typescript-eslint/no-floating-promises': 'off', 86 | '@typescript-eslint/explicit-module-boundary-types': 'off', 87 | '@typescript-eslint/no-explicit-any': 'off', 88 | '@typescript-eslint/ban-types': 'off', 89 | '@typescript-eslint/ban-ts-comment': 'off', 90 | '@typescript-eslint/no-namespace': 'off', 91 | '@typescript-eslint/no-misused-promises': 'off', 92 | '@typescript-eslint/prefer-regexp-exec': 'off', 93 | // javascript 94 | 'prefer-promise-reject-errors': 'off', 95 | 'func-names': 'off', 96 | 'no-bitwise': 'off', 97 | 'no-plusplus': 'off', 98 | 'prefer-template': 'off', 99 | 'object-curly-newline': 'off', 100 | 'no-param-reassign': 'off', 101 | 'no-restricted-globals': 'off', 102 | 'no-underscore-dangle': 'off', 103 | 'no-restricted-syntax': 'off', 104 | 'no-useless-escape': 'off', 105 | 'no-confusing-arrow': 'off', 106 | 'no-new': 'off', 107 | 'no-void': 'off', 108 | 'prefer-rest-params': 'off', 109 | 'no-async-promise-executor': 'off', 110 | 'no-case-declarations': 'off', 111 | // jsx 112 | 'jsx-a11y/aria-role': 'off', 113 | 'jsx-a11y/anchor-is-valid': 'off', 114 | 'jsx-a11y/label-has-associated-control': 'off', 115 | 'jsx-a11y/click-events-have-key-events': 'off', 116 | 'jsx-a11y/no-static-element-interactions': 'off', 117 | 'jsx-a11y/no-noninteractive-element-interactions': 'off', 118 | 'jsx-a11y/alt-text': 'off', 119 | // react 120 | 'jsx-quotes': [2, 'prefer-double'], 121 | 'react/jsx-indent': [2, 2], 122 | 'react/jsx-indent-props': [2, 2], 123 | 'react/jsx-curly-newline': [2, { 124 | multiline: 'consistent' 125 | }], 126 | 'react/jsx-max-props-per-line': [2, { 127 | maximum: 3 128 | }], 129 | 'react/jsx-fragments': [2, 'syntax'], 130 | 'react/jsx-equals-spacing': [2, 'never'], 131 | 'react/destructuring-assignment': [2, 'always'], 132 | 'react/jsx-tag-spacing': [2, { 133 | closingSlash: 'never', 134 | beforeSelfClosing: 'always', 135 | afterOpening: 'never', 136 | beforeClosing: 'allow' 137 | }], 138 | 'react/jsx-closing-bracket-location': [2, 'line-aligned'], 139 | 'react/jsx-boolean-value': [2, 'never'], 140 | 'react/self-closing-comp': [2, { 141 | component: true, 142 | html: true 143 | }], 144 | 'react/jsx-first-prop-new-line': [2, 'multiline-multiprop'], 145 | 'react/jsx-sort-props': [2, { 146 | callbacksLast: true 147 | }], 148 | 'react/jsx-wrap-multilines': [2, { 149 | declaration: 'parens-new-line', 150 | assignment: 'parens-new-line', 151 | return: 'parens-new-line', 152 | condition: 'parens-new-line', 153 | logical: 'parens-new-line', 154 | arrow: 'parens-new-line', 155 | prop: 'parens-new-line' 156 | }], 157 | 'react/jsx-one-expression-per-line': 2, 158 | 'react/jsx-filename-extension': 'off', 159 | 'react/jsx-props-no-spreading': 'off', 160 | 'react/jsx-uses-react': 'off', 161 | 'react/react-in-jsx-scope': 'off', 162 | 'react/display-name': 'off', 163 | 'arrow-parens': 'off', 164 | 'react/sort-comp': 'off', 165 | 'react/no-deprecated': 'off', 166 | 'react/button-has-type': 'off', 167 | 'react/prop-types': 'off', 168 | 'arrow-body-style': 'off', 169 | 'react/require-default-props': 'off', 170 | 'react/no-array-index-key': 'off', 171 | 'react/static-property-placement': 'off', 172 | 'react/prefer-stateless-function': 'off', 173 | 'react/state-in-constructor': 'off', 174 | 'no-nested-ternary': 'off', 175 | 'react/no-danger': 'off' 176 | } 177 | }; 178 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | # push: 5 | # branches: 6 | # - main 7 | 8 | # https://frontside.com/blog/2020-05-26-github-actions-pull_request 9 | pull_request: 10 | types: [ opened, edited, synchronize, reopened ] 11 | branches: 12 | - main 13 | 14 | jobs: 15 | lint: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Setup Node 18.x 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: 18.x 23 | registry-url: https://registry.npmjs.org/ 24 | 25 | - name: Install 26 | run: npm i 27 | 28 | - name: Lint 29 | run: npm run lint 30 | 31 | stylelint: 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v3 35 | - name: Setup Node 18.x 36 | uses: actions/setup-node@v3 37 | with: 38 | node-version: 18.x 39 | registry-url: https://registry.npmjs.org/ 40 | 41 | - name: Install 42 | run: npm i 43 | 44 | - name: Stylelint 45 | run: npm run stylelint 46 | 47 | typecheck: 48 | runs-on: ubuntu-latest 49 | steps: 50 | - uses: actions/checkout@v3 51 | - name: Setup Node 18.x 52 | uses: actions/setup-node@v3 53 | with: 54 | node-version: 18.x 55 | registry-url: https://registry.npmjs.org/ 56 | 57 | - name: Install 58 | run: npm i 59 | 60 | - name: Typecheck 61 | run: npm run typecheck 62 | 63 | test: 64 | runs-on: ubuntu-latest 65 | steps: 66 | - uses: actions/checkout@v3 67 | - name: Setup Node 18.x 68 | uses: actions/setup-node@v3 69 | with: 70 | node-version: 18.x 71 | registry-url: https://registry.npmjs.org/ 72 | 73 | - name: Install 74 | run: npm i 75 | 76 | - name: Unit Test 77 | run: npm run test 78 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yaml: -------------------------------------------------------------------------------- 1 | name: 'Deploy(main)' 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | pull_request: 9 | types: [ opened, edited, synchronize, reopened ] 10 | branches: 11 | - main 12 | 13 | jobs: 14 | check: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | - uses: actions/setup-node@v3 19 | with: 20 | node-version: 18 21 | 22 | - run: npm i 23 | - run: npm test 24 | - run: npm run lint 25 | - run: npm run stylelint 26 | - run: npm run typecheck 27 | 28 | deploy: 29 | needs: check 30 | name: 'Deploy storybook' 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v3 34 | 35 | # https://github.com/marketplace/actions/vercel-action 36 | - name: deploy to vercel 37 | uses: amondnet/vercel-action@v25 38 | with: 39 | github-token: ${{ secrets.GITHUB_TOKEN }} 40 | vercel-args: '--prod' 41 | vercel-token: ${{ secrets.VERCEL_TOKEN }} 42 | vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} 43 | vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} 44 | 45 | # https://github.com/marketplace/actions/netlify-deploy 46 | # - name: deploy to netlify 47 | # uses: jsmrcaga/action-netlify-deploy@v1.8.0 48 | # with: 49 | # NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_TOKEN }} 50 | # NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} 51 | # NETLIFY_DEPLOY_TO_PROD: true 52 | # use_nvm: false 53 | # build_command: 'npm run build:docs' 54 | # build_directory: 'docs/build' 55 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release (main) 2 | 3 | on: 4 | push: 5 | # Sequence of patterns matched against refs/tags 6 | tags: 7 | - "v*" # push events to matching v*, i.e. v1.0, v20.15.10 8 | 9 | jobs: 10 | check: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions/setup-node@v3 15 | with: 16 | node-version: 18 17 | 18 | - run: npm i 19 | - run: npm test 20 | - run: npm run lint 21 | - run: npm run stylelint 22 | - run: npm run typecheck 23 | 24 | publish-npm: 25 | needs: check 26 | runs-on: ubuntu-latest 27 | steps: 28 | - uses: actions/checkout@v3 29 | - uses: actions/setup-node@v3 30 | with: 31 | node-version: 18 32 | 33 | - name: Install 34 | run: npm i 35 | 36 | - name: Set registry 37 | run: | 38 | npm config set //registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN 39 | npm publish 40 | env: 41 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 42 | 43 | create-release: 44 | needs: publish-npm 45 | runs-on: ubuntu-latest 46 | steps: 47 | - uses: actions/checkout@v3 48 | with: 49 | fetch-depth: 0 50 | 51 | - uses: actions/setup-node@v3 52 | with: 53 | node-version: 18 54 | registry-url: https://registry.npmjs.org/ 55 | 56 | - run: npx changelogithub 57 | env: 58 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .vite-ssg-dist 3 | .vite-ssg-temp 4 | *.local 5 | dist 6 | dist-ssr 7 | build 8 | node_modules 9 | .idea/ 10 | *.log 11 | /coverage 12 | package-lock.json 13 | pnpm-lock.yaml 14 | yarn.lock 15 | 16 | src/index.css 17 | playground/dist 18 | storybook-static 19 | 20 | __unconfig_vite.config.ts 21 | .vercel 22 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # shellcheck source=./_/husky.sh 4 | . "$(dirname "$0")/_/husky.sh" 5 | 6 | npx --no-install commitlint --edit "$1" 7 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npm run lint 2 | npm run stylelint 3 | npm run typecheck 4 | -------------------------------------------------------------------------------- /.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from '@storybook/react-vite'; 2 | 3 | import { join, dirname } from 'path'; 4 | 5 | /** 6 | * This function is used to resolve the absolute path of a package. 7 | * It is needed in projects that use Yarn PnP or are set up within a monorepo. 8 | */ 9 | function getAbsolutePath(value: string): any { 10 | return dirname(require.resolve(join(value, 'package.json'))); 11 | } 12 | const config: StorybookConfig = { 13 | stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], 14 | addons: [ 15 | getAbsolutePath('@storybook/addon-links'), 16 | getAbsolutePath('@storybook/addon-actions'), 17 | getAbsolutePath('@storybook/addon-essentials'), 18 | getAbsolutePath('@storybook/addon-interactions'), 19 | getAbsolutePath('@storybook/addon-onboarding'), 20 | ], 21 | framework: { 22 | name: getAbsolutePath('@storybook/react-vite'), 23 | options: {}, 24 | }, 25 | docs: { 26 | autodocs: 'tag', 27 | }, 28 | }; 29 | 30 | export default config; 31 | -------------------------------------------------------------------------------- /.storybook/preview-body.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /.storybook/preview.ts: -------------------------------------------------------------------------------- 1 | import type { Preview } from '@storybook/react'; 2 | import '../src/styles/main.scss'; 3 | 4 | const preview: Preview = { 5 | parameters: { 6 | actions: { argTypesRegex: '^on[A-Z].*' }, 7 | controls: { 8 | matchers: { 9 | color: /(background|color)$/i, 10 | date: /Date$/, 11 | }, 12 | }, 13 | }, 14 | }; 15 | 16 | export default preview; 17 | -------------------------------------------------------------------------------- /.stylelintignore: -------------------------------------------------------------------------------- 1 | dist 2 | src/index.css 3 | -------------------------------------------------------------------------------- /.stylelintrc.cjs: -------------------------------------------------------------------------------- 1 | // https://stylelint.io/user-guide/get-started 2 | // 执行npx stylelint src/**/*.css 进行校验 3 | 4 | module.exports = { 5 | extends: [ 6 | 'stylelint-config-standard', 7 | ], 8 | rules: { 9 | 'annotation-no-unknown': null, 10 | 'at-rule-no-unknown': null, 11 | 'keyframe-selector-notation': null, 12 | 'keyframe-block-no-duplicate-selectors': null, 13 | 'property-no-vendor-prefix': null, 14 | 'declaration-colon-newline-after': null, 15 | 'value-list-comma-newline-after': null, 16 | 'custom-property-pattern': null, 17 | 'color-hex-length': 'short', 18 | 'color-function-notation': null, 19 | 'alpha-value-notation': null, 20 | 'value-no-vendor-prefix': null, 21 | 'selector-class-pattern': null, 22 | 'function-url-quotes': null, 23 | 'no-descending-specificity': null, 24 | 'font-family-no-missing-generic-family-keyword': null, 25 | }, 26 | overrides: [ 27 | // 若项目中存在scss文件,添加以下配置 28 | { 29 | files: '**/*.scss', 30 | customSyntax: 'postcss-scss', 31 | }, 32 | ], 33 | }; 34 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "stylelint.vscode-stylelint" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": false, 3 | "editor.formatOnPaste": false, 4 | "editor.tabSize": 2, 5 | "editor.insertSpaces": true, 6 | "editor.codeActionsOnSave": { 7 | "source.fixAll.eslint": "explicit", 8 | "source.fixAll.stylelint": "explicit" 9 | }, 10 | "eslint.options": { 11 | "overrideConfigFile": "./.eslintrc.cjs" 12 | }, 13 | "eslint.validate": [ 14 | "javascript", 15 | "javascriptreact", 16 | "typescript", 17 | "typescriptreact" 18 | ], 19 | "stylelint.validate": [ 20 | "css", 21 | "scss", 22 | "less", 23 | "postcss" 24 | ], 25 | "[html]": { 26 | "editor.defaultFormatter": "vscode.html-language-features" 27 | }, 28 | "[json]": { 29 | "editor.defaultFormatter": "vscode.json-language-features" 30 | }, 31 | "[jsonc]": { 32 | "editor.defaultFormatter": "vscode.json-language-features" 33 | }, 34 | "[javascript]": { 35 | "editor.defaultFormatter": "vscode.typescript-language-features" 36 | }, 37 | "[typescriptreact]": { 38 | "editor.defaultFormatter": "vscode.typescript-language-features" 39 | }, 40 | "[typescript]": { 41 | "editor.defaultFormatter": "vscode.typescript-language-features" 42 | }, 43 | "tslint.suppressWhileTypeErrorsPresent": true, 44 | "typescript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": true, 45 | "typescript.format.semicolons": "insert", 46 | "typescript.updateImportsOnFileMove.enabled": "always", 47 | "typescript.tsdk": "./node_modules/typescript/lib", 48 | "files.eol": "\n", 49 | } 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 jerrywu001 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | ## Features 4 | 5 | - 💻 React suport >=16.8.0 6 | - ✈️ Project init with [vite](https://vitejs.dev/) 7 | - 📦 Support cjs & esm 8 | 9 | --- 10 | 11 | - 🪖 [Github Actions](https://docs.github.com/cn/actions) support (Auto CI on pull_request / Auto Release on push tag / Auto Deploy on push & pull_request) 12 | - 🍕 Build npm package with [tsup](https://tsup.egoist.dev/) 13 | - Build-in Docusaurus 14 | 15 | > ```npm run docs``` 16 | 17 | - 🍭 Built-in react dev environment 18 | 19 | > [playground](./playground/vite.config.ts) folder 20 | > 21 | > start --> ```npm run dev``` 22 | 23 | - 🍔 Use [browserslistrc](./.browserslistrc) 24 | - 🪗 Build styles with sass &&[postcss](./postcss.config.js) ([postcss-nested](https://www.npmjs.com/package/postcss-nested)/ [autoprefixer](https://www.npmjs.com/package/autoprefixer) / [cssnano](https://cssnano.co/docs/getting-started/)) 25 | - 🌭 [Stylelint](https://stylelint.io/) that helps you avoid errors and enforce conventions in your styles. 26 | - 🍟 [Mono repo with npm](https://dev.to/ynwd/how-to-create-react-monorepo-with-npm-workspace-webpack-and-create-react-app-2dhn) 27 | - 🎉 [TypeScript](https://www.typescriptlang.org/), of course 28 | - 🎄 Unit Testing with [Vitest](https://vitest.dev/) 29 | - 🏑 [Storybook](https://storybook.js.org/) for building UI components and pages 30 | - 🧆 [ESLint](https://eslint.org/) statically analyzes your code to quickly find problems. 31 | - ⚒ [Husky](https://typicode.github.io/husky) & [lint-staged](https://github.com/okonet/lint-staged#readme) 32 | - ☕ [Commitlint](https://commitlint.js.org) that helps your team adhering to a commit convention 33 | - 🛸 Deploy Storybook on [Netlify](https://www.netlify.com/) ---> [config](./.github/workflows/deploy.yaml) 34 | - 🥳 [MIT License](https://mit-license.org/) 35 | 36 | ## how to use 37 | 38 | replace ```custom-package-name``` with your package name 39 | 40 | 41 | ## Directory structure 42 | 43 | ```js 44 | Project 45 | ├── __tests__ # Unit Testing 46 | ├── babel.config.js # babel config 47 | ├── package.json 48 | ├── playground # dev environment folder (can use source code) 49 | │ ├── index.html 50 | │ ├── package.json 51 | │ ├── public 52 | │ ├── src 53 | │ ├── tsconfig.json 54 | │ ├── vite-env.d.ts 55 | │ └── vite.config.ts 56 | ├── postcss.config.js # build styles with postcss 57 | ├── src # Package source code 58 | │ ├── index.ts # Package source entry 59 | │ ├── stories # storybook for building UI components and pages 60 | │ ├── styles # styles for Package 61 | │ └── types.ts # ts type declaration for Package 62 | ├── tsconfig.json # ts config 63 | └── tsup.config.ts # build package with tsup 64 | ``` 65 | 66 | ## How to add GITHUB_TOKEN 67 | 68 | - [add GITHUB_TOKEN](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) 69 | 70 | ## How to add NPM_TOKEN 71 | 72 | 0. update project setting 73 | 74 | settings -> actions -> General 75 | 76 | ![./token.png](https://ik.imagekit.io/jerrywu001/token.png?updatedAt=1678687995627) 77 | 78 | 1. [create npm auth token](https://docs.npmjs.com/creating-and-viewing-access-tokens) 79 | 2. then copy npm token, add to github project settings 80 | 81 | - project -> settings -> secrets -> actions -> create new token with name:NPM_TOKEN 82 | 83 | ## How to add NETLIFY_TOKEN 84 | 85 | 1. [create netlify auth token](https://app.netlify.com/user/applications#personal-access-tokens) 86 | 2. then copy netlify token, add to github project settings 87 | 88 | - project -> settings -> secrets -> actions -> create new token with name:NETLIFY_TOKEN 89 | 90 | 3. create a site on netlify 91 | 92 | 4. copy the site_id from your netlify site settings, add it to github project settings 93 | 94 | - project -> settings -> secrets -> actions -> create new token with name:NETLIFY_SITE_ID 95 | 96 | 5. Stop Build from Build settings of site 97 | 98 | 6. [build-command-javascript-heap-out-of-memory](https://answers.netlify.com/t/build-command-javascript-heap-out-of-memory/85348/6) 99 | 100 | ## How to generate VERCEL_ORG_ID / VERCEL_PROJECT_ID 101 | 102 | 1. run ```npx vercel``` in project root folder 103 | 2. open .vercel/project.json 104 | 3. copy orgId & projectId, add it to github project settings 105 | 106 | - project -> settings -> secrets -> actions -> create new token with name:VERCEL_ORG_ID & VERCEL_PROJECT_ID 107 | 108 | 4. [create vercel token](https://vercel.com/account/tokens), add it to github project settings 109 | 110 | - project -> settings -> secrets -> actions -> create new token with name:VERCEL_TOKEN 111 | 112 | 5. change project build setting 113 | 114 | ![setting](https://ik.imagekit.io/jerrywu001/vercel_deploy.png?ik-sdk-version=javascript-1.4.3&updatedAt=1658727573636) 115 | 116 | ## Sponsor 117 | 118 |

119 |

Special Sponsor

120 |

121 | 122 |

123 | 124 | special sponsor appwrite 125 | 126 |

127 | 128 | 129 | -------------------------------------------------------------------------------- /__tests__/__snapshots__/dom.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`dom test > testing-library/jest-dom 1`] = ` 4 |
7 | `; 8 | -------------------------------------------------------------------------------- /__tests__/dom.test.tsx: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom'; 2 | import { describe, test, expect } from 'vitest'; 3 | 4 | describe('dom test', () => { 5 | test.concurrent('testing-library/jest-dom', async () => { 6 | const div = document.createElement('div'); 7 | div.id = 'adm-mask'; 8 | expect(div).not.toBeNull(); 9 | expect(div).toBeDefined(); 10 | expect(div).toBeInstanceOf(HTMLDivElement); 11 | 12 | await document.body.appendChild(div); 13 | 14 | const mask = document.body.querySelector('#adm-mask') as HTMLElement; 15 | expect(mask).toMatchSnapshot(); 16 | div.remove(); 17 | expect(mask).not.toBeInTheDocument(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /__tests__/js.spec.tsx: -------------------------------------------------------------------------------- 1 | import { test, expect } from 'vitest'; 2 | 3 | test.concurrent('handles js', () => { 4 | expect(1 + 1).toBe(2); 5 | }); 6 | -------------------------------------------------------------------------------- /__tests__/mockfn.spec.ts: -------------------------------------------------------------------------------- 1 | import { vi, describe, test, expect } from 'vitest'; 2 | 3 | function timer(callback) { 4 | setTimeout(() => { 5 | callback(); 6 | }, 5000); 7 | } 8 | 9 | describe('mock function test', () => { 10 | test('toHaveReturnedWith', async () => { 11 | const init = { test: 'hello' }; 12 | const mockFn = vi.fn().mockImplementation(() => init); 13 | // const mockFn = vi.fn(() => init); 14 | 15 | await mockFn(); 16 | 17 | expect(mockFn).toBeCalledTimes(1); 18 | expect(mockFn).toHaveReturnedWith(init); 19 | expect(mockFn.mock.results[0].value).toStrictEqual(init); 20 | }); 21 | 22 | test('promise resolve', async () => { // 不要使用toHaveReturnedWith 23 | // const mockFn = vi.fn().mockImplementation(apples => Promise.resolve(apples + 1)); 24 | const mockFn = vi.fn(apples => Promise.resolve(apples + 1)); 25 | expect(mockFn).not.toBeCalled(); 26 | expect(mockFn).toBeCalledTimes(0); 27 | expect(mockFn.mock.calls.length).toBe(0); 28 | 29 | const val = await mockFn(2); 30 | 31 | expect(mockFn).toBeCalledTimes(1); 32 | expect(val).toBe(3); 33 | expect(mockFn.mock.results[0].value).toEqual(3); 34 | }); 35 | 36 | test('timers', () => { 37 | vi.useFakeTimers(); 38 | const mockFn = vi.fn(); 39 | timer(mockFn); 40 | vi.advanceTimersByTime(5000); 41 | expect(mockFn).toBeCalled(); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /app.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'esbuild-plugin-babel' { 2 | export default function babel(build?: any): any; 3 | } 4 | -------------------------------------------------------------------------------- /commitlint.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // https://github.com/conventional-changelog/commitlint/#what-is-commitlint 3 | extends: ['@commitlint/config-conventional'], 4 | }; 5 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /docs/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/docs/get-started/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Get - Started", 3 | "position": 2, 4 | "link": { 5 | "type": "generated-index", 6 | "description": " " 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/docs/get-started/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Installation 6 | 7 | Installation 8 | -------------------------------------------------------------------------------- /docs/docs/get-started/markdown-features.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # Markdown Features 6 | 7 | Docusaurus supports **[Markdown](https://daringfireball.net/projects/markdown/syntax)** and a few **additional features**. 8 | 9 | ## Front Matter 10 | 11 | Markdown documents have metadata at the top called [Front Matter](https://jekyllrb.com/docs/front-matter/): 12 | 13 | ```text title="my-doc.md" 14 | // highlight-start 15 | --- 16 | id: my-doc-id 17 | title: My document title 18 | description: My document description 19 | slug: /my-custom-url 20 | --- 21 | // highlight-end 22 | 23 | ## Markdown heading 24 | 25 | Markdown text with [links](./hello.md) 26 | ``` 27 | 28 | ## Images 29 | 30 | Regular Markdown images are supported. 31 | 32 | You can use absolute paths to reference images in the static directory (`static/img/docusaurus.png`): 33 | 34 | ```md 35 | ![Docusaurus logo](/img/docusaurus.png) 36 | ``` 37 | 38 | ![Docusaurus logo](/img/docusaurus.png) 39 | 40 | 41 | ## Code Blocks 42 | 43 | Markdown code blocks are supported with Syntax highlighting. 44 | 45 | ```jsx title="src/components/HelloDocusaurus.js" 46 | function HelloDocusaurus() { 47 | return ( 48 |

Hello, Docusaurus!

49 | ) 50 | } 51 | ``` 52 | 53 | ```jsx title="src/components/HelloDocusaurus.js" 54 | function HelloDocusaurus() { 55 | return

Hello, Docusaurus!

; 56 | } 57 | ``` 58 | 59 | ## Admonitions 60 | 61 | Docusaurus has a special syntax to create admonitions and callouts: 62 | 63 | :::tip My tip 64 | 65 | Use this awesome feature option 66 | 67 | ::: 68 | 69 | :::danger Take care 70 | 71 | This action is dangerous 72 | 73 | ::: 74 | 75 | :::tip My tip 76 | 77 | Use this awesome feature option 78 | 79 | ::: 80 | 81 | :::danger Take care 82 | 83 | This action is dangerous 84 | 85 | ::: 86 | 87 | ## MDX and React Components 88 | 89 | [MDX](https://mdxjs.com/) can make your documentation more **interactive** and allows using any **React components inside Markdown**: 90 | 91 | ```jsx 92 | export const Highlight = ({children, color}) => ( 93 | { 102 | alert(`You clicked the color ${color} with label ${children}`) 103 | }}> 104 | {children} 105 | 106 | ); 107 | 108 | This is Docusaurus green ! 109 | 110 | This is Facebook blue ! 111 | ``` 112 | 113 | export const Highlight = ({children, color}) => ( 114 | { 123 | alert(`You clicked the color ${color} with label ${children}`); 124 | }}> 125 | {children} 126 | 127 | ); 128 | 129 | This is Docusaurus green ! 130 | 131 | This is Facebook blue ! 132 | -------------------------------------------------------------------------------- /docs/docs/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Component Intro 6 | 7 | Component Intro 8 | -------------------------------------------------------------------------------- /docs/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Note: type annotations allow type checking and IDEs autocompletion 3 | 4 | const lightCodeTheme = require('prism-react-renderer/themes/github'); 5 | const darkCodeTheme = require('prism-react-renderer/themes/dracula'); 6 | 7 | /** @type {import('@docusaurus/types').Config} */ 8 | const config = { 9 | title: 'custom-package-name', 10 | tagline: 'Dinosaurs are cool', 11 | url: 'https://your-docusaurus-test-site.com', 12 | baseUrl: '/', 13 | onBrokenLinks: 'throw', 14 | onBrokenMarkdownLinks: 'warn', 15 | favicon: 'img/favicon.ico', 16 | 17 | // GitHub pages deployment config. 18 | // If you aren't using GitHub pages, you don't need these. 19 | organizationName: 'facebook', // Usually your GitHub org/user name. 20 | projectName: 'docusaurus', // Usually your repo name. 21 | 22 | // Even if you don't use internalization, you can use this field to set useful 23 | // metadata like html lang. For example, if your site is Chinese, you may want 24 | // to replace "en" with "zh-Hans". 25 | i18n: { 26 | defaultLocale: 'en', 27 | locales: ['en'], 28 | }, 29 | 30 | presets: [ 31 | [ 32 | 'classic', 33 | /** @type {import('@docusaurus/preset-classic').Options} */ 34 | ({ 35 | docs: { 36 | sidebarPath: require.resolve('./sidebars.js'), 37 | // Please change this to your repo. 38 | // Remove this to remove the "edit this page" links. 39 | editUrl: 40 | 'https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/', 41 | }, 42 | theme: { 43 | customCss: require.resolve('./src/css/custom.css'), 44 | }, 45 | }), 46 | ], 47 | ], 48 | 49 | themeConfig: 50 | /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ 51 | ({ 52 | navbar: { 53 | title: 'custom-package-name', 54 | logo: { 55 | alt: 'custom-package-name Logo', 56 | src: 'img/logo.svg', 57 | }, 58 | items: [ 59 | { 60 | type: 'doc', 61 | docId: 'intro', 62 | position: 'left', 63 | label: 'Get Started', 64 | }, 65 | { 66 | href: 'https://github.com/facebook/docusaurus', 67 | label: 'GitHub', 68 | position: 'right', 69 | }, 70 | ], 71 | }, 72 | footer: { 73 | style: 'dark', 74 | copyright: `Copyright © ${new Date().getFullYear()} My Project, Inc. Built with Docusaurus.`, 75 | }, 76 | prism: { 77 | theme: lightCodeTheme, 78 | darkTheme: darkCodeTheme, 79 | }, 80 | }), 81 | }; 82 | 83 | module.exports = config; 84 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids", 15 | "typecheck": "tsc" 16 | }, 17 | "dependencies": { 18 | "@docusaurus/core": "^3.3.2", 19 | "@docusaurus/preset-classic": "^3.3.2", 20 | "@mdx-js/react": "^2.3.0", 21 | "clsx": "^2.1.1", 22 | "prism-react-renderer": "^1.3.5", 23 | "react": "^18.3.1", 24 | "react-dom": "^18.3.1" 25 | }, 26 | "devDependencies": { 27 | "@docusaurus/module-type-aliases": "^3.3.2", 28 | "@tsconfig/docusaurus": "^2.0.3", 29 | "typescript": "^5.4.5" 30 | }, 31 | "browserslist": { 32 | "production": [ 33 | ">0.5%", 34 | "not dead", 35 | "not op_mini all" 36 | ], 37 | "development": [ 38 | "last 1 chrome version", 39 | "last 1 firefox version", 40 | "last 1 safari version" 41 | ] 42 | }, 43 | "engines": { 44 | "node": ">=16" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /docs/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | // @ts-check 13 | 14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 15 | const sidebars = { 16 | // By default, Docusaurus generates a sidebar from the docs folder structure 17 | tutorialSidebar: [{ type: 'autogenerated', dirName: '.' }], 18 | 19 | // But you can create a sidebar manually 20 | /* 21 | tutorialSidebar: [ 22 | 'intro', 23 | 'hello', 24 | { 25 | type: 'category', 26 | label: 'Tutorial', 27 | items: ['tutorial-basics/create-a-document'], 28 | }, 29 | ], 30 | */ 31 | }; 32 | 33 | module.exports = sidebars; 34 | -------------------------------------------------------------------------------- /docs/src/components/HomepageFeatures/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import styles from './styles.module.css'; 4 | 5 | type FeatureItem = { 6 | title: string; 7 | Svg: React.ComponentType>; 8 | description: JSX.Element; 9 | }; 10 | 11 | const FeatureList: FeatureItem[] = [ 12 | { 13 | title: 'Easy to Use', 14 | Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default, 15 | description: ( 16 | <> 17 | Docusaurus was designed from the ground up to be easily installed and 18 | used to get your website up and running quickly. 19 | 20 | ), 21 | }, 22 | { 23 | title: 'Focus on What Matters', 24 | Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default, 25 | description: ( 26 | <> 27 | Docusaurus lets you focus on your docs, and we'll do the chores. Go 28 | ahead and move your docs into the 29 | {' '} 30 | 31 | docs 32 | 33 | {' '} 34 | directory. 35 | 36 | ), 37 | }, 38 | { 39 | title: 'Powered by React', 40 | Svg: require('@site/static/img/undraw_docusaurus_react.svg').default, 41 | description: ( 42 | <> 43 | Extend or customize your website layout by reusing React. Docusaurus can 44 | be extended while reusing the same header and footer. 45 | 46 | ), 47 | }, 48 | ]; 49 | 50 | function Feature({ title, Svg, description }: FeatureItem) { 51 | return ( 52 |
53 |
54 | 55 |
56 |
57 |

58 | {title} 59 |

60 |

61 | {description} 62 |

63 |
64 |
65 | ); 66 | } 67 | 68 | export default function HomepageFeatures(): JSX.Element { 69 | return ( 70 |
71 |
72 |
73 | {FeatureList.map((props, idx) => ( 74 | 75 | ))} 76 |
77 |
78 |
79 | ); 80 | } 81 | -------------------------------------------------------------------------------- /docs/src/components/HomepageFeatures/styles.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /docs/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #2e8555; 10 | --ifm-color-primary-dark: #29784c; 11 | --ifm-color-primary-darker: #277148; 12 | --ifm-color-primary-darkest: #205d3b; 13 | --ifm-color-primary-light: #33925d; 14 | --ifm-color-primary-lighter: #359962; 15 | --ifm-color-primary-lightest: #3cad6e; 16 | --ifm-code-font-size: 95%; 17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 18 | } 19 | 20 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 21 | [data-theme='dark'] { 22 | --ifm-color-primary: #25c2a0; 23 | --ifm-color-primary-dark: #21af90; 24 | --ifm-color-primary-darker: #1fa588; 25 | --ifm-color-primary-darkest: #1a8870; 26 | --ifm-color-primary-light: #29d5b0; 27 | --ifm-color-primary-lighter: #32d8b4; 28 | --ifm-color-primary-lightest: #4fddbf; 29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); 30 | } 31 | -------------------------------------------------------------------------------- /docs/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /docs/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import Link from '@docusaurus/Link'; 4 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 5 | import Layout from '@theme/Layout'; 6 | import HomepageFeatures from '@site/src/components/HomepageFeatures'; 7 | 8 | import styles from './index.module.css'; 9 | 10 | function HomepageHeader() { 11 | const { siteConfig } = useDocusaurusContext(); 12 | return ( 13 |
14 |
15 |

16 | {siteConfig.title} 17 |

18 |

19 | {siteConfig.tagline} 20 |

21 |
22 | 26 | Docusaurus Tutorial - 5min ⏱️ 27 | 28 |
29 |
30 |
31 | ); 32 | } 33 | 34 | export default function Home(): JSX.Element { 35 | const { siteConfig } = useDocusaurusContext(); 36 | return ( 37 | 41 | 42 |
43 | 44 |
45 |
46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /docs/src/pages/markdown-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown page example 3 | --- 4 | 5 | # Markdown page example 6 | 7 | You don't need React to write simple standalone pages. 8 | -------------------------------------------------------------------------------- /docs/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codes-templates/npm-react/cd0bec5779d0aca7c63ebb6fb37eb24225594529/docs/static/.nojekyll -------------------------------------------------------------------------------- /docs/static/img/docusaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codes-templates/npm-react/cd0bec5779d0aca7c63ebb6fb37eb24225594529/docs/static/img/docusaurus.png -------------------------------------------------------------------------------- /docs/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codes-templates/npm-react/cd0bec5779d0aca7c63ebb6fb37eb24225594529/docs/static/img/favicon.ico -------------------------------------------------------------------------------- /docs/static/img/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/img/undraw_docusaurus_mountain.svg: -------------------------------------------------------------------------------- 1 | 2 | Easy to Use 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /docs/static/img/undraw_docusaurus_react.svg: -------------------------------------------------------------------------------- 1 | 2 | Powered by React 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /docs/static/img/undraw_docusaurus_tree.svg: -------------------------------------------------------------------------------- 1 | 2 | Focus on What Matters 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // This file is not used in compilation. It is here just for a nice editor experience. 3 | "extends": "@tsconfig/docusaurus/tsconfig.json", 4 | "compilerOptions": { 5 | "baseUrl": "." 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "custom-package-name", 3 | "version": "0.0.7", 4 | "description": "", 5 | "keywords": [], 6 | "type": "module", 7 | "homepage": "https://github.com/codes-templates/npm-react#readme", 8 | "bugs": { 9 | "url": "https://github.com/codes-templates/npm-react/issues" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/codes-templates/npm-react.git" 14 | }, 15 | "license": "MIT", 16 | "author": "jerrywu001 <57242263@163.com>", 17 | "sideEffects": [ 18 | "dist/index.css" 19 | ], 20 | "main": "./dist/index.js", 21 | "module": "./dist/index.mjs", 22 | "types": "./dist/index.d.ts", 23 | "exports": { 24 | ".": { 25 | "types": "./dist/index.d.ts", 26 | "import": "./dist/index.mjs", 27 | "require": "./dist/index.js" 28 | }, 29 | "./*": [ 30 | "./*", 31 | "./*.d.ts" 32 | ] 33 | }, 34 | "typesVersions": { 35 | "*": { 36 | "*": [ 37 | "./dist/*", 38 | "./*" 39 | ] 40 | } 41 | }, 42 | "files": [ 43 | "dist", 44 | "package.json", 45 | "README.md", 46 | "global.d.ts", 47 | "*.d.ts" 48 | ], 49 | "workspaces": [ 50 | "playground", 51 | "docs" 52 | ], 53 | "scripts": { 54 | "storybook": "storybook dev -p 6006", 55 | "build": "tsup && npm run build:css", 56 | "dev": "npm run dev -w playground", 57 | "dev:use-bundle": "run-p tsup-w dev-pack", 58 | "docs": "npm run start -w docs", 59 | "build:docs": "npm run build -w docs", 60 | "build:css": "npm run build:scss && npm run build:postcss && rimraf rf ./src/index.css", 61 | "build:scss": "sass --no-source-map --style=compressed src/styles/main.scss src/index.css", 62 | "build:postcss": "postcss src/index.css -o dist/index.css", 63 | "build:vite": "vite build && npm run build:css", 64 | "build:storybook": "cross-env NO_DTS=1 storybook build", 65 | "preview:docs": "npm run serve -w docs", 66 | "lint": "eslint --ext .ts,.tsx ./src", 67 | "tsup-w": "tsup --watch", 68 | "dev-pack": "npm run dev:usepack -w playground", 69 | "prepare": "chmod a+x .husky/* && husky", 70 | "prepublishOnly": "npm run build", 71 | "release": "bumpp --commit --tag --push", 72 | "stylelint": "stylelint \"src/**/*.{css,scss}\"", 73 | "test": "vitest", 74 | "test:ui": "vitest --ui", 75 | "typecheck": "tsc --noEmit" 76 | }, 77 | "dependencies": { 78 | "@babel/runtime": "^7.24.5", 79 | "core-js": "^3.37.1" 80 | }, 81 | "devDependencies": { 82 | "@babel/plugin-transform-runtime": "^7.24.3", 83 | "@commitlint/cli": "^19.3.0", 84 | "@commitlint/config-conventional": "^19.2.2", 85 | "@rollup/plugin-babel": "^6.0.4", 86 | "@storybook/addon-essentials": "^8.1.1", 87 | "@storybook/addon-interactions": "^8.1.1", 88 | "@storybook/addon-links": "^8.1.1", 89 | "@storybook/addon-onboarding": "^8.1.1", 90 | "@storybook/blocks": "^8.1.1", 91 | "@storybook/react": "^8.1.1", 92 | "@storybook/react-vite": "^8.1.1", 93 | "@storybook/test": "^8.1.1", 94 | "@swc/core": "^1.5.7", 95 | "@testing-library/jest-dom": "^6.4.5", 96 | "@testing-library/react": "^15.0.7", 97 | "@testing-library/user-event": "^14.5.2", 98 | "@types/react": "^18.3.2", 99 | "@types/react-dom": "^18.3.0", 100 | "@typescript-eslint/eslint-plugin": "^7.9.0", 101 | "@typescript-eslint/parser": "^7.9.0", 102 | "@vitest/ui": "^1.6.0", 103 | "babel-loader": "^9.1.3", 104 | "browserslist-to-esbuild": "^2.1.1", 105 | "bumpp": "^9.4.1", 106 | "cross-env": "^7.0.3", 107 | "cssnano": "^7.0.1", 108 | "esbuild-plugin-babel": "^0.2.3", 109 | "eslint": "^8.57.0", 110 | "eslint-config-airbnb-base": "^15.0.0", 111 | "eslint-config-airbnb-typescript": "^18.0.0", 112 | "eslint-plugin-import": "^2.29.1", 113 | "eslint-plugin-jsx-a11y": "^6.8.0", 114 | "eslint-plugin-react": "^7.34.1", 115 | "eslint-plugin-react-hooks": "^4.6.2", 116 | "eslint-plugin-storybook": "^0.8.0", 117 | "husky": "^9.0.11", 118 | "jsdom": "^24.0.0", 119 | "less": "^4.2.0", 120 | "npm-run-all": "^4.1.5", 121 | "path": "^0.12.7", 122 | "postcss": "^8.4.38", 123 | "postcss-cli": "^11.0.0", 124 | "postcss-flexbugs-fixes": "^5.0.2", 125 | "postcss-nested": "^6.0.1", 126 | "postcss-preset-env": "^9.5.13", 127 | "postcss-scss": "^4.0.9", 128 | "react": "^18.3.1", 129 | "react-dom": "^18.3.1", 130 | "react-element-to-jsx-string": "^15.0.0", 131 | "rimraf": "^5.0.7", 132 | "sass": "^1.77.1", 133 | "storybook": "^8.1.1", 134 | "stylelint": "^15.11.0", 135 | "stylelint-config-standard": "^34.0.0", 136 | "tsup": "^8.0.2", 137 | "typescript": "^5.4.5", 138 | "vite": "^5.2.11", 139 | "vite-plugin-dts": "^3.9.1", 140 | "vitest": "^1.6.0" 141 | }, 142 | "peerDependencies": { 143 | "react": "^16.8.0 || ^17 || ^18", 144 | "react-dom": "^16.8.0 || ^17 || ^18" 145 | }, 146 | "engines": { 147 | "node": ">=18", 148 | "npm": ">=9" 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /playground/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /playground/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playground", 3 | "version": "1.0.0", 4 | "type": "module", 5 | "scripts": { 6 | "build": "tsc && vite build", 7 | "dev": "vite --host --open", 8 | "dev:usepack": "cross-env USEPACK=true vite --host --port 5000 --open", 9 | "preview": "vite preview", 10 | "typecheck": "tsc --noEmit" 11 | }, 12 | "dependencies": { 13 | "react-router": "^6.23.1", 14 | "react-router-dom": "^6.23.1" 15 | }, 16 | "devDependencies": { 17 | "@types/react-router": "^5.1.20", 18 | "@types/react-router-dom": "^5.3.3", 19 | "@vitejs/plugin-react": "^4.2.1", 20 | "cross-env": "^7.0.3" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /playground/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codes-templates/npm-react/cd0bec5779d0aca7c63ebb6fb37eb24225594529/playground/public/favicon.ico -------------------------------------------------------------------------------- /playground/src/main.tsx: -------------------------------------------------------------------------------- 1 | import App from './routers'; 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom/client'; 4 | 5 | // eslint-disable-next-line import/no-relative-packages 6 | import '../../src/styles/main.scss'; // 不要修改或删除 7 | 8 | const root = document.getElementById('root')!; 9 | 10 | ReactDOM.createRoot(root).render(); 11 | -------------------------------------------------------------------------------- /playground/src/pages/home/Home.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Demo, REACT_TEST } from 'custom-package-name'; 3 | 4 | export default function Home() { 5 | return ( 6 |
7 | home 8 | {' '} 9 | 10 | { REACT_TEST.demo } 11 | 12 | 13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /playground/src/routers.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/dot-notation */ 2 | /* eslint-disable @typescript-eslint/no-shadow */ 3 | /* eslint-disable react/jsx-one-expression-per-line */ 4 | import Home from './pages/home/Home'; 5 | import { BrowserRouter as Router, Navigate } from 'react-router-dom'; 6 | import React, { useEffect } from 'react'; 7 | import { 8 | Route, 9 | Routes, 10 | useLocation, 11 | useNavigationType, 12 | } from 'react-router'; 13 | 14 | // https://reactrouter.com/docs/en/v6 15 | 16 | function AppRoutes() { 17 | const location = useLocation(); 18 | 19 | return ( 20 | 21 | } index /> 22 | } path="/home" /> 23 | 24 | ); 25 | } 26 | 27 | export default function App() { 28 | return ( 29 | 30 | 31 | 32 | ); 33 | } 34 | 35 | function AppContainer() { 36 | const location = useLocation(); 37 | const action = useNavigationType(); 38 | 39 | useEffect(() => { 40 | const { pathname = '', search = '' } = location; 41 | console.log(`router mode: ${action}, url: ${pathname}${search}`); 42 | }); 43 | 44 | return ( 45 | 46 | ); 47 | } 48 | 49 | export enum Action { 50 | Pop = 'POP', 51 | Push = 'PUSH', 52 | Replace = 'REPLACE', 53 | } 54 | -------------------------------------------------------------------------------- /playground/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "esnext", 5 | "module": "esnext", 6 | "moduleResolution": "Node", 7 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 8 | "skipDefaultLibCheck": true, 9 | "noEmit": true, 10 | "sourceMap": true, 11 | "allowJs": true, 12 | "skipLibCheck": true, 13 | "strictNullChecks": true, 14 | "esModuleInterop": true, 15 | "ignoreDeprecations": "5.0", 16 | "allowSyntheticDefaultImports": true, 17 | "strict": true, 18 | "forceConsistentCasingInFileNames": true, 19 | "noUnusedLocals": false, 20 | "noFallthroughCasesInSwitch": true, 21 | "noImplicitReturns": false, 22 | "noImplicitThis": true, 23 | "noImplicitAny": false, 24 | "importHelpers": false, 25 | "resolveJsonModule": true, 26 | "isolatedModules": false, 27 | "emitDecoratorMetadata": true, 28 | "experimentalDecorators": true, 29 | "jsx": "react", 30 | "paths": { 31 | "@/*": [ 32 | "src/*" 33 | ], 34 | "custom-package-name": [ 35 | "../src" 36 | ], 37 | "custom-package-name/*": [ 38 | "../src/*" 39 | ] 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /playground/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/triple-slash-reference 2 | /// 3 | /// 4 | -------------------------------------------------------------------------------- /playground/vite.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import path from 'path'; 4 | import { defineConfig } from 'vite'; 5 | import react from '@vitejs/plugin-react'; 6 | 7 | export default defineConfig({ 8 | resolve: { 9 | alias: { 10 | '@/': `${path.resolve(__dirname, 'src')}/`, 11 | 'custom-package-name': path.resolve(__dirname, process.env.USEPACK === 'true' ? '../dist' : '../src'), 12 | }, 13 | }, 14 | build: { 15 | sourcemap: true, 16 | }, 17 | plugins: [ 18 | react(), 19 | ], 20 | }); 21 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | // https://cssnano.co/docs/getting-started/ 2 | 3 | module.exports = { 4 | plugins: [ 5 | require('cssnano')({ 6 | preset: 'default', 7 | }), 8 | require('postcss-nested'), 9 | require('postcss-flexbugs-fixes'), // https://github.com/luisrudge/postcss-flexbugs-fixes#readme 10 | require('postcss-preset-env')({ 11 | autoprefixer: { 12 | grid: false, 13 | }, 14 | }), 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /setupTests.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom'; 2 | -------------------------------------------------------------------------------- /src/components/Demo.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | const test = null; 4 | 5 | export function Demo() { 6 | const [count] = useState(0); 7 | const b = '5'.padStart(2, '1'); 8 | const c = test ?? 'hello'; 9 | const d = {} as Record; 10 | 11 | return ( 12 |
13 |

14 | count in jsx component: 15 | {' '} 16 | {count} 17 |

18 |

19 | {b} 20 |

21 |

22 | {c} 23 |

24 |

25 | test `?.` 26 | {' '} 27 | {d?.name} 28 |

29 |
30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Demo'; 2 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types'; // can not delete 2 | export * from './presets'; 3 | export * from './components'; 4 | -------------------------------------------------------------------------------- /src/presets/index.ts: -------------------------------------------------------------------------------- 1 | import { TEST_REACT } from '../types'; 2 | 3 | export const REACT_TEST: TEST_REACT = { 4 | demo: 'hello world', 5 | }; 6 | -------------------------------------------------------------------------------- /src/stories/Button.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | 3 | import { Button } from './Button'; 4 | 5 | // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export 6 | const meta = { 7 | title: 'Example/Button', 8 | component: Button, 9 | parameters: { 10 | // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout 11 | layout: 'centered', 12 | }, 13 | // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs 14 | tags: ['autodocs'], 15 | // More on argTypes: https://storybook.js.org/docs/react/api/argtypes 16 | argTypes: { 17 | backgroundColor: { control: 'color' }, 18 | }, 19 | } satisfies Meta; 20 | 21 | export default meta; 22 | type Story = StoryObj; 23 | 24 | // More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args 25 | export const Primary: Story = { 26 | args: { 27 | primary: true, 28 | label: 'Button', 29 | }, 30 | }; 31 | 32 | export const Secondary: Story = { 33 | args: { 34 | label: 'Button', 35 | }, 36 | }; 37 | 38 | export const Large: Story = { 39 | args: { 40 | size: 'large', 41 | label: 'Button', 42 | }, 43 | }; 44 | 45 | export const Small: Story = { 46 | args: { 47 | size: 'small', 48 | label: 'Button', 49 | }, 50 | }; 51 | 52 | export const Warning: Story = { 53 | args: { 54 | primary: true, 55 | label: 'Delete now', 56 | backgroundColor: 'red', 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /src/stories/Button.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './button.less'; 3 | 4 | interface ButtonProps { 5 | /** 6 | * Is this the principal call to action on the page? 7 | */ 8 | primary?: boolean; 9 | /** 10 | * What background color to use 11 | */ 12 | backgroundColor?: string; 13 | /** 14 | * How large should the button be? 15 | */ 16 | size?: 'small' | 'medium' | 'large'; 17 | /** 18 | * Button contents 19 | */ 20 | label: string; 21 | /** 22 | * Optional click handler 23 | */ 24 | onClick?: () => void; 25 | } 26 | 27 | /** 28 | * Primary UI component for user interaction 29 | */ 30 | export const Button = ({ 31 | primary = false, 32 | size = 'medium', 33 | backgroundColor, 34 | label, 35 | ...props 36 | }: ButtonProps) => { 37 | const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary'; 38 | 39 | return ( 40 | 48 | ); 49 | }; 50 | -------------------------------------------------------------------------------- /src/stories/Configure.mdx: -------------------------------------------------------------------------------- 1 | import { Meta } from "@storybook/blocks"; 2 | 3 | import Github from "./assets/github.svg"; 4 | import Discord from "./assets/discord.svg"; 5 | import Youtube from "./assets/youtube.svg"; 6 | import Tutorials from "./assets/tutorials.svg"; 7 | import Styling from "./assets/styling.png"; 8 | import Context from "./assets/context.png"; 9 | import Assets from "./assets/assets.png"; 10 | import Docs from "./assets/docs.png"; 11 | import Share from "./assets/share.png"; 12 | import FigmaPlugin from "./assets/figma-plugin.png"; 13 | import Testing from "./assets/testing.png"; 14 | import Accessibility from "./assets/accessibility.png"; 15 | import Theming from "./assets/theming.png"; 16 | import AddonLibrary from "./assets/addon-library.png"; 17 | 18 | export const RightArrow = () => 31 | 32 | 33 | 34 | 35 | 36 |
37 |
38 | # Configure your project 39 | 40 | Because Storybook works separately from your app, you'll need to configure it for your specific stack and setup. Below, explore guides for configuring Storybook with popular frameworks and tools. If you get stuck, learn how you can ask for help from our community. 41 |
42 |
43 |
44 | A wall of logos representing different styling technologies 48 |

Add styling and CSS

49 |

Like with web applications, there are many ways to include CSS within Storybook. Learn more about setting up styling within Storybook.

50 | Learn more 54 |
55 |
56 | An abstraction representing the composition of data for a component 60 |

Provide context and mocking

61 |

Often when a story doesn't render, it's because your component is expecting a specific environment or context (like a theme provider) to be available.

62 | Learn more 66 |
67 |
68 | A representation of typography and image assets 69 |
70 |

Load assets and resources

71 |

To link static files (like fonts) to your projects and stories, use the 72 | `staticDirs` configuration option to specify folders to load when 73 | starting Storybook.

74 | Learn more 78 |
79 |
80 |
81 |
82 |
83 |
84 | # Do more with Storybook 85 | 86 | Now that you know the basics, let's explore other parts of Storybook that will improve your experience. This list is just to get you started. You can customise Storybook in many ways to fit your needs. 87 |
88 | 89 |
90 |
91 |
92 | A screenshot showing the autodocs tag being set, pointing a docs page being generated 93 |

Autodocs

94 |

Auto-generate living, 95 | interactive reference documentation from your components and stories.

96 | Learn more 100 |
101 |
102 | A browser window showing a Storybook being published to a chromatic.com URL 103 |

Publish to Chromatic

104 |

Publish your Storybook to review and collaborate with your entire team.

105 | Learn more 109 |
110 |
111 | Windows showing the Storybook plugin in Figma 112 |

Figma Plugin

113 |

Embed your stories into Figma to cross-reference the design and live 114 | implementation in one place.

115 | Learn more 119 |
120 |
121 | Screenshot of tests passing and failing 122 |

Testing

123 |

Use stories to test a component in all its variations, no matter how 124 | complex.

125 | Learn more 129 |
130 |
131 | Screenshot of accessibility tests passing and failing 132 |

Accessibility

133 |

Automatically test your components for a11y issues as you develop.

134 | Learn more 138 |
139 |
140 | Screenshot of Storybook in light and dark mode 141 |

Theming

142 |

Theme Storybook's UI to personalize it to your project.

143 | Learn more 147 |
148 |
149 |
150 |
151 |
152 |
153 |

Addons

154 |

Integrate your tools with Storybook to connect workflows.

155 | Discover all addons 159 |
160 |
161 | Integrate your tools with Storybook to connect workflows. 162 |
163 |
164 | 165 |
166 |
167 | Github logo 168 | Join our contributors building the future of UI development. 169 | 170 | Star on GitHub 174 |
175 |
176 | Discord logo 177 |
178 | Get support and chat with frontend developers. 179 | 180 | Join Discord server 184 |
185 |
186 |
187 | Youtube logo 188 |
189 | Watch tutorials, feature previews and interviews. 190 | 191 | Watch on YouTube 195 |
196 |
197 |
198 | A book 199 |

Follow guided walkthroughs on for key workflows.

200 | 201 | Discover tutorials 205 |
206 |
207 | 208 | 365 | -------------------------------------------------------------------------------- /src/stories/Header.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | 3 | import { Header } from './Header'; 4 | 5 | const meta = { 6 | title: 'Example/Header', 7 | component: Header, 8 | // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs 9 | tags: ['autodocs'], 10 | parameters: { 11 | // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout 12 | layout: 'fullscreen', 13 | }, 14 | } satisfies Meta; 15 | 16 | export default meta; 17 | type Story = StoryObj; 18 | 19 | export const LoggedIn: Story = { 20 | args: { 21 | user: { 22 | name: 'Jane Doe', 23 | }, 24 | onLogin: () => {}, 25 | onLogout: () => {}, 26 | onCreateAccount: () => {}, 27 | }, 28 | }; 29 | 30 | export const LoggedOut = {} as Story; 31 | -------------------------------------------------------------------------------- /src/stories/Header.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Button } from './Button'; 4 | import './header.scss'; 5 | 6 | type User = { 7 | name: string; 8 | }; 9 | 10 | interface HeaderProps { 11 | user?: User; 12 | onLogin: () => void; 13 | onLogout: () => void; 14 | onCreateAccount: () => void; 15 | } 16 | 17 | export const Header = ({ user, onLogin, onLogout, onCreateAccount }: HeaderProps) => ( 18 |
19 |
20 |
21 | 22 | 23 | 27 | 31 | 35 | 36 | 37 |

Acme

38 |
39 |
40 | {user ? ( 41 | <> 42 | 43 | Welcome, {user.name}! 44 | 45 |
54 |
55 |
56 | ); 57 | -------------------------------------------------------------------------------- /src/stories/Page.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | import { within, userEvent } from '@storybook/test'; 3 | 4 | import { Page } from './Page'; 5 | 6 | const meta = { 7 | title: 'Example/Page', 8 | component: Page, 9 | parameters: { 10 | // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout 11 | layout: 'fullscreen', 12 | }, 13 | } satisfies Meta; 14 | 15 | export default meta; 16 | type Story = StoryObj; 17 | 18 | export const LoggedOut: Story = {}; 19 | 20 | // More on interaction testing: https://storybook.js.org/docs/react/writing-tests/interaction-testing 21 | export const LoggedIn: Story = { 22 | play: async ({ canvasElement }) => { 23 | const canvas = within(canvasElement); 24 | const loginButton = await canvas.getByRole('button', { 25 | name: /Log in/i, 26 | }); 27 | await userEvent.click(loginButton); 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /src/stories/Page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Demo, REACT_TEST } from 'custom-package-name'; 3 | 4 | import { Header } from './Header'; 5 | import './page.css'; 6 | 7 | type User = { 8 | name: string; 9 | }; 10 | 11 | function Pkg() { 12 | return ( 13 |
14 | home 15 | {' '} 16 | 17 | { REACT_TEST.demo } 18 | 19 | 20 |
21 | ); 22 | } 23 | 24 | export const Page: React.FC = () => { 25 | const [user, setUser] = React.useState(); 26 | 27 | return ( 28 |
29 |
setUser({ name: 'Jane Doe' })} 32 | onLogout={() => setUser(undefined)} 33 | onCreateAccount={() => setUser({ name: 'Jane Doe' })} 34 | /> 35 | 36 |
37 |

Pages in Storybook

38 |

39 | We recommend building UIs with a{' '} 40 | 41 | component-driven 42 | {' '} 43 | process starting with atomic components and ending with pages. 44 |

45 |

46 | Render pages with mock data. This makes it easy to build and review page states without 47 | needing to navigate to them in your app. Here are some handy patterns for managing page 48 | data in Storybook: 49 |

50 |
    51 |
  • 52 | Use a higher-level connected component. Storybook helps you compose such data from the 53 | "args" of child component stories 54 |
  • 55 |
  • 56 | Assemble data in the page component from your services. You can mock these services out 57 | using Storybook. 58 |
  • 59 |
60 |

61 | Get a guided tutorial on component-driven development at{' '} 62 | 63 | Storybook tutorials 64 | 65 | . Read more in the{' '} 66 | 67 | docs 68 | 69 | . 70 |

71 |
72 | Tip Adjust the width of the canvas with the{' '} 73 | 74 | 75 | 80 | 81 | 82 | Viewports addon in the toolbar 83 |
84 | 85 |
86 |
87 | ); 88 | }; 89 | -------------------------------------------------------------------------------- /src/stories/Pkg.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Demo } from 'custom-package-name'; 2 | import React from 'react'; 3 | 4 | export default { 5 | title: 'UsePkg', 6 | }; 7 | 8 | export const Pkg = (): React.JSX.Element => ; 9 | -------------------------------------------------------------------------------- /src/stories/assets/accessibility.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codes-templates/npm-react/cd0bec5779d0aca7c63ebb6fb37eb24225594529/src/stories/assets/accessibility.png -------------------------------------------------------------------------------- /src/stories/assets/accessibility.svg: -------------------------------------------------------------------------------- 1 | 2 | Accessibility 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/stories/assets/addon-library.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codes-templates/npm-react/cd0bec5779d0aca7c63ebb6fb37eb24225594529/src/stories/assets/addon-library.png -------------------------------------------------------------------------------- /src/stories/assets/assets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codes-templates/npm-react/cd0bec5779d0aca7c63ebb6fb37eb24225594529/src/stories/assets/assets.png -------------------------------------------------------------------------------- /src/stories/assets/context.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codes-templates/npm-react/cd0bec5779d0aca7c63ebb6fb37eb24225594529/src/stories/assets/context.png -------------------------------------------------------------------------------- /src/stories/assets/discord.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/stories/assets/docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codes-templates/npm-react/cd0bec5779d0aca7c63ebb6fb37eb24225594529/src/stories/assets/docs.png -------------------------------------------------------------------------------- /src/stories/assets/figma-plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codes-templates/npm-react/cd0bec5779d0aca7c63ebb6fb37eb24225594529/src/stories/assets/figma-plugin.png -------------------------------------------------------------------------------- /src/stories/assets/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/stories/assets/share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codes-templates/npm-react/cd0bec5779d0aca7c63ebb6fb37eb24225594529/src/stories/assets/share.png -------------------------------------------------------------------------------- /src/stories/assets/styling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codes-templates/npm-react/cd0bec5779d0aca7c63ebb6fb37eb24225594529/src/stories/assets/styling.png -------------------------------------------------------------------------------- /src/stories/assets/testing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codes-templates/npm-react/cd0bec5779d0aca7c63ebb6fb37eb24225594529/src/stories/assets/testing.png -------------------------------------------------------------------------------- /src/stories/assets/theming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codes-templates/npm-react/cd0bec5779d0aca7c63ebb6fb37eb24225594529/src/stories/assets/theming.png -------------------------------------------------------------------------------- /src/stories/assets/tutorials.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/stories/assets/youtube.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/stories/button.less: -------------------------------------------------------------------------------- 1 | .storybook-button { 2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | font-weight: 700; 4 | border: 0; 5 | border-radius: 3em; 6 | cursor: pointer; 7 | display: inline-block; 8 | line-height: 1; 9 | } 10 | 11 | .storybook-button--primary { 12 | color: white; 13 | background-color: #1ea7fd; 14 | } 15 | 16 | .storybook-button--secondary { 17 | color: #333; 18 | background-color: transparent; 19 | box-shadow: rgba(0, 0, 0, 0.15) 0 0 0 1px inset; 20 | } 21 | 22 | .storybook-button--small { 23 | font-size: 12px; 24 | padding: 10px 16px; 25 | } 26 | 27 | .storybook-button--medium { 28 | font-size: 14px; 29 | padding: 11px 20px; 30 | } 31 | 32 | .storybook-button--large { 33 | font-size: 16px; 34 | padding: 12px 24px; 35 | } 36 | -------------------------------------------------------------------------------- /src/stories/header.scss: -------------------------------------------------------------------------------- 1 | .storybook-header { 2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | border-bottom: 1px solid rgba(0, 0, 0, 0.1); 4 | padding: 15px 20px; 5 | display: flex; 6 | align-items: center; 7 | justify-content: space-between; 8 | } 9 | 10 | .storybook-header svg { 11 | display: inline-block; 12 | vertical-align: top; 13 | } 14 | 15 | .storybook-header h1 { 16 | font-weight: 700; 17 | font-size: 20px; 18 | line-height: 1; 19 | margin: 6px 0 6px 10px; 20 | display: inline-block; 21 | vertical-align: top; 22 | } 23 | 24 | .storybook-header button + button { 25 | margin-left: 10px; 26 | } 27 | 28 | .storybook-header .welcome { 29 | color: #333; 30 | font-size: 14px; 31 | margin-right: 10px; 32 | } 33 | -------------------------------------------------------------------------------- /src/stories/page.css: -------------------------------------------------------------------------------- 1 | .storybook-page { 2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | font-size: 14px; 4 | line-height: 24px; 5 | padding: 48px 20px; 6 | margin: 0 auto; 7 | max-width: 600px; 8 | color: #333; 9 | } 10 | 11 | .storybook-page h2 { 12 | font-weight: 700; 13 | font-size: 32px; 14 | line-height: 1; 15 | margin: 0 0 4px; 16 | display: inline-block; 17 | vertical-align: top; 18 | } 19 | 20 | .storybook-page p { 21 | margin: 1em 0; 22 | } 23 | 24 | .storybook-page a { 25 | text-decoration: none; 26 | color: #1ea7fd; 27 | } 28 | 29 | .storybook-page ul { 30 | padding-left: 30px; 31 | margin: 1em 0; 32 | } 33 | 34 | .storybook-page li { 35 | margin-bottom: 8px; 36 | } 37 | 38 | .storybook-page .tip { 39 | display: inline-block; 40 | border-radius: 1em; 41 | font-size: 11px; 42 | line-height: 12px; 43 | font-weight: 700; 44 | background: #e7fdd8; 45 | color: #66bf3c; 46 | padding: 4px 12px; 47 | margin-right: 10px; 48 | vertical-align: top; 49 | } 50 | 51 | .storybook-page .tip-wrapper { 52 | font-size: 13px; 53 | line-height: 20px; 54 | margin-top: 40px; 55 | margin-bottom: 40px; 56 | } 57 | 58 | .storybook-page .tip-wrapper svg { 59 | display: inline-block; 60 | height: 12px; 61 | width: 12px; 62 | margin-right: 4px; 63 | vertical-align: top; 64 | margin-top: 3px; 65 | } 66 | 67 | .storybook-page .tip-wrapper svg path { 68 | fill: #1ea7fd; 69 | } 70 | -------------------------------------------------------------------------------- /src/styles/main.scss: -------------------------------------------------------------------------------- 1 | .demo { 2 | color: #333; 3 | user-select: none; 4 | } 5 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export interface TEST_REACT { 2 | demo: string; 3 | } 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "ESNext", 5 | "useDefineForClassFields": true, 6 | "module": "ESNext", 7 | "moduleResolution": "Node", 8 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 9 | "skipDefaultLibCheck": true, 10 | "noEmit": true, 11 | "sourceMap": true, 12 | "allowJs": true, 13 | "skipLibCheck": true, 14 | "strictNullChecks": true, 15 | "esModuleInterop": true, 16 | "allowSyntheticDefaultImports": true, 17 | "strict": true, 18 | "forceConsistentCasingInFileNames": true, 19 | "noUnusedLocals": true, 20 | "noFallthroughCasesInSwitch": true, 21 | "noImplicitReturns": false, 22 | "noImplicitThis": true, 23 | "noImplicitAny": false, 24 | "importHelpers": false, 25 | "resolveJsonModule": true, 26 | "isolatedModules": false, 27 | "jsx": "react", 28 | "paths": { 29 | "custom-package-name": [ 30 | "./src" 31 | ], 32 | "custom-package-name/*": [ 33 | "./src/*" 34 | ], 35 | "@site/*": ["./docs/*"] 36 | } 37 | }, 38 | "include": [ 39 | "**/*" 40 | ], 41 | "exclude": [ 42 | "node_modules", 43 | "build", 44 | "dist", 45 | "__unconfig_vite.config.ts" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup'; 2 | import babel from 'esbuild-plugin-babel'; 3 | 4 | // https://esbuild.github.io/ 5 | 6 | export default defineConfig({ 7 | name: 'tsup', 8 | entry: [ 9 | './src/index.ts', 10 | ], 11 | outExtension({ format }) { 12 | const extension = format === 'esm' ? '.mjs' : '.js'; 13 | return { 14 | js: extension, 15 | }; 16 | }, 17 | target: 'es6', 18 | format: [ 19 | 'cjs', 20 | 'esm', 21 | ], 22 | shims: false, 23 | clean: true, 24 | treeshake: true, 25 | dts: './src/index.ts', 26 | sourcemap: false, 27 | splitting: false, 28 | minify: true, 29 | esbuildPlugins: [ 30 | babel(), 31 | ], 32 | }); 33 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | import dts from 'vite-plugin-dts'; 5 | import path from 'path'; 6 | import { UserConfig, defineConfig, loadEnv } from 'vite'; 7 | import browserslistToEsbuild from 'browserslist-to-esbuild'; 8 | import { getBabelOutputPlugin } from '@rollup/plugin-babel'; 9 | import pkg from './package.json'; 10 | 11 | const resolvePath = (pathName: string) => path.resolve(__dirname, pathName); 12 | 13 | // vite config can not read babel.config.js🤣🤣🤣 14 | export default defineConfig(({ mode }: UserConfig ) => { 15 | const env = loadEnv(mode as string, process.cwd(), ''); 16 | 17 | return { 18 | resolve: { 19 | alias: { 20 | 'custom-package-name': resolvePath('./src/index.ts'), 21 | 'custom-package-name/': resolvePath('../src/'), 22 | }, 23 | }, 24 | build: { 25 | minify: true, 26 | lib: { 27 | fileName: (type) => { 28 | if (type === 'es') return 'index.mjs'; 29 | if (type === 'cjs') return 'index.js'; 30 | return 'index.js'; 31 | }, 32 | entry: resolvePath('src/index.ts'), 33 | formats: ['es', 'cjs'], 34 | }, 35 | target: browserslistToEsbuild(), 36 | sourcemap: false, 37 | rollupOptions: { 38 | plugins: [ 39 | // https://www.npmjs.com/package/@rollup/plugin-babel 40 | getBabelOutputPlugin({ 41 | configFile: path.resolve(__dirname, '.babelrc'), 42 | filename: '.babelrc', 43 | }), 44 | ], 45 | output: { 46 | exports: 'named', 47 | }, 48 | external: [ 49 | ...Object.keys(pkg.dependencies), // if exist 50 | ...Object.keys(pkg.devDependencies), 51 | ...Object.keys(pkg.peerDependencies), 52 | ], 53 | }, 54 | }, 55 | plugins: [ 56 | env?.NO_DTS !== '1' 57 | ? 58 | // https://www.npmjs.com/package/vite-plugin-dts 59 | dts({ 60 | include: 'src', 61 | exclude: ['src/stories/**/**', '**/*.stories.tsx'], 62 | rollupTypes: true, 63 | afterBuild: () => { 64 | // do something else 65 | }, 66 | }) 67 | : null, 68 | ], 69 | // https://github.com/vitest-dev/vitest 70 | test: { 71 | globals: true, 72 | environment: 'jsdom', 73 | setupFiles: ['./setupTests.ts'], 74 | transformMode: { 75 | web: [/.[tj]sx$/], 76 | }, 77 | }, 78 | } as UserConfig; 79 | }); 80 | --------------------------------------------------------------------------------