mirror of
https://github.com/ant-design/ant-design.git
synced 2026-02-14 21:39:20 +08:00
Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b30832cda1 | ||
|
|
c23ea0c5e6 | ||
|
|
0c1f181957 | ||
|
|
44d74415bc | ||
|
|
1c302163af | ||
|
|
4d0ac007ed | ||
|
|
282c792224 | ||
|
|
ba2ef53089 | ||
|
|
0792ac8762 | ||
|
|
aabf60586d | ||
|
|
f52003261f | ||
|
|
b0146a20cb | ||
|
|
68b948b088 | ||
|
|
bece43062b | ||
|
|
2e8205b01b | ||
|
|
b81e3e9af0 | ||
|
|
045059f604 | ||
|
|
a473770836 | ||
|
|
92b7902f4b | ||
|
|
9ee1a903a3 | ||
|
|
5e7a31d71f | ||
|
|
27c9c086b6 | ||
|
|
046f394c88 | ||
|
|
2ae5efa29f | ||
|
|
e85ec40c9a | ||
|
|
bb335bcc78 | ||
|
|
26dd6f70af | ||
|
|
91a5947ef2 | ||
|
|
be19e4e65f | ||
|
|
ef7bb3763e | ||
|
|
ac740d74fb | ||
|
|
e35663065c | ||
|
|
4c52401fd1 | ||
|
|
7c96796872 | ||
|
|
ac63d26f88 | ||
|
|
b22ef2280c | ||
|
|
831f0fcb5e | ||
|
|
4db7cbcf43 | ||
|
|
dd16a24426 | ||
|
|
f692e7ee34 | ||
|
|
fd553c9ba0 | ||
|
|
1b23971fd6 | ||
|
|
04ef6a8bce | ||
|
|
703a4b1011 | ||
|
|
a1c733ad07 | ||
|
|
9918140695 | ||
|
|
87cbe0168f | ||
|
|
eea0b028c9 | ||
|
|
88bed67368 | ||
|
|
f7e7ce9d4f | ||
|
|
ae7666f8fa | ||
|
|
3fb7325a21 | ||
|
|
59c29a400d | ||
|
|
d3102bb2db | ||
|
|
e036e4c9a3 | ||
|
|
da3540bd59 | ||
|
|
bba87d4125 | ||
|
|
6de8522894 | ||
|
|
674074ce93 | ||
|
|
c68f4463d3 | ||
|
|
a96a95ccb8 | ||
|
|
b112378de3 | ||
|
|
db4bec92be | ||
|
|
b6fc0168db | ||
|
|
92fe411961 | ||
|
|
ab29ecb377 | ||
|
|
23d5a856c0 | ||
|
|
6e4fa95d0f | ||
|
|
2753ce91c9 | ||
|
|
057f5b8526 | ||
|
|
d3eea22097 | ||
|
|
da2f54a18a | ||
|
|
e5dd231aaa | ||
|
|
c6eafe8380 |
@@ -1,20 +1,34 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const defaultVars = require('./scripts/default-vars');
|
||||
const darkVars = require('./scripts/dark-vars');
|
||||
const compactVars = require('./scripts/compact-vars');
|
||||
|
||||
function generateThemeFileContent(theme) {
|
||||
return `const { ${theme}ThemeSingle } = require('./theme');\nconst defaultTheme = require('./default-theme');\n
|
||||
module.exports = {
|
||||
...defaultTheme,
|
||||
...${theme}ThemeSingle
|
||||
}`;
|
||||
}
|
||||
const packageInfo = require('./package.json');
|
||||
|
||||
// We need compile additional content for antd user
|
||||
function finalizeCompile() {
|
||||
if (fs.existsSync(path.join(__dirname, './lib'))) {
|
||||
// Build package.json version to lib/version/index.js
|
||||
// prevent json-loader needing in user-side
|
||||
const versionFilePath = path.join(process.cwd(), 'lib', 'version', 'index.js');
|
||||
const versionFileContent = fs.readFileSync(versionFilePath).toString();
|
||||
fs.writeFileSync(
|
||||
versionFilePath,
|
||||
versionFileContent.replace(
|
||||
/require\(('|")\.\.\/\.\.\/package\.json('|")\)/,
|
||||
`{ version: '${packageInfo.version}' }`,
|
||||
),
|
||||
);
|
||||
// eslint-disable-next-line
|
||||
console.log('Wrote version into lib/version/index.js');
|
||||
|
||||
// Build package.json version to lib/version/index.d.ts
|
||||
// prevent https://github.com/ant-design/ant-design/issues/4935
|
||||
const versionDefPath = path.join(process.cwd(), 'lib', 'version', 'index.d.ts');
|
||||
fs.writeFileSync(
|
||||
versionDefPath,
|
||||
`declare var _default: "${packageInfo.version}";\nexport default _default;\n`,
|
||||
);
|
||||
// eslint-disable-next-line
|
||||
console.log('Wrote version into lib/version/index.d.ts');
|
||||
|
||||
// Build a entry less file to dist/antd.less
|
||||
const componentsPath = path.join(process.cwd(), 'components');
|
||||
let componentsLessContent = '';
|
||||
@@ -33,42 +47,6 @@ function finalizeCompile() {
|
||||
}
|
||||
}
|
||||
|
||||
function buildThemeFile(theme, vars) {
|
||||
// Build less entry file: dist/antd.${theme}.less
|
||||
if (theme !== 'default') {
|
||||
fs.writeFileSync(
|
||||
path.join(process.cwd(), 'dist', `antd.${theme}.less`),
|
||||
`@import "../lib/style/${theme}.less";\n@import "../lib/style/components.less";`,
|
||||
);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Built a entry less file to dist/antd.${theme}.less`);
|
||||
} else {
|
||||
fs.writeFileSync(
|
||||
path.join(process.cwd(), 'dist', `default-theme.js`),
|
||||
`module.exports = ${JSON.stringify(vars, null, 2)};\n`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Build ${theme}.js: dist/${theme}-theme.js, for less-loader
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(process.cwd(), 'dist', `theme.js`),
|
||||
`const ${theme}ThemeSingle = ${JSON.stringify(vars, null, 2)};\n`,
|
||||
{
|
||||
flag: 'a',
|
||||
},
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(process.cwd(), 'dist', `${theme}-theme.js`),
|
||||
generateThemeFileContent(theme),
|
||||
);
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Built a ${theme} theme js file to dist/${theme}-theme.js`);
|
||||
}
|
||||
|
||||
function finalizeDist() {
|
||||
if (fs.existsSync(path.join(__dirname, './dist'))) {
|
||||
// Build less entry file: dist/antd.less
|
||||
@@ -76,48 +54,9 @@ function finalizeDist() {
|
||||
path.join(process.cwd(), 'dist', 'antd.less'),
|
||||
'@import "../lib/style/index.less";\n@import "../lib/style/components.less";',
|
||||
);
|
||||
// eslint-disable-next-line no-console
|
||||
fs.writeFileSync(
|
||||
path.join(process.cwd(), 'dist', 'theme.js'),
|
||||
`const defaultTheme = require('./default-theme.js');\n`,
|
||||
);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Built a entry less file to dist/antd.less');
|
||||
buildThemeFile('default', defaultVars);
|
||||
buildThemeFile('dark', darkVars);
|
||||
buildThemeFile('compact', compactVars);
|
||||
fs.writeFileSync(
|
||||
path.join(process.cwd(), 'dist', `theme.js`),
|
||||
`
|
||||
function getThemeVariables(options = {}) {
|
||||
let themeVar = {
|
||||
'hack': \`true;@import "\${require.resolve('antd/lib/style/color/colorPalette.less')}";\`,
|
||||
...defaultTheme
|
||||
};
|
||||
if(options.dark) {
|
||||
themeVar = {
|
||||
...themeVar,
|
||||
...darkThemeSingle
|
||||
}
|
||||
}
|
||||
if(options.compact){
|
||||
themeVar = {
|
||||
...themeVar,
|
||||
...compactThemeSingle
|
||||
}
|
||||
}
|
||||
return themeVar;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
darkThemeSingle,
|
||||
compactThemeSingle,
|
||||
getThemeVariables
|
||||
}`,
|
||||
{
|
||||
flag: 'a',
|
||||
},
|
||||
);
|
||||
// eslint-disable-next-line
|
||||
console.log('Built a entry less file to dist/antd.less');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,5 +67,4 @@ module.exports = {
|
||||
dist: {
|
||||
finalize: finalizeDist,
|
||||
},
|
||||
generateThemeFileContent,
|
||||
};
|
||||
|
||||
258
.circleci/config.yml
Normal file
258
.circleci/config.yml
Normal file
@@ -0,0 +1,258 @@
|
||||
version: 2
|
||||
|
||||
references:
|
||||
container_config: &container_config
|
||||
docker:
|
||||
- image: circleci/node:lts
|
||||
working_directory: ~/ant-design
|
||||
|
||||
attach_workspace: &attach_workspace
|
||||
attach_workspace:
|
||||
at: ~/ant-design
|
||||
|
||||
install_react: &install_react
|
||||
run: REACT=15 ./scripts/install-react.sh
|
||||
|
||||
react_15: &react_15
|
||||
environment:
|
||||
REACT: 15
|
||||
|
||||
react_16: &react_16
|
||||
environment:
|
||||
REACT: 16
|
||||
|
||||
workflow: &workflow
|
||||
jobs:
|
||||
- setup:
|
||||
filters:
|
||||
branches:
|
||||
ignore: gh-pages
|
||||
- dist:
|
||||
requires:
|
||||
- setup
|
||||
- compile:
|
||||
requires:
|
||||
- setup
|
||||
- lint:
|
||||
requires:
|
||||
- setup
|
||||
- test_dist:
|
||||
requires:
|
||||
- dist
|
||||
- test_lib:
|
||||
requires:
|
||||
- compile
|
||||
- test_es:
|
||||
requires:
|
||||
- compile
|
||||
- test_dom:
|
||||
requires:
|
||||
- setup
|
||||
- test_node:
|
||||
requires:
|
||||
- setup
|
||||
- test_dist_15:
|
||||
requires:
|
||||
- dist
|
||||
- test_lib_15:
|
||||
requires:
|
||||
- compile
|
||||
- test_es_15:
|
||||
requires:
|
||||
- compile
|
||||
- test_dom_15:
|
||||
requires:
|
||||
- setup
|
||||
- test_node_15:
|
||||
requires:
|
||||
- setup
|
||||
- check_metadata:
|
||||
requires:
|
||||
- setup
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
<<: *container_config
|
||||
steps:
|
||||
- checkout
|
||||
- run: node -v
|
||||
- run: npm -v
|
||||
- run: npm install
|
||||
- run:
|
||||
command: |
|
||||
set +eo
|
||||
npm ls
|
||||
true
|
||||
- persist_to_workspace:
|
||||
root: ~/ant-design
|
||||
paths:
|
||||
- node_modules
|
||||
- store_artifacts:
|
||||
path: package-lock.json
|
||||
|
||||
dist:
|
||||
<<: *container_config
|
||||
steps:
|
||||
- checkout
|
||||
- *attach_workspace
|
||||
- run: npm run dist
|
||||
- run: node ./tests/dekko/dist.test.js
|
||||
- run: npm run bundlesize
|
||||
- persist_to_workspace:
|
||||
root: ~/ant-design
|
||||
paths:
|
||||
- dist
|
||||
|
||||
compile:
|
||||
<<: *container_config
|
||||
steps:
|
||||
- checkout
|
||||
- *attach_workspace
|
||||
- run: npm run compile
|
||||
- run: node ./tests/dekko/lib.test.js
|
||||
- persist_to_workspace:
|
||||
root: ~/ant-design
|
||||
paths:
|
||||
- lib
|
||||
- es
|
||||
|
||||
lint:
|
||||
<<: *container_config
|
||||
steps:
|
||||
- checkout
|
||||
- *attach_workspace
|
||||
- run: npm run lint
|
||||
|
||||
test_dist:
|
||||
<<: *container_config
|
||||
<<: *react_16
|
||||
steps:
|
||||
- checkout
|
||||
- *attach_workspace
|
||||
- run:
|
||||
command: npm test -- -w 1
|
||||
environment:
|
||||
LIB_DIR: dist
|
||||
|
||||
test_lib:
|
||||
<<: *container_config
|
||||
<<: *react_16
|
||||
steps:
|
||||
- checkout
|
||||
- *attach_workspace
|
||||
- run:
|
||||
command: npm test -- -w 1
|
||||
environment:
|
||||
LIB_DIR: lib
|
||||
|
||||
test_es:
|
||||
<<: *container_config
|
||||
<<: *react_16
|
||||
steps:
|
||||
- checkout
|
||||
- *attach_workspace
|
||||
- run:
|
||||
command: npm test -- -w 1
|
||||
environment:
|
||||
LIB_DIR: es
|
||||
|
||||
test_dom:
|
||||
<<: *container_config
|
||||
<<: *react_16
|
||||
steps:
|
||||
- checkout
|
||||
- *attach_workspace
|
||||
- run: npm test -- -w 1 --coverage
|
||||
- run: bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
test_node:
|
||||
<<: *container_config
|
||||
<<: *react_16
|
||||
steps:
|
||||
- checkout
|
||||
- *attach_workspace
|
||||
- run: npm run test-node -- -w 1
|
||||
|
||||
test_dist_15:
|
||||
<<: *container_config
|
||||
<<: *react_15
|
||||
steps:
|
||||
- checkout
|
||||
- *attach_workspace
|
||||
- *install_react
|
||||
- run:
|
||||
command: npm test -- -w 1 -u
|
||||
environment:
|
||||
LIB_DIR: dist
|
||||
REACT: 15
|
||||
|
||||
test_lib_15:
|
||||
<<: *container_config
|
||||
<<: *react_15
|
||||
steps:
|
||||
- checkout
|
||||
- *attach_workspace
|
||||
- *install_react
|
||||
- run:
|
||||
command: npm test -- -w 1 -u
|
||||
environment:
|
||||
LIB_DIR: lib
|
||||
REACT: 15
|
||||
|
||||
test_es_15:
|
||||
<<: *container_config
|
||||
<<: *react_15
|
||||
steps:
|
||||
- checkout
|
||||
- *attach_workspace
|
||||
- *install_react
|
||||
- run:
|
||||
command: npm test -- -w 1 -u
|
||||
environment:
|
||||
LIB_DIR: es
|
||||
REACT: 15
|
||||
|
||||
test_dom_15:
|
||||
<<: *container_config
|
||||
<<: *react_15
|
||||
steps:
|
||||
- checkout
|
||||
- *attach_workspace
|
||||
- *install_react
|
||||
- run:
|
||||
command: npm test -- -w 1 -u
|
||||
environment:
|
||||
REACT: 15
|
||||
|
||||
test_node_15:
|
||||
<<: *container_config
|
||||
<<: *react_15
|
||||
steps:
|
||||
- checkout
|
||||
- *attach_workspace
|
||||
- *install_react
|
||||
- run:
|
||||
command: npm run test-node -- -w 1 -u
|
||||
environment:
|
||||
REACT: 15
|
||||
|
||||
check_metadata:
|
||||
<<: *container_config
|
||||
steps:
|
||||
- checkout
|
||||
- *attach_workspace
|
||||
- run: node ./scripts/check-demo.js
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build_test:
|
||||
<<: *workflow
|
||||
nightly:
|
||||
<<: *workflow
|
||||
triggers:
|
||||
- schedule:
|
||||
cron: "0 0 * * *"
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
9
.codecov.yml
Normal file
9
.codecov.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
codecov:
|
||||
branch: master
|
||||
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
# Fail the status if coverage drops by >= 0.1%
|
||||
threshold: 0.1
|
||||
@@ -10,17 +10,9 @@ module.exports = {
|
||||
'**/*.json',
|
||||
],
|
||||
modulePattern: [
|
||||
{
|
||||
pattern: /ConfigContext.*renderEmpty/ms,
|
||||
module: '../empty',
|
||||
},
|
||||
{
|
||||
pattern: /ConfigConsumer.*renderEmpty/ms,
|
||||
module: '../empty',
|
||||
},
|
||||
{
|
||||
pattern: /config-provider\/context.*renderEmpty/ms,
|
||||
module: '../empty',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
node_modules/
|
||||
@@ -4,27 +4,12 @@ components/**/*.jsx
|
||||
!components/*/demo/*
|
||||
!.*.js
|
||||
# Docs templates
|
||||
site/theme/template/Color/ColorPicker.jsx
|
||||
site/theme/template/IconDisplay/*.js
|
||||
site/theme/template/IconDisplay/*.jsx
|
||||
site/theme/template/IconDisplay/fields.js
|
||||
site/theme/template/Home/**/*.jsx
|
||||
site/theme/template/utils.jsx
|
||||
site/theme/template/Layout/**/*.jsx
|
||||
site/theme/template/Layout/Footer.jsx
|
||||
site/theme/template/Content/Article.jsx
|
||||
site/theme/template/Content/EditButton.jsx
|
||||
site/theme/template/Resources/*.jsx
|
||||
site/theme/template/Resources/**/*.jsx
|
||||
site/theme/template/NotFound.jsx
|
||||
typings
|
||||
es/**/*
|
||||
lib/**/*
|
||||
node_modules
|
||||
_site
|
||||
dist
|
||||
coverage
|
||||
**/*.d.ts
|
||||
# Scripts
|
||||
scripts/previewEditor/**/*
|
||||
jest-stare
|
||||
|
||||
111
.eslintrc.js
111
.eslintrc.js
@@ -1,4 +1,4 @@
|
||||
module.exports = {
|
||||
const eslintrc = {
|
||||
extends: [
|
||||
'airbnb',
|
||||
'prettier',
|
||||
@@ -20,61 +20,23 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['markdown', 'react', 'babel', 'jest', '@typescript-eslint', 'react-hooks', 'unicorn'],
|
||||
plugins: ['markdown', 'react', 'babel', 'jest', '@typescript-eslint'],
|
||||
// https://github.com/typescript-eslint/typescript-eslint/issues/46#issuecomment-470486034
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': [2, { args: 'none' }],
|
||||
'no-unused-expressions': 'off',
|
||||
'@typescript-eslint/no-unused-expressions': 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.md'],
|
||||
globals: {
|
||||
React: true,
|
||||
ReactDOM: true,
|
||||
mountNode: true,
|
||||
},
|
||||
rules: {
|
||||
indent: 0,
|
||||
'no-console': 0,
|
||||
'no-plusplus': 0,
|
||||
'eol-last': 0,
|
||||
'no-script-url': 0,
|
||||
'prefer-rest-params': 0,
|
||||
'react/no-access-state-in-setstate': 0,
|
||||
'react/destructuring-assignment': 0,
|
||||
'react/no-multi-comp': 0,
|
||||
'jsx-a11y/href-no-hash': 0,
|
||||
'import/no-extraneous-dependencies': 0,
|
||||
'jsx-a11y/control-has-associated-label': 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
rules: {
|
||||
camelcase: 0,
|
||||
'react/jsx-one-expression-per-line': 0,
|
||||
'react/prop-types': 0,
|
||||
'react/forbid-prop-types': 0,
|
||||
'react/jsx-indent': 0,
|
||||
'react/jsx-wrap-multilines': ['error', { declaration: false, assignment: false }],
|
||||
'react/jsx-filename-extension': 0,
|
||||
'react/state-in-constructor': 0,
|
||||
'react/jsx-props-no-spreading': 0,
|
||||
'react/destructuring-assignment': 0, // TODO: remove later
|
||||
'react/require-default-props': 0,
|
||||
'react/sort-comp': 0,
|
||||
'react/display-name': 0,
|
||||
'react/static-property-placement': 0,
|
||||
'react/no-find-dom-node': 0,
|
||||
'react/no-unused-prop-types': 0,
|
||||
'react/default-props-match-prop-types': 0,
|
||||
'react-hooks/rules-of-hooks': 2, // Checks rules of Hooks
|
||||
|
||||
'import/extensions': 0,
|
||||
'import/no-cycle': 0,
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{
|
||||
@@ -94,43 +56,66 @@ module.exports = {
|
||||
'jsx-a11y/click-events-have-key-events': 0,
|
||||
'jsx-a11y/anchor-is-valid': 0,
|
||||
'jsx-a11y/no-noninteractive-element-interactions': 0,
|
||||
'comma-dangle': ['error', 'always-multiline'],
|
||||
'react/jsx-filename-extension': 0,
|
||||
'react/state-in-constructor': 0,
|
||||
'react/jsx-props-no-spreading': 0,
|
||||
'prefer-destructuring': 0, // TODO: remove later
|
||||
'consistent-return': 0, // TODO: remove later
|
||||
'no-return-assign': 0, // TODO: remove later
|
||||
'no-param-reassign': 0, // TODO: remove later
|
||||
'react/destructuring-assignment': 0, // TODO: remove later
|
||||
'react/no-did-update-set-state': 0, // TODO: remove later
|
||||
'react/require-default-props': 0,
|
||||
'react/default-props-match-prop-types': 0,
|
||||
'import/no-cycle': 0,
|
||||
'react/no-find-dom-node': 0,
|
||||
'no-underscore-dangle': 0,
|
||||
'react/sort-comp': 0,
|
||||
// label-has-for has been deprecated
|
||||
// https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/label-has-for.md
|
||||
'jsx-a11y/label-has-for': 0,
|
||||
|
||||
'comma-dangle': ['error', 'always-multiline'],
|
||||
'consistent-return': 0, // TODO: remove later
|
||||
'no-param-reassign': 0, // TODO: remove later
|
||||
'no-underscore-dangle': 0,
|
||||
// for (let i = 0; i < len; i++)
|
||||
'no-plusplus': 0,
|
||||
// https://eslint.org/docs/rules/no-continue
|
||||
// labeledLoop is conflicted with `eslint . --fix`
|
||||
'no-continue': 0,
|
||||
'react/display-name': 0,
|
||||
// ban this for Number.isNaN needs polyfill
|
||||
'no-restricted-globals': 0,
|
||||
'max-classes-per-file': 0,
|
||||
|
||||
'react/static-property-placement': 0,
|
||||
'jest/no-test-callback': 0,
|
||||
'jest/expect-expect': 0,
|
||||
'jest/no-done-callback': 0,
|
||||
'jest/valid-title': 0,
|
||||
'jest/no-conditional-expect': 0,
|
||||
|
||||
'unicorn/better-regex': 2,
|
||||
'unicorn/prefer-trim-start-end': 2,
|
||||
'unicorn/expiring-todo-comments': 2,
|
||||
'unicorn/no-abusive-eslint-disable': 2,
|
||||
|
||||
// https://github.com/typescript-eslint/typescript-eslint/issues/2540#issuecomment-692866111
|
||||
'no-use-before-define': 0,
|
||||
'@typescript-eslint/no-use-before-define': 2,
|
||||
'no-shadow': 0,
|
||||
'@typescript-eslint/no-shadow': [2, { ignoreTypeValueShadow: true }],
|
||||
// https://github.com/typescript-eslint/typescript-eslint/issues/2528#issuecomment-689369395
|
||||
'no-undef': 0,
|
||||
'import/extensions': 0,
|
||||
},
|
||||
globals: {
|
||||
gtag: true,
|
||||
},
|
||||
};
|
||||
|
||||
if (process.env.RUN_ENV === 'DEMO') {
|
||||
eslintrc.globals = Object.assign(eslintrc.globals, {
|
||||
React: true,
|
||||
ReactDOM: true,
|
||||
mountNode: true,
|
||||
});
|
||||
|
||||
Object.assign(eslintrc.rules, {
|
||||
indent: 0,
|
||||
'no-console': 0,
|
||||
'no-plusplus': 0,
|
||||
'eol-last': 0,
|
||||
'no-script-url': 0,
|
||||
'prefer-rest-params': 0,
|
||||
'react/no-access-state-in-setstate': 0,
|
||||
'react/destructuring-assignment': 0,
|
||||
'react/no-multi-comp': 0,
|
||||
'jsx-a11y/href-no-hash': 0,
|
||||
'import/no-extraneous-dependencies': 0,
|
||||
'import/no-unresolved': 0,
|
||||
'jsx-a11y/control-has-associated-label': 0,
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = eslintrc;
|
||||
|
||||
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@@ -4,6 +4,5 @@ github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, u
|
||||
open_collective: ant-design
|
||||
patreon: ant_design
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
issuehunt: ant-design/ant-design
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
custom: https://www.buymeacoffee.com/antdesign
|
||||
custom: # Replace with a single custom sponsorship URL
|
||||
|
||||
11
.github/ISSUE_TEMPLATE/----please-use-new-issue-ant-design---.md
vendored
Normal file
11
.github/ISSUE_TEMPLATE/----please-use-new-issue-ant-design---.md
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
name: '⚠️ Please use new-issue.ant.design ⚠️'
|
||||
about: The issue which is not created via http://new-issue.ant.design will be closed immediately.
|
||||
labels:
|
||||
---
|
||||
|
||||
The issue which is not created via http://new-issue.ant.design will be closed immediately.
|
||||
|
||||
---
|
||||
|
||||
注意:不是用 http://new-issue.ant.design 创建的 issue 会被立即关闭。
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +0,0 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Create new issue
|
||||
url: http://new-issue.ant.design
|
||||
about: The issue which is not created via http://new-issue.ant.design will be closed immediately.
|
||||
21
.github/PULL_REQUEST_TEMPLATE.md
vendored
21
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,25 +1,20 @@
|
||||
<!--
|
||||
First of all, thank you for your contribution! 😄
|
||||
|
||||
New feature please send a pull request to feature branch, and rest to master branch.
|
||||
Pull requests will be merged after one of the collaborators approve.
|
||||
Please makes sure that these forms are filled before submitting your pull request, thank you!
|
||||
-->
|
||||
New feature please send pull request to feature branch, and rest to master branch.
|
||||
Pull request will be merged after one of collaborators approve.
|
||||
Please makes sure that these form are filled before submitting your pull request, thank you!
|
||||
|
||||
[[中文版模板 / Chinese template](https://github.com/ant-design/ant-design/blob/master/.github/PULL_REQUEST_TEMPLATE/pr_cn.md)]
|
||||
-->
|
||||
|
||||
### 🤔 This is a ...
|
||||
|
||||
- [ ] New feature
|
||||
- [ ] Bug fix
|
||||
- [ ] Site / documentation update
|
||||
- [ ] Demo update
|
||||
- [ ] Site / document update
|
||||
- [ ] Component style update
|
||||
- [ ] TypeScript definition update
|
||||
- [ ] Bundle size optimization
|
||||
- [ ] Performance optimization
|
||||
- [ ] Enhancement feature
|
||||
- [ ] Internationalization
|
||||
- [ ] Refactoring
|
||||
- [ ] Code style optimization
|
||||
- [ ] Test Case
|
||||
@@ -37,13 +32,13 @@ Please makes sure that these forms are filled before submitting your pull reques
|
||||
<!--
|
||||
1. Describe the problem and the scenario.
|
||||
2. GIF or snapshot should be provided if includes UI/interactive modification.
|
||||
3. How to fix the problem, and list final API implementation and usage sample if that is a new feature.
|
||||
3. How to fix the problem, and list final API implementation and usage sample if that is an new feature.
|
||||
-->
|
||||
|
||||
### 📝 Changelog
|
||||
|
||||
<!--
|
||||
Describe changes from the user side, and list all potential break changes or other risks.
|
||||
Describe changes from userside, and list all potential break changes or other risks.
|
||||
--->
|
||||
|
||||
| Language | Changelog |
|
||||
@@ -53,8 +48,6 @@ Describe changes from the user side, and list all potential break changes or oth
|
||||
|
||||
### ☑️ Self Check before Merge
|
||||
|
||||
⚠️ Please check all items below before review. ⚠️
|
||||
|
||||
- [ ] Doc is updated/provided or not needed
|
||||
- [ ] Demo is updated/provided or not needed
|
||||
- [ ] TypeScript definition is updated/provided or not needed
|
||||
|
||||
21
.github/PULL_REQUEST_TEMPLATE/pr_cn.md
vendored
21
.github/PULL_REQUEST_TEMPLATE/pr_cn.md
vendored
@@ -2,24 +2,19 @@
|
||||
首先,感谢你的贡献!😄
|
||||
|
||||
新特性请提交至 feature 分支,其余可提交至 master 分支。
|
||||
在维护者审核通过后会合并。
|
||||
在一个维护者审核通过后合并。
|
||||
请确保填写以下 pull request 的信息,谢谢!~
|
||||
-->
|
||||
|
||||
[[English Template / 英文模板](https://github.com/ant-design/ant-design/blob/master/.github/PULL_REQUEST_TEMPLATE.md)]
|
||||
[[English Template / 英文模板](?expand=1)]
|
||||
-->
|
||||
|
||||
### 🤔 这个变动的性质是?
|
||||
|
||||
- [ ] 新特性提交
|
||||
- [ ] 日常 bug 修复
|
||||
- [ ] 站点、文档改进
|
||||
- [ ] 演示代码改进
|
||||
- [ ] 组件样式/交互改进
|
||||
- [ ] 组件样式改进
|
||||
- [ ] TypeScript 定义更新
|
||||
- [ ] 包体积优化
|
||||
- [ ] 性能优化
|
||||
- [ ] 功能增强
|
||||
- [ ] 国际化改进
|
||||
- [ ] 重构
|
||||
- [ ] 代码风格优化
|
||||
- [ ] 测试用例
|
||||
@@ -40,10 +35,10 @@
|
||||
3. 涉及UI/交互变动需要有截图或 GIF。
|
||||
-->
|
||||
|
||||
### 📝 更新日志
|
||||
### 📝 更新日志怎么写?
|
||||
|
||||
<!--
|
||||
从用户角度描述具体变化,以及可能的 breaking change 和其他风险。
|
||||
> 从用户角度描述具体变化,以及可能的 breaking change 和其他风险?
|
||||
-->
|
||||
|
||||
| 语言 | 更新描述 |
|
||||
@@ -51,9 +46,9 @@
|
||||
| 🇺🇸 英文 | |
|
||||
| 🇨🇳 中文 | |
|
||||
|
||||
### ☑️ 请求合并前的自查清单
|
||||
- 中文(可选):
|
||||
|
||||
⚠️ 请自检并全部**勾选全部选项**。⚠️
|
||||
### ☑️ 请求合并前的自查清单
|
||||
|
||||
- [ ] 文档已补充或无须补充
|
||||
- [ ] 代码演示已提供或无须提供
|
||||
|
||||
11
.github/dependabot.yml
vendored
11
.github/dependabot.yml
vendored
@@ -1,11 +0,0 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "npm" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "daily"
|
||||
3
.github/tests_checker.yml
vendored
3
.github/tests_checker.yml
vendored
@@ -1,3 +0,0 @@
|
||||
comment: 'Could you please add tests to make sure this change works as expected?',
|
||||
fileExtensions: [.ts', '.tsx', '.json']
|
||||
testDir: '__tests__'
|
||||
16
.github/workflows/auto-close.yml
vendored
16
.github/workflows/auto-close.yml
vendored
@@ -1,16 +0,0 @@
|
||||
name: 🧐 Auto Closer
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
|
||||
jobs:
|
||||
close-by-label:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.label.name == '3.x'
|
||||
steps:
|
||||
- name: Comment on issue
|
||||
uses: peter-evans/close-issue@v1
|
||||
with:
|
||||
comment: "Hi @${{ github.event.issue.user.login }},<br />Current branch is off the maintenance period. We may not accept pull request or fix bug with it anymore. This topic will be auto closed.<br /><br />你好 @${{ github.event.issue.user.login }},<br />当前分支已经过了维护期。我们不会再接受对其的相关 PR 与 issue。当前 topic 会被自动关闭。"
|
||||
|
||||
46
.github/workflows/codacy-analysis.yml
vendored
46
.github/workflows/codacy-analysis.yml
vendored
@@ -1,46 +0,0 @@
|
||||
# This workflow checks out code, performs a Codacy security scan
|
||||
# and integrates the results with the
|
||||
# GitHub Advanced Security code scanning feature. For more information on
|
||||
# the Codacy security scan action usage and parameters, see
|
||||
# https://github.com/codacy/codacy-analysis-cli-action.
|
||||
# For more information on Codacy Analysis CLI in general, see
|
||||
# https://github.com/codacy/codacy-analysis-cli.
|
||||
|
||||
name: Codacy Security Scan
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
codacy-security-scan:
|
||||
name: Codacy Security Scan
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Checkout the repository to the GitHub Actions runner
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
|
||||
- name: Run Codacy Analysis CLI
|
||||
uses: codacy/codacy-analysis-cli-action@1.1.0
|
||||
with:
|
||||
# Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
|
||||
# You can also omit the token and run the tools that support default configurations
|
||||
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
|
||||
verbose: true
|
||||
output: results.sarif
|
||||
format: sarif
|
||||
# Adjust severity of non-security issues
|
||||
gh-code-scanning-compat: true
|
||||
# Force 0 exit code to allow SARIF file generation
|
||||
# This will handover control about PR rejection to the GitHub side
|
||||
max-allowed-issues: 2147483647
|
||||
|
||||
# Upload the SARIF file generated in the previous step
|
||||
- name: Upload SARIF results file
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
54
.github/workflows/codeql-analysis.yml
vendored
54
.github/workflows/codeql-analysis.yml
vendored
@@ -1,54 +0,0 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master, 0.12-stable, 1.x-stable, 2.x-stable, 3.x-stable, feature, gh-pages]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [master]
|
||||
schedule:
|
||||
- cron: '0 13 * * 1'
|
||||
|
||||
jobs:
|
||||
analyse:
|
||||
name: Analyse
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
# with:
|
||||
# languages: go, javascript, csharp, python, cpp, java
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
20
.github/workflows/compressed-size.yml
vendored
20
.github/workflows/compressed-size.yml
vendored
@@ -1,20 +0,0 @@
|
||||
name: 📦 Compressed Size
|
||||
|
||||
on: [pull_request_target]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_JOB_NUMBER: 1
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
||||
- uses: preactjs/compressed-size-action@v2
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
pattern: "./dist/**/*.min.{js,css}"
|
||||
build-script: "dist"
|
||||
2
.github/workflows/deploy-site.yml
vendored
2
.github/workflows/deploy-site.yml
vendored
@@ -3,7 +3,7 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
branches:
|
||||
- master
|
||||
- 3.x-stable
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
|
||||
17
.github/workflows/gitleaks.yml
vendored
17
.github/workflows/gitleaks.yml
vendored
@@ -1,17 +0,0 @@
|
||||
name: gitleaks
|
||||
|
||||
on: [push,pull_request]
|
||||
|
||||
jobs:
|
||||
gitleaks:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: '1'
|
||||
- name: wget
|
||||
uses: wei/wget@v1
|
||||
with:
|
||||
args: -O .gitleaks.toml https://raw.githubusercontent.com/ycjcl868/gitleaks/master/.gitleaks.toml
|
||||
- name: gitleaks-action
|
||||
uses: zricethezav/gitleaks-action@master
|
||||
20
.github/workflows/label-merge.yml
vendored
20
.github/workflows/label-merge.yml
vendored
@@ -1,20 +0,0 @@
|
||||
name: Automatic Merge by label
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- labeled
|
||||
jobs:
|
||||
auto-merge:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.label.name == 'BranchAutoMerge' && github.event.pull_request.head.repo.full_name == 'ant-design/ant-design' && (github.event.pull_request.head.ref == 'master' || github.event.pull_request.head.ref == 'feature')
|
||||
steps:
|
||||
- name: Auto Approve
|
||||
uses: zombieJ/automerge-action@0.0.2
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
- name: Auto Merge
|
||||
uses: "pascalgn/automerge-action@v0.12.0"
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
MERGE_LABELS: "BranchAutoMerge"
|
||||
MERGE_REMOVE_LABELS: "BranchAutoMerge"
|
||||
26
.github/workflows/lighthouse-ci.yml
vendored
26
.github/workflows/lighthouse-ci.yml
vendored
@@ -1,15 +1,19 @@
|
||||
name: lighthouse ci
|
||||
|
||||
on: [push]
|
||||
|
||||
name: Lighthouse
|
||||
on: push
|
||||
jobs:
|
||||
lighthouseci:
|
||||
lighthouse:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2-beta
|
||||
- uses: actions/checkout@v1
|
||||
- name: Audit URLs using Lighthouse
|
||||
uses: treosh/lighthouse-ci-action@v2
|
||||
with:
|
||||
node-version: '14'
|
||||
- run: npm install && npm install -g @lhci/cli@0.4.x
|
||||
- run: npm run site
|
||||
- run: lhci autorun --upload.target=temporary-public-storage
|
||||
urls: |
|
||||
https://ant.design
|
||||
https://ant.design/docs/react/introduce-cn
|
||||
https://ant.design/components/button-cn
|
||||
- name: Save results
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: lighthouse-results
|
||||
path: '.lighthouseci' # This will save the Lighthouse results as .json files
|
||||
|
||||
14
.github/workflows/mirror.yml
vendored
14
.github/workflows/mirror.yml
vendored
@@ -1,14 +0,0 @@
|
||||
name: Mirror
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
to_gitee:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'ant-design/ant-design'
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: pixta-dev/repository-mirroring-action@v1
|
||||
with:
|
||||
target_repo_url: git@gitee.com:ant-design/ant-design.git
|
||||
ssh_private_key: ${{ secrets.GITEE_SSH_PRIVATE_KEY }}
|
||||
7
.github/workflows/rebase.yml
vendored
7
.github/workflows/rebase.yml
vendored
@@ -1,17 +1,14 @@
|
||||
on:
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
name: Automatic Rebase
|
||||
jobs:
|
||||
rebase:
|
||||
name: Rebase
|
||||
if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Automatic Rebase
|
||||
uses: cirrus-actions/rebase@1.3
|
||||
uses: cirrus-actions/rebase@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
13
.github/workflows/sync.yml
vendored
13
.github/workflows/sync.yml
vendored
@@ -1,13 +0,0 @@
|
||||
name: 🔄 Sync packages
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
jobs:
|
||||
sync_packages:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Sync packages
|
||||
uses: LexSwed/npm-package-sync@master
|
||||
with:
|
||||
registry-to: 'https://npm.pkg.github.com'
|
||||
token-to: ${{ secrets.GITHUB_TOKEN }}
|
||||
331
.github/workflows/test.yml
vendored
331
.github/workflows/test.yml
vendored
@@ -1,6 +1,6 @@
|
||||
name: ✅ test
|
||||
name: test
|
||||
|
||||
on: [push, pull_request_target]
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: cache package-lock.json
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
- name: create package-lock.json
|
||||
run: npm i --package-lock-only
|
||||
|
||||
- name: hack for single file
|
||||
- name: hack for singe file
|
||||
run: |
|
||||
if [ ! -d "package-temp-dir" ]; then
|
||||
mkdir package-temp-dir
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
|
||||
- name: cache node_modules
|
||||
id: node_modules_cache_id
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
@@ -43,25 +43,25 @@ jobs:
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: cache lib
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: lib
|
||||
key: lib-${{ github.sha }}
|
||||
|
||||
- name: cache es
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: es
|
||||
key: es-${{ github.sha }}
|
||||
@@ -73,6 +73,36 @@ jobs:
|
||||
run: node ./tests/dekko/lib.test.js
|
||||
needs: setup
|
||||
|
||||
dist:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: dist
|
||||
run: npm run dist
|
||||
|
||||
- name: check
|
||||
run: node ./tests/dekko/dist.test.js
|
||||
|
||||
- name: test
|
||||
run: npm test
|
||||
env:
|
||||
LIB_DIR: dist
|
||||
needs: setup
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -80,13 +110,13 @@ jobs:
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
@@ -95,98 +125,48 @@ jobs:
|
||||
run: npm run lint
|
||||
needs: setup
|
||||
|
||||
check_metadata:
|
||||
node:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: check demo
|
||||
run: node ./scripts/check-demo.js
|
||||
needs: setup
|
||||
|
||||
react-17-dom:
|
||||
name: react@17.x / dom
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: test
|
||||
run: npm test -- -w 1 --coverage
|
||||
|
||||
- name: coverage
|
||||
run: bash <(curl -s https://codecov.io/bash)
|
||||
run: npm test
|
||||
needs: setup
|
||||
|
||||
react-17-node:
|
||||
name: react@17.x / node
|
||||
lib:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: test
|
||||
run: npm run test-node
|
||||
needs: setup
|
||||
|
||||
react-17-lib:
|
||||
name: react@17.x / lib
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: restore cache from lib
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: lib
|
||||
key: lib-${{ github.sha }}
|
||||
@@ -197,27 +177,26 @@ jobs:
|
||||
LIB_DIR: lib
|
||||
needs: compile
|
||||
|
||||
react-17-es:
|
||||
name: react@17.x / es
|
||||
es:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: restore cache from es
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: es
|
||||
key: es-${{ github.sha }}
|
||||
@@ -227,207 +206,3 @@ jobs:
|
||||
env:
|
||||
LIB_DIR: es
|
||||
needs: compile
|
||||
|
||||
react-17-dist:
|
||||
name: react@17.x / dist
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: dist
|
||||
run: npm run dist
|
||||
env:
|
||||
NODE_OPTIONS: --max_old_space_size=4096
|
||||
|
||||
- name: check
|
||||
run: node ./tests/dekko/dist.test.js
|
||||
|
||||
- name: bundlesize
|
||||
run: npm run bundlesize
|
||||
env:
|
||||
BUNDLESIZE_GITHUB_TOKEN: ${{ secrets.BUNDLESIZE_GITHUB_TOKEN }}
|
||||
|
||||
- name: test
|
||||
run: npm test
|
||||
env:
|
||||
LIB_DIR: dist
|
||||
needs: setup
|
||||
|
||||
react-16-dom:
|
||||
name: react@16.x / dom
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
REACT: 16
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: install react 16
|
||||
run: npm run install-react-16
|
||||
|
||||
- name: test
|
||||
run: npm test -- -w 1 --coverage
|
||||
needs: setup
|
||||
|
||||
react-16-node:
|
||||
name: react@16.x / node
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
REACT: 16
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: install react 16
|
||||
run: npm run install-react-16
|
||||
|
||||
- name: test
|
||||
run: npm run test-node
|
||||
needs: setup
|
||||
|
||||
react-16-lib:
|
||||
name: react@16.x / lib
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
REACT: 16
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: restore cache from lib
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: lib
|
||||
key: lib-${{ github.sha }}
|
||||
|
||||
- name: install react 16
|
||||
run: npm run install-react-16
|
||||
|
||||
- name: test
|
||||
run: npm test
|
||||
env:
|
||||
LIB_DIR: lib
|
||||
needs: compile
|
||||
|
||||
react-16-es:
|
||||
name: react@16.x / es
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
REACT: 16
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: restore cache from es
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: es
|
||||
key: es-${{ github.sha }}
|
||||
|
||||
- name: install react 16
|
||||
run: npm run install-react-16
|
||||
|
||||
- name: test
|
||||
run: npm test
|
||||
env:
|
||||
LIB_DIR: es
|
||||
needs: compile
|
||||
|
||||
react-16-dist:
|
||||
name: react@16.x / dist
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
REACT: 16
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: install react 16
|
||||
run: npm run install-react-16
|
||||
|
||||
- name: dist
|
||||
run: npm run dist
|
||||
env:
|
||||
NODE_OPTIONS: --max_old_space_size=4096
|
||||
|
||||
- name: check
|
||||
run: node ./tests/dekko/dist.test.js
|
||||
|
||||
- name: test
|
||||
run: npm test
|
||||
env:
|
||||
LIB_DIR: dist
|
||||
needs: setup
|
||||
|
||||
36
.github/workflows/ui.yml
vendored
36
.github/workflows/ui.yml
vendored
@@ -1,36 +0,0 @@
|
||||
name: UI
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
if: github.event_name == 'pull_request_target'
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
ref: refs/pull/${{ github.event.pull_request.number }}/head
|
||||
|
||||
- name: checkout
|
||||
if: github.event_name == 'push'
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: install
|
||||
run: npm install
|
||||
|
||||
- name: test
|
||||
run: npm run test-image
|
||||
|
||||
- name: argos-ci
|
||||
# argos-ci only support base branch, so we should expect master
|
||||
if: github.event_name == 'pull_request_target' && github.base_ref == 'master'
|
||||
run: npm run argos -- --token ${{ secrets.ARGOS_TOKEN }} --branch pull/${{ github.event.pull_request.number }}/merge --commit ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: argos-ci
|
||||
if: github.event_name == 'push'
|
||||
run: npm run argos -- --token ${{ secrets.ARGOS_TOKEN }} --branch ${GITHUB_REF##*/} --commit ${{ github.sha }}
|
||||
24
.gitignore
vendored
24
.gitignore
vendored
@@ -25,7 +25,6 @@ nohup.out
|
||||
_site
|
||||
_data
|
||||
dist
|
||||
report.html
|
||||
/lib
|
||||
/es
|
||||
elasticsearch-*
|
||||
@@ -39,29 +38,8 @@ components/**/*.jsx
|
||||
!components/**/__tests__/**/*.js
|
||||
!components/**/__tests__/**/*.js.snap
|
||||
/.history
|
||||
*.tmp
|
||||
|
||||
# Docs templates
|
||||
site/theme/template/Color/ColorPicker.jsx
|
||||
site/theme/template/IconDisplay/*.js
|
||||
site/theme/template/IconDisplay/*.jsx
|
||||
site/theme/template/IconDisplay/fields.js
|
||||
site/theme/template/Home/**/*.jsx
|
||||
site/theme/template/utils.jsx
|
||||
site/theme/template/Layout/Footer.jsx
|
||||
site/theme/template/Layout/Header/**/*.jsx
|
||||
site/theme/template/Layout/SiteContext.jsx
|
||||
site/theme/template/Content/Article.jsx
|
||||
site/theme/template/Content/EditButton.jsx
|
||||
site/theme/template/Resources/*.jsx
|
||||
site/theme/template/Resources/**/*.jsx
|
||||
site/theme/template/NotFound.jsx
|
||||
scripts/previewEditor/index.html
|
||||
components/version/version.tsx
|
||||
|
||||
# Image snapshot diff
|
||||
__diff_output__/
|
||||
__image_snapshots__/
|
||||
/jest-stare
|
||||
/imageSnapshots
|
||||
/imageDiffSnapshots
|
||||
*.tmp
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
const { moduleNameMapper, transformIgnorePatterns } = require('./.jest');
|
||||
|
||||
// jest config for image snapshots
|
||||
module.exports = {
|
||||
setupFiles: ['./tests/setup.js'],
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'md'],
|
||||
moduleNameMapper,
|
||||
transform: {
|
||||
'\\.tsx?$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
|
||||
'\\.js$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
|
||||
'\\.md$': './node_modules/@ant-design/tools/lib/jest/demoPreprocessor',
|
||||
'\\.(jpg|png|gif|svg)$': './node_modules/@ant-design/tools/lib/jest/imagePreprocessor',
|
||||
},
|
||||
testRegex: 'image\\.test\\.(j|t)s$',
|
||||
transformIgnorePatterns,
|
||||
snapshotSerializers: ['enzyme-to-json/serializer'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsConfigFile: './tsconfig.test.json',
|
||||
},
|
||||
},
|
||||
preset: 'jest-puppeteer',
|
||||
testTimeout: 10000,
|
||||
};
|
||||
20
.jest.js
20
.jest.js
@@ -1,21 +1,13 @@
|
||||
const libDir = process.env.LIB_DIR;
|
||||
|
||||
const transformIgnorePatterns = [
|
||||
'/dist/',
|
||||
// Ignore modules without es dir.
|
||||
// Update: @babel/runtime should also be transformed
|
||||
'node_modules/(?!.*@(babel|ant-design))[^/]+?/(?!(es|node_modules)/)',
|
||||
'node_modules/[^/]+?/(?!(es|node_modules)/)', // Ignore modules without es dir
|
||||
];
|
||||
|
||||
function getTestRegex(libDir) {
|
||||
if (libDir === 'dist') {
|
||||
return 'demo\\.test\\.js$';
|
||||
}
|
||||
return '.*\\.test\\.(j|t)sx?$';
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
verbose: true,
|
||||
setupFiles: ['./tests/setup.js'],
|
||||
setupFilesAfterEnv: ['./tests/setupAfterEnv.ts'],
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'md'],
|
||||
modulePathIgnorePatterns: ['/_site/'],
|
||||
moduleNameMapper: {
|
||||
@@ -25,16 +17,15 @@ module.exports = {
|
||||
'^react-dnd-touch-backend$': 'react-dnd-touch-backend/dist/cjs',
|
||||
'^react-dnd-test-backend$': 'react-dnd-test-backend/dist/cjs',
|
||||
'^react-dnd-test-utils$': 'react-dnd-test-utils/dist/cjs',
|
||||
'\\.(css|less)$': 'identity-obj-proxy',
|
||||
},
|
||||
testPathIgnorePatterns: ['/node_modules/', 'dekko', 'node', 'image.test.js', 'image.test.ts'],
|
||||
testPathIgnorePatterns: ['/node_modules/', 'dekko', 'node'],
|
||||
transform: {
|
||||
'\\.tsx?$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
|
||||
'\\.js$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
|
||||
'\\.md$': './node_modules/@ant-design/tools/lib/jest/demoPreprocessor',
|
||||
'\\.(jpg|png|gif|svg)$': './node_modules/@ant-design/tools/lib/jest/imagePreprocessor',
|
||||
},
|
||||
testRegex: getTestRegex(process.env.LIB_DIR),
|
||||
testRegex: `${libDir === 'dist' ? 'demo' : '.*'}\\.test\\.js$`,
|
||||
collectCoverageFrom: [
|
||||
'components/**/*.{ts,tsx}',
|
||||
'!components/*/style/index.tsx',
|
||||
@@ -42,7 +33,6 @@ module.exports = {
|
||||
'!components/*/locale/index.tsx',
|
||||
'!components/*/__tests__/type.test.tsx',
|
||||
'!components/**/*/interface.{ts,tsx}',
|
||||
'!components/*/__tests__/image.test.{ts,tsx}',
|
||||
],
|
||||
transformIgnorePatterns,
|
||||
snapshotSerializers: ['enzyme-to-json/serializer'],
|
||||
|
||||
@@ -3,7 +3,6 @@ const { moduleNameMapper, transformIgnorePatterns } = require('./.jest');
|
||||
// jest config for server render environment
|
||||
module.exports = {
|
||||
setupFiles: ['./tests/setup.js'],
|
||||
setupFilesAfterEnv: ['./tests/setupAfterEnv.ts'],
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'md'],
|
||||
moduleNameMapper,
|
||||
transform: {
|
||||
@@ -12,8 +11,13 @@ module.exports = {
|
||||
'\\.md$': './node_modules/@ant-design/tools/lib/jest/demoPreprocessor',
|
||||
'\\.(jpg|png|gif|svg)$': './node_modules/@ant-design/tools/lib/jest/imagePreprocessor',
|
||||
},
|
||||
testRegex: 'demo\\.test\\.(j|t)s$',
|
||||
testRegex: 'demo\\.test\\.js$',
|
||||
testEnvironment: 'node',
|
||||
transformIgnorePatterns,
|
||||
snapshotSerializers: ['enzyme-to-json/serializer'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsConfigFile: './tsconfig.test.json',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
**/*.png
|
||||
**/*.svg
|
||||
CODEOWNERS
|
||||
.dockerignore
|
||||
Dockerfile.ui-test
|
||||
package.json
|
||||
.umi
|
||||
.umi-production
|
||||
@@ -21,8 +17,6 @@ yarn-error.log
|
||||
*.snap
|
||||
components/*/*.js
|
||||
components/*/*.jsx
|
||||
components/*/*.md
|
||||
docs/**/*.md
|
||||
.gitignore
|
||||
.npmignore
|
||||
.prettierignore
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"trailingComma": "all",
|
||||
"printWidth": 100,
|
||||
"proseWrap": "never",
|
||||
"arrowParens": "avoid",
|
||||
"overrides": [
|
||||
{
|
||||
"files": ".prettierrc",
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
const config = {
|
||||
plugins: [
|
||||
'remark-preset-lint-recommended',
|
||||
['remark-lint-list-item-indent', 'space'],
|
||||
['remark-lint-no-literal-urls', false],
|
||||
],
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
31
.travis.yml
Normal file
31
.travis.yml
Normal file
@@ -0,0 +1,31 @@
|
||||
sudo: false
|
||||
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- 11
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.npm
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- env: TEST_TYPE=lint
|
||||
- env: REACT=16 TEST_TYPE=test:dist
|
||||
- env: REACT=16 TEST_TYPE=test:lib
|
||||
- env: REACT=16 TEST_TYPE=test:es
|
||||
- env: REACT=16 TEST_TYPE=test:dom
|
||||
- env: REACT=16 TEST_TYPE=test:node
|
||||
- env: REACT=15 TEST_TYPE=test:dist
|
||||
- env: REACT=15 TEST_TYPE=test:lib
|
||||
- env: REACT=15 TEST_TYPE=test:es
|
||||
- env: REACT=15 TEST_TYPE=test:dom
|
||||
- env: REACT=15 TEST_TYPE=test:node
|
||||
|
||||
before_script:
|
||||
- scripts/install-react.sh
|
||||
|
||||
script:
|
||||
- scripts/travis-script.sh
|
||||
508
AUTHORS.txt
508
AUTHORS.txt
File diff suppressed because it is too large
Load Diff
4319
CHANGELOG.en-US.md
4319
CHANGELOG.en-US.md
File diff suppressed because it is too large
Load Diff
4324
CHANGELOG.zh-CN.md
4324
CHANGELOG.zh-CN.md
File diff suppressed because it is too large
Load Diff
@@ -34,7 +34,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [xingmin.zhu@alipay.com.](mailto:xingmin.zhu@alipay.com.) The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at xingmin.zhu@alipay.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
FROM buildkite/puppeteer:v3.0.4
|
||||
RUN mkdir /app
|
||||
WORKDIR /app
|
||||
COPY package.json ./
|
||||
ENV PATH="${PATH}:/app/node_modules/.bin"
|
||||
COPY . .
|
||||
164
README-pt_BR.md
164
README-pt_BR.md
@@ -1,164 +0,0 @@
|
||||
<p align="center">
|
||||
<a href="https://ant.design">
|
||||
<img width="200" src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h1 align="center">Ant Design</h1>
|
||||
|
||||
<div align="center">
|
||||
|
||||
Uma solução empresarial de design e biblioteca UI para React.
|
||||
|
||||
[![CI status][github-action-image]][github-action-url] [![codecov][codecov-image]][codecov-url] [![NPM version][npm-image]][npm-url] [![NPM downloads][download-image]][download-url]
|
||||
|
||||
[![david deps][david-image]][david-url] [![david devDeps][david-dev-image]][david-dev-url] [![Total alerts][lgtm-image]][lgtm-url] [![FOSSA Status][fossa-image]][fossa-url] [![Issues need help][help-wanted-image]][help-wanted-url]
|
||||
|
||||
[![Follow Twitter][twitter-image]][twitter-url] [![Gitter][gitter-english-image]][gitter-english-url] [![Gitter][gitter-chinese-image]][gitter-chinese-url] [![[SemVer stability]][semver-stability-image]][semver-stability-url]
|
||||
|
||||
[npm-image]: http://img.shields.io/npm/v/antd.svg?style=flat-square
|
||||
[npm-url]: http://npmjs.org/package/antd
|
||||
[github-action-image]: https://github.com/ant-design/ant-design/workflows/test/badge.svg
|
||||
[github-action-url]: https://github.com/ant-design/ant-design/actions?query=workflow%3Atest
|
||||
[codecov-image]: https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square
|
||||
[codecov-url]: https://codecov.io/gh/ant-design/ant-design/branch/master
|
||||
[david-image]: https://img.shields.io/david/ant-design/ant-design?style=flat-square
|
||||
[david-dev-url]: https://david-dm.org/ant-design/ant-design?type=dev
|
||||
[david-dev-image]: https://img.shields.io/david/dev/ant-design/ant-design?style=flat-square
|
||||
[david-url]: https://david-dm.org/ant-design/ant-design
|
||||
[download-image]: https://img.shields.io/npm/dm/antd.svg?style=flat-square
|
||||
[download-url]: https://npmjs.org/package/antd
|
||||
[lgtm-image]: https://flat.badgen.net/lgtm/alerts/g/ant-design/ant-design
|
||||
[lgtm-url]: https://lgtm.com/projects/g/ant-design/ant-design/alerts/
|
||||
[fossa-image]: https://app.fossa.io/api/projects/git%2Bgithub.com%2Fant-design%2Fant-design.svg?type=shield
|
||||
[fossa-url]: https://app.fossa.io/projects/git%2Bgithub.com%2Fant-design%2Fant-design?ref=badge_shield
|
||||
[help-wanted-image]: https://flat.badgen.net/github/label-issues/ant-design/ant-design/help%20wanted/open
|
||||
[help-wanted-url]: https://github.com/ant-design/ant-design/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22
|
||||
[twitter-image]: https://img.shields.io/twitter/follow/AntDesignUI.svg?label=Ant%20Design&style=social
|
||||
[twitter-url]: https://twitter.com/AntDesignUI
|
||||
[gitter-english-image]: https://img.shields.io/gitter/room/ant-design/ant-design-english.svg?style=flat-square&logoWidth=18&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjEyMzUiIGhlaWdodD0iNjUwIiB2aWV3Qm94PSIwIDAgNzQxMCAzOTAwIj4NCjxyZWN0IHdpZHRoPSI3NDEwIiBoZWlnaHQ9IjM5MDAiIGZpbGw9IiNiMjIyMzQiLz4NCjxwYXRoIGQ9Ik0wLDQ1MEg3NDEwbTAsNjAwSDBtMCw2MDBINzQxMG0wLDYwMEgwbTAsNjAwSDc0MTBtMCw2MDBIMCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjMwMCIvPg0KPHJlY3Qgd2lkdGg9IjI5NjQiIGhlaWdodD0iMjEwMCIgZmlsbD0iIzNjM2I2ZSIvPg0KPGcgZmlsbD0iI2ZmZiI%2BDQo8ZyBpZD0iczE4Ij4NCjxnIGlkPSJzOSI%2BDQo8ZyBpZD0iczUiPg0KPGcgaWQ9InM0Ij4NCjxwYXRoIGlkPSJzIiBkPSJNMjQ3LDkwIDMxNy41MzQyMzAsMzA3LjA4MjAzOSAxMzIuODczMjE4LDE3Mi45MTc5NjFIMzYxLjEyNjc4MkwxNzYuNDY1NzcwLDMwNy4wODIwMzl6Ii8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB5PSI0MjAiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHk9Ijg0MCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTI2MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTY4MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjczQiIHg9IjI0NyIgeT0iMjEwIi8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzOSIgeD0iNDk0Ii8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzMTgiIHg9Ijk4OCIvPg0KPHVzZSB4bGluazpocmVmPSIjczkiIHg9IjE5NzYiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3M1IiB4PSIyNDcwIi8%2BDQo8L2c%2BDQo8L3N2Zz4%3D
|
||||
[gitter-english-url]: https://gitter.im/ant-design/ant-design-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
|
||||
[gitter-chinese-image]: https://img.shields.io/gitter/room/ant-design/ant-design.svg?style=flat-square&logoWidth=18&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjkwMCIgaGVpZ2h0PSI2MDAiIHZpZXdCb3g9IjAgMCAzMCAyMCI%2BDQo8ZGVmcz4NCjxwYXRoIGlkPSJzIiBkPSJNMCwtMSAwLjU4Nzc4NSwwLjgwOTAxNyAtMC45NTEwNTcsLTAuMzA5MDE3SDAuOTUxMDU3TC0wLjU4Nzc4NSwwLjgwOTAxN3oiIGZpbGw9IiNmZmRlMDAiLz4NCjwvZGVmcz4NCjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIyMCIgZmlsbD0iI2RlMjkxMCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw1KSBzY2FsZSgzKSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsMikgcm90YXRlKDIzLjAzNjI0MykiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyLDQpIHJvdGF0ZSg0NS44Njk4OTgpIi8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMiw3KSByb3RhdGUoNjkuOTQ1Mzk2KSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsOSkgcm90YXRlKDIwLjY1OTgwOCkiLz4NCjwvc3ZnPg%3D%3D
|
||||
[gitter-chinese-url]: https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
[semver-stability-url]: https://dependabot.com/compatibility-score.html/?dependency-name=antd&package-manager=npm_and_yarn&new-version=latest
|
||||
[semver-stability-image]: https://api.dependabot.com/badges/compatibility_score?dependency-name=antd&package-manager=npm_and_yarn&target-version=latest&version-scheme=semver
|
||||
|
||||
</div>
|
||||
|
||||
[](https://ant.design)
|
||||
|
||||
[English](./README.md) | Português | [简体中文](./README-zh_CN.md)
|
||||
|
||||
## ✨ Funcionalidades
|
||||
|
||||
- 🌈 Design empresarial de interface para aplicações web.
|
||||
- 📦 Um conjunto de alta qualidade, componentes React prontos para uso.
|
||||
- 🛡 Escrito em TypeScript com tipos previsíveis.
|
||||
- ⚙️ Pacote completo de recursos de design e ferramentas de desenvolvimento.
|
||||
- 🌍 Suporte de internacionalização para dezenas de idiomas.
|
||||
- 🎨 Personalização poderosa do tema em todos os detalhes.
|
||||
|
||||
## 🖥 Suporte aos ambientes
|
||||
|
||||
- Navegadores modernos e Internet Explorer 11 (com [polyfills](https://ant.design/docs/react/getting-started#Compatibility))
|
||||
- Renderização no lado do servidor (server-side)
|
||||
- [Electron](https://www.electronjs.org/)
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Electron |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| IE11, Edge | últimas 2 versões | últimas 2 versões | últimas 2 versões | últimas 2 versões | últimas 2 versões |
|
||||
|
||||
## 📦 Instalação
|
||||
|
||||
```bash
|
||||
npm install antd
|
||||
```
|
||||
|
||||
```bash
|
||||
yarn add antd
|
||||
```
|
||||
|
||||
## 🔨 Uso
|
||||
|
||||
```jsx
|
||||
import { Button, DatePicker } from 'antd';
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<Button type="primary">PRESS ME</Button>
|
||||
<DatePicker />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
Importe o estilo manualmente:
|
||||
|
||||
```jsx
|
||||
import 'antd/dist/antd.css'; // ou 'antd/dist/antd.less'
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
Veja [Uso no Typescript](https://ant.design/docs/react/use-in-typescript).
|
||||
|
||||
## 🌍 Internacionalização
|
||||
|
||||
Veja [i18n](https://ant.design/docs/react/i18n).
|
||||
|
||||
## 🔗 Links
|
||||
|
||||
- [Página inicial](https://ant.design/)
|
||||
- [Componentes](https://ant.design/components/overview)
|
||||
- [Ant Design Pro](http://pro.ant.design/)
|
||||
- [Ant Design Charts](https://charts.ant.design)
|
||||
- [Change Log](CHANGELOG.en-US.md)
|
||||
- [rc-components](http://react-component.github.io/)
|
||||
- [Mobile UI](http://mobile.ant.design)
|
||||
- [Ant Design Icones](https://github.com/ant-design/ant-design-icons)
|
||||
- [Ant Design Cores](https://github.com/ant-design/ant-design-colors)
|
||||
- [Ant Design Pro Layout](https://github.com/ant-design/ant-design-pro-layout)
|
||||
- [Ant Design Pro Blocks](https://github.com/ant-design/pro-blocks)
|
||||
- [Tema escuro](https://github.com/ant-design/ant-design-dark-theme)
|
||||
- [Página de aterrissagem](https://landing.ant.design)
|
||||
- [Motion](https://motion.ant.design)
|
||||
- [Mercado de páginas](http://scaffold.ant.design)
|
||||
- [Instruções ao desenvolvedor](https://github.com/ant-design/ant-design/wiki/Development)
|
||||
- [Versionando as notas de atualização](https://github.com/ant-design/ant-design/wiki/%E8%BD%AE%E5%80%BC%E8%A7%84%E5%88%99%E5%92%8C%E7%89%88%E6%9C%AC%E5%8F%91%E5%B8%83%E6%B5%81%E7%A8%8B)
|
||||
- [FAQ](https://ant.design/docs/react/faq)
|
||||
- [CodeSandbox Template](https://u.ant.design/codesandbox-repro) para relatório de erros
|
||||
- [Awesome Ant Design](https://github.com/websemantics/awesome-ant-design)
|
||||
- [Customize Theme](https://ant.design/docs/react/customize-theme)
|
||||
- [How to Apply for Being A Collaborator](https://github.com/ant-design/ant-design/wiki/Collaborators#how-to-apply-for-being-a-collaborator)
|
||||
|
||||
## ⌨️ Desenvolvimento
|
||||
|
||||
Use Gitpod, um ambiente de desenvolvimento online para GitHub.
|
||||
|
||||
[](https://gitpod.io/#https://github.com/ant-design/ant-design)
|
||||
|
||||
Ou clone localmente:
|
||||
|
||||
```bash
|
||||
$ git clone git@github.com:ant-design/ant-design.git
|
||||
$ cd ant-design
|
||||
$ npm install
|
||||
$ npm start
|
||||
```
|
||||
|
||||
Abra seu navegador e visite http://127.0.0.1:8001, veja mais em [Desenvolvimento](https://github.com/ant-design/ant-design/wiki/Development).
|
||||
|
||||
## 🤝 Contribuição [](http://makeapullrequest.com)
|
||||
|
||||
Leia nosso [guia de contribução](https://ant.design/docs/react/contributing) e vamos contruir um melhor antd juntos.
|
||||
|
||||
Nós saudamos todas as contribuições. Por favor, leia nosso [CONTRIBUTING.md](https://github.com/ant-design/ant-design/blob/master/.github/CONTRIBUTING.md) primeiro. Você pode submeter todas as ideias como [Pull Requests](https://github.com/ant-design/ant-design/pulls) ou como [GitHub issues](https://github.com/ant-design/ant-design/issues). Se você quiser melhorar o código, verifique [instruções ao desenvolvedor](https://github.com/ant-design/ant-design/wiki/Development) e divirta-se! :)
|
||||
|
||||
Se você é um colaborador, por favor siga nossa [Pull Request princípio](https://github.com/ant-design/ant-design/wiki/PR-principle) para criar um Pull Request através do [template do colaborador](https://github.com/ant-design/ant-design/compare?expand=1&template=collaborator.md).
|
||||
|
||||
[](https://issuehunt.io/repos/34526884)
|
||||
|
||||
## ❤️ Patrocionadores e Apoiadores [](https://opencollective.com/ant-design#support) [](https://opencollective.com/ant-design#support)
|
||||
|
||||
[](https://opencollective.com/ant-design#support)
|
||||
|
||||
[](https://opencollective.com/ant-design#support)
|
||||
@@ -1,5 +1,5 @@
|
||||
<p align="center">
|
||||
<a href="https://ant.design">
|
||||
<a href="http://ant.design">
|
||||
<img width="200" src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg">
|
||||
</a>
|
||||
</p>
|
||||
@@ -10,44 +10,17 @@
|
||||
|
||||
一套企业级 UI 设计语言和 React 组件库。
|
||||
|
||||
[![CI status][github-action-image]][github-action-url] [![codecov][codecov-image]][codecov-url] [![NPM version][npm-image]][npm-url] [![NPM downloads][download-image]][download-url]
|
||||
[](https://circleci.com/gh/ant-design/ant-design)  [](https://codecov.io/gh/ant-design/ant-design/branch/master) [](https://www.npmjs.com/package/antd) [](https://www.npmjs.com/package/antd) [](http://npmjs.com/antd)
|
||||
|
||||
[![david deps][david-image]][david-url] [![david devDeps][david-dev-image]][david-dev-url] [![Total alerts][lgtm-image]][lgtm-url] [![FOSSA Status][fossa-image]][fossa-url] [![Issues need help][help-wanted-image]][help-wanted-url]
|
||||
[](https://david-dm.org/ant-design/ant-design) [](https://david-dm.org/ant-design/ant-design?type=dev) [](https://lgtm.com/projects/g/ant-design/ant-design/alerts/) [](https://app.fossa.io/projects/git%2Bgithub.com%2Fant-design%2Fant-design?ref=badge_shield) [](https://github.com/ant-design/ant-design/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)
|
||||
|
||||
[![Follow Twitter][twitter-image]][twitter-url] [![Gitter][gitter-english-image]][gitter-english-url] [![Gitter][gitter-chinese-image]][gitter-chinese-url] [![[SemVer stability]][semver-stability-image]][semver-stability-url]
|
||||
|
||||
[npm-image]: http://img.shields.io/npm/v/antd.svg?style=flat-square
|
||||
[npm-url]: http://npmjs.org/package/antd
|
||||
[github-action-image]: https://github.com/ant-design/ant-design/workflows/test/badge.svg
|
||||
[github-action-url]: https://github.com/ant-design/ant-design/actions?query=workflow%3Atest
|
||||
[codecov-image]: https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square
|
||||
[codecov-url]: https://codecov.io/gh/ant-design/ant-design/branch/master
|
||||
[david-image]: https://img.shields.io/david/ant-design/ant-design?style=flat-square
|
||||
[david-dev-url]: https://david-dm.org/ant-design/ant-design?type=dev
|
||||
[david-dev-image]: https://img.shields.io/david/dev/ant-design/ant-design?style=flat-square
|
||||
[david-url]: https://david-dm.org/ant-design/ant-design
|
||||
[download-image]: https://img.shields.io/npm/dm/antd.svg?style=flat-square
|
||||
[download-url]: https://npmjs.org/package/antd
|
||||
[lgtm-image]: https://flat.badgen.net/lgtm/alerts/g/ant-design/ant-design
|
||||
[lgtm-url]: https://lgtm.com/projects/g/ant-design/ant-design/alerts/
|
||||
[fossa-image]: https://app.fossa.io/api/projects/git%2Bgithub.com%2Fant-design%2Fant-design.svg?type=shield
|
||||
[fossa-url]: https://app.fossa.io/projects/git%2Bgithub.com%2Fant-design%2Fant-design?ref=badge_shield
|
||||
[help-wanted-image]: https://flat.badgen.net/github/label-issues/ant-design/ant-design/help%20wanted/open
|
||||
[help-wanted-url]: https://github.com/ant-design/ant-design/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22
|
||||
[twitter-image]: https://img.shields.io/twitter/follow/AntDesignUI.svg?label=Ant%20Design&style=social
|
||||
[twitter-url]: https://twitter.com/AntDesignUI
|
||||
[gitter-english-image]: https://img.shields.io/gitter/room/ant-design/ant-design-english.svg?style=flat-square&logoWidth=18&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjEyMzUiIGhlaWdodD0iNjUwIiB2aWV3Qm94PSIwIDAgNzQxMCAzOTAwIj4NCjxyZWN0IHdpZHRoPSI3NDEwIiBoZWlnaHQ9IjM5MDAiIGZpbGw9IiNiMjIyMzQiLz4NCjxwYXRoIGQ9Ik0wLDQ1MEg3NDEwbTAsNjAwSDBtMCw2MDBINzQxMG0wLDYwMEgwbTAsNjAwSDc0MTBtMCw2MDBIMCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjMwMCIvPg0KPHJlY3Qgd2lkdGg9IjI5NjQiIGhlaWdodD0iMjEwMCIgZmlsbD0iIzNjM2I2ZSIvPg0KPGcgZmlsbD0iI2ZmZiI%2BDQo8ZyBpZD0iczE4Ij4NCjxnIGlkPSJzOSI%2BDQo8ZyBpZD0iczUiPg0KPGcgaWQ9InM0Ij4NCjxwYXRoIGlkPSJzIiBkPSJNMjQ3LDkwIDMxNy41MzQyMzAsMzA3LjA4MjAzOSAxMzIuODczMjE4LDE3Mi45MTc5NjFIMzYxLjEyNjc4MkwxNzYuNDY1NzcwLDMwNy4wODIwMzl6Ii8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB5PSI0MjAiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHk9Ijg0MCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTI2MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTY4MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjczQiIHg9IjI0NyIgeT0iMjEwIi8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzOSIgeD0iNDk0Ii8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzMTgiIHg9Ijk4OCIvPg0KPHVzZSB4bGluazpocmVmPSIjczkiIHg9IjE5NzYiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3M1IiB4PSIyNDcwIi8%2BDQo8L2c%2BDQo8L3N2Zz4%3D
|
||||
[gitter-english-url]: https://gitter.im/ant-design/ant-design-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
|
||||
[gitter-chinese-image]: https://img.shields.io/gitter/room/ant-design/ant-design.svg?style=flat-square&logoWidth=18&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjkwMCIgaGVpZ2h0PSI2MDAiIHZpZXdCb3g9IjAgMCAzMCAyMCI%2BDQo8ZGVmcz4NCjxwYXRoIGlkPSJzIiBkPSJNMCwtMSAwLjU4Nzc4NSwwLjgwOTAxNyAtMC45NTEwNTcsLTAuMzA5MDE3SDAuOTUxMDU3TC0wLjU4Nzc4NSwwLjgwOTAxN3oiIGZpbGw9IiNmZmRlMDAiLz4NCjwvZGVmcz4NCjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIyMCIgZmlsbD0iI2RlMjkxMCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw1KSBzY2FsZSgzKSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsMikgcm90YXRlKDIzLjAzNjI0MykiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyLDQpIHJvdGF0ZSg0NS44Njk4OTgpIi8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMiw3KSByb3RhdGUoNjkuOTQ1Mzk2KSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsOSkgcm90YXRlKDIwLjY1OTgwOCkiLz4NCjwvc3ZnPg%3D%3D
|
||||
[gitter-chinese-url]: https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
[semver-stability-url]: https://dependabot.com/compatibility-score.html/?dependency-name=antd&package-manager=npm_and_yarn
|
||||
[semver-stability-image]: https://api.dependabot.com/badges/compatibility_score?dependency-name=antd&package-manager=npm_and_yarn&version-scheme=semver
|
||||
[](https://twitter.com/AntDesignUI) [](https://gitter.im/ant-design/ant-design-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [](https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
</div>
|
||||
|
||||
[](https://ant.design/index-cn)
|
||||
[](http://ant.design/index-cn)
|
||||
|
||||
[English](./README.md) | [Português](./README-pt_BR.md) | 简体中文
|
||||
[English](./README.md) | 简体中文
|
||||
|
||||
## ✨ 特性
|
||||
|
||||
@@ -58,15 +31,15 @@
|
||||
- 🌍 数十个国际化语言支持。
|
||||
- 🎨 深入每个细节的主题定制能力。
|
||||
|
||||
## 🖥 兼容环境
|
||||
## 🖥 支持环境
|
||||
|
||||
- 现代浏览器和 IE11(需要 [polyfills](https://ant.design/docs/react/getting-started-cn#兼容性))。
|
||||
- 现代浏览器和 IE9 及以上。
|
||||
- 支持服务端渲染。
|
||||
- [Electron](https://www.electronjs.org/)
|
||||
- [Electron](http://electron.atom.io/)
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Electron |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| IE11, Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Electron |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| IE9, IE10, IE11, Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||
|
||||
## 📦 安装
|
||||
|
||||
@@ -80,7 +53,7 @@ yarn add antd
|
||||
|
||||
## 🔨 示例
|
||||
|
||||
```jsx
|
||||
````jsx
|
||||
import { Button, DatePicker } from 'antd';
|
||||
|
||||
const App = () => (
|
||||
@@ -89,38 +62,36 @@ const App = () => (
|
||||
<DatePicker />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
引入样式:
|
||||
|
||||
```jsx
|
||||
import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
|
||||
```
|
||||
````
|
||||
|
||||
### 🌈 定制主题
|
||||
|
||||
参考 [定制主题](https://ant.design/docs/react/customize-theme-cn) 文档。
|
||||
你也可以使用 [babel-plugin-import](https://ant.design/docs/react/getting-started-cn#按需加载)。
|
||||
|
||||
### 🛡 TypeScript
|
||||
|
||||
参考 [在 TypeScript 中使用](https://ant.design/docs/react/use-in-typescript-cn)。
|
||||
参考 [在 TypeScript 中使用](https://ant.design/docs/react/use-in-typescript-cn)
|
||||
|
||||
## 🌍 国际化
|
||||
|
||||
参考 [国际化文档](https://ant.design/docs/react/i18n-cn)。
|
||||
参考 [国际化文档](http://ant.design/docs/react/i18n-cn)。
|
||||
|
||||
## 🔗 链接
|
||||
|
||||
- [首页](https://ant.design/)
|
||||
- [所有组件](https://ant.design/components/overview-cn)
|
||||
- [首页](http://ant.design/)
|
||||
- [组件库](http://ant.design/docs/react/introduce)
|
||||
- [Ant Design Pro](http://pro.ant.design/)
|
||||
- [更新日志](CHANGELOG.en-US.md)
|
||||
- [React 底层基础组件](http://react-component.github.io/)
|
||||
- [移动端组件](http://mobile.ant.design)
|
||||
- [页面级组件](https://procomponents.ant.design)
|
||||
- [Ant Design 图表](https://charts.ant.design)
|
||||
- [Ant Design 图标](https://github.com/ant-design/ant-design-icons)
|
||||
- [Ant Design 色彩](https://github.com/ant-design/ant-design-colors)
|
||||
- [Ant Design Pro 布局组件](https://github.com/ant-design/ant-design-pro-layout)
|
||||
- [Ant Design Pro 区块集](https://github.com/ant-design/pro-blocks)
|
||||
- [Dark Theme](https://github.com/ant-design/ant-design-dark-theme)
|
||||
- [首页模板集](https://landing.ant.design)
|
||||
- [动效](https://motion.ant.design)
|
||||
- [脚手架市场](http://scaffold.ant.design)
|
||||
@@ -129,9 +100,8 @@ import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
|
||||
- [版本发布规则](https://github.com/ant-design/ant-design/wiki/%E8%BD%AE%E5%80%BC%E8%A7%84%E5%88%99%E5%92%8C%E7%89%88%E6%9C%AC%E5%8F%91%E5%B8%83%E6%B5%81%E7%A8%8B)
|
||||
- [常见问题](https://ant.design/docs/react/faq-cn)
|
||||
- [CodeSandbox 模板](https://u.ant.design/codesandbox-repro) for bug reports
|
||||
- [定制主题](https://ant.design/docs/react/customize-theme-cn)
|
||||
- [国际化](https://ant.design/docs/react/i18n-cn)
|
||||
- [成为社区协作成员](https://github.com/ant-design/ant-design/wiki/Collaborators#how-to-apply-for-being-a-collaborator)
|
||||
- [Awesome Ant Design](https://github.com/websemantics/awesome-ant-design)
|
||||
- [定制主题](http://ant.design/docs/react/customize-theme-cn)
|
||||
|
||||
## ⌨️ 本地开发
|
||||
|
||||
|
||||
70
README.md
70
README.md
@@ -1,5 +1,5 @@
|
||||
<p align="center">
|
||||
<a href="https://ant.design">
|
||||
<a href="http://ant.design">
|
||||
<img width="200" src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg">
|
||||
</a>
|
||||
</p>
|
||||
@@ -10,44 +10,17 @@
|
||||
|
||||
An enterprise-class UI design language and React UI library.
|
||||
|
||||
[![CI status][github-action-image]][github-action-url] [![codecov][codecov-image]][codecov-url] [![NPM version][npm-image]][npm-url] [![NPM downloads][download-image]][download-url]
|
||||
[](https://circleci.com/gh/ant-design/ant-design)  [](https://codecov.io/gh/ant-design/ant-design/branch/master) [](https://www.npmjs.com/package/antd) [](https://www.npmjs.com/package/antd) [](http://npmjs.com/antd)
|
||||
|
||||
[![david deps][david-image]][david-url] [![david devDeps][david-dev-image]][david-dev-url] [![Total alerts][lgtm-image]][lgtm-url] [![FOSSA Status][fossa-image]][fossa-url] [![Issues need help][help-wanted-image]][help-wanted-url]
|
||||
[](https://david-dm.org/ant-design/ant-design) [](https://david-dm.org/ant-design/ant-design?type=dev) [](https://lgtm.com/projects/g/ant-design/ant-design/alerts/) [](https://app.fossa.io/projects/git%2Bgithub.com%2Fant-design%2Fant-design?ref=badge_shield) [](https://github.com/ant-design/ant-design/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)
|
||||
|
||||
[![Follow Twitter][twitter-image]][twitter-url] [![Gitter][gitter-english-image]][gitter-english-url] [![Gitter][gitter-chinese-image]][gitter-chinese-url] [![[SemVer stability]][semver-stability-image]][semver-stability-url]
|
||||
|
||||
[npm-image]: http://img.shields.io/npm/v/antd.svg?style=flat-square
|
||||
[npm-url]: http://npmjs.org/package/antd
|
||||
[github-action-image]: https://github.com/ant-design/ant-design/workflows/test/badge.svg
|
||||
[github-action-url]: https://github.com/ant-design/ant-design/actions?query=workflow%3Atest
|
||||
[codecov-image]: https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square
|
||||
[codecov-url]: https://codecov.io/gh/ant-design/ant-design/branch/master
|
||||
[david-image]: https://img.shields.io/david/ant-design/ant-design?style=flat-square
|
||||
[david-dev-url]: https://david-dm.org/ant-design/ant-design?type=dev
|
||||
[david-dev-image]: https://img.shields.io/david/dev/ant-design/ant-design?style=flat-square
|
||||
[david-url]: https://david-dm.org/ant-design/ant-design
|
||||
[download-image]: https://img.shields.io/npm/dm/antd.svg?style=flat-square
|
||||
[download-url]: https://npmjs.org/package/antd
|
||||
[lgtm-image]: https://flat.badgen.net/lgtm/alerts/g/ant-design/ant-design
|
||||
[lgtm-url]: https://lgtm.com/projects/g/ant-design/ant-design/alerts/
|
||||
[fossa-image]: https://app.fossa.io/api/projects/git%2Bgithub.com%2Fant-design%2Fant-design.svg?type=shield
|
||||
[fossa-url]: https://app.fossa.io/projects/git%2Bgithub.com%2Fant-design%2Fant-design?ref=badge_shield
|
||||
[help-wanted-image]: https://flat.badgen.net/github/label-issues/ant-design/ant-design/help%20wanted/open
|
||||
[help-wanted-url]: https://github.com/ant-design/ant-design/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22
|
||||
[twitter-image]: https://img.shields.io/twitter/follow/AntDesignUI.svg?label=Ant%20Design&style=social
|
||||
[twitter-url]: https://twitter.com/AntDesignUI
|
||||
[gitter-english-image]: https://img.shields.io/gitter/room/ant-design/ant-design-english.svg?style=flat-square&logoWidth=18&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjEyMzUiIGhlaWdodD0iNjUwIiB2aWV3Qm94PSIwIDAgNzQxMCAzOTAwIj4NCjxyZWN0IHdpZHRoPSI3NDEwIiBoZWlnaHQ9IjM5MDAiIGZpbGw9IiNiMjIyMzQiLz4NCjxwYXRoIGQ9Ik0wLDQ1MEg3NDEwbTAsNjAwSDBtMCw2MDBINzQxMG0wLDYwMEgwbTAsNjAwSDc0MTBtMCw2MDBIMCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjMwMCIvPg0KPHJlY3Qgd2lkdGg9IjI5NjQiIGhlaWdodD0iMjEwMCIgZmlsbD0iIzNjM2I2ZSIvPg0KPGcgZmlsbD0iI2ZmZiI%2BDQo8ZyBpZD0iczE4Ij4NCjxnIGlkPSJzOSI%2BDQo8ZyBpZD0iczUiPg0KPGcgaWQ9InM0Ij4NCjxwYXRoIGlkPSJzIiBkPSJNMjQ3LDkwIDMxNy41MzQyMzAsMzA3LjA4MjAzOSAxMzIuODczMjE4LDE3Mi45MTc5NjFIMzYxLjEyNjc4MkwxNzYuNDY1NzcwLDMwNy4wODIwMzl6Ii8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB5PSI0MjAiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHk9Ijg0MCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTI2MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTY4MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjczQiIHg9IjI0NyIgeT0iMjEwIi8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzOSIgeD0iNDk0Ii8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzMTgiIHg9Ijk4OCIvPg0KPHVzZSB4bGluazpocmVmPSIjczkiIHg9IjE5NzYiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3M1IiB4PSIyNDcwIi8%2BDQo8L2c%2BDQo8L3N2Zz4%3D
|
||||
[gitter-english-url]: https://gitter.im/ant-design/ant-design-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
|
||||
[gitter-chinese-image]: https://img.shields.io/gitter/room/ant-design/ant-design.svg?style=flat-square&logoWidth=18&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjkwMCIgaGVpZ2h0PSI2MDAiIHZpZXdCb3g9IjAgMCAzMCAyMCI%2BDQo8ZGVmcz4NCjxwYXRoIGlkPSJzIiBkPSJNMCwtMSAwLjU4Nzc4NSwwLjgwOTAxNyAtMC45NTEwNTcsLTAuMzA5MDE3SDAuOTUxMDU3TC0wLjU4Nzc4NSwwLjgwOTAxN3oiIGZpbGw9IiNmZmRlMDAiLz4NCjwvZGVmcz4NCjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIyMCIgZmlsbD0iI2RlMjkxMCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw1KSBzY2FsZSgzKSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsMikgcm90YXRlKDIzLjAzNjI0MykiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyLDQpIHJvdGF0ZSg0NS44Njk4OTgpIi8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMiw3KSByb3RhdGUoNjkuOTQ1Mzk2KSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsOSkgcm90YXRlKDIwLjY1OTgwOCkiLz4NCjwvc3ZnPg%3D%3D
|
||||
[gitter-chinese-url]: https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
[semver-stability-url]: https://dependabot.com/compatibility-score.html/?dependency-name=antd&package-manager=npm_and_yarn
|
||||
[semver-stability-image]: https://api.dependabot.com/badges/compatibility_score?dependency-name=antd&package-manager=npm_and_yarn&version-scheme=semver
|
||||
[](https://twitter.com/AntDesignUI) [](https://gitter.im/ant-design/ant-design-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [](https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
</div>
|
||||
|
||||
[](https://ant.design)
|
||||
[](http://ant.design)
|
||||
|
||||
English | [Português](./README-pt_BR.md) | [简体中文](./README-zh_CN.md)
|
||||
English | [简体中文](./README-zh_CN.md)
|
||||
|
||||
## ✨ Features
|
||||
|
||||
@@ -60,13 +33,13 @@ English | [Português](./README-pt_BR.md) | [简体中文](./README-zh_CN.md)
|
||||
|
||||
## 🖥 Environment Support
|
||||
|
||||
- Modern browsers and Internet Explorer 11 (with [polyfills](https://stackoverflow.com/questions/57020976/polyfills-in-2019-for-ie11))
|
||||
- Modern browsers and Internet Explorer 9+ (with [polyfills](https://ant.design/docs/react/getting-started#Compatibility))
|
||||
- Server-side Rendering
|
||||
- [Electron](https://www.electronjs.org/)
|
||||
- [Electron](http://electron.atom.io/)
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Electron |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| IE11, Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Electron |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| IE9, IE10, IE11, Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||
|
||||
## 📦 Install
|
||||
|
||||
@@ -86,7 +59,7 @@ import { Button, DatePicker } from 'antd';
|
||||
const App = () => (
|
||||
<>
|
||||
<Button type="primary">PRESS ME</Button>
|
||||
<DatePicker placeholder="select date" />
|
||||
<DatePicker />
|
||||
</>
|
||||
);
|
||||
```
|
||||
@@ -97,26 +70,29 @@ And import style manually:
|
||||
import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
|
||||
```
|
||||
|
||||
Or use [babel-plugin-import](https://ant.design/docs/react/getting-started#Import-on-Demand).
|
||||
|
||||
### TypeScript
|
||||
|
||||
`antd` is written in TypeScript with complete definitions, check [Use in TypeScript](https://ant.design/docs/react/use-in-typescript) to getting started.
|
||||
See [Use in TypeScript](https://ant.design/docs/react/use-in-typescript).
|
||||
|
||||
## 🌍 Internationalization
|
||||
|
||||
Dozens of languages supported in `antd`, see [i18n](https://ant.design/docs/react/i18n).
|
||||
See [i18n](http://ant.design/docs/react/i18n).
|
||||
|
||||
## 🔗 Links
|
||||
|
||||
- [Home page](https://ant.design/)
|
||||
- [Components Overview](https://ant.design/components/overview)
|
||||
- [Home page](http://ant.design/)
|
||||
- [Components](http://ant.design/docs/react/introduce)
|
||||
- [Ant Design Pro](http://pro.ant.design/)
|
||||
- [Change Log](CHANGELOG.en-US.md)
|
||||
- [rc-components](http://react-component.github.io/)
|
||||
- [Mobile UI](http://mobile.ant.design)
|
||||
- [Ant Design Pro Components](https://procomponents.ant.design)
|
||||
- [Ant Design Charts](https://charts.ant.design)
|
||||
- [Ant Design Icons](https://github.com/ant-design/ant-design-icons)
|
||||
- [Ant Design Colors](https://github.com/ant-design/ant-design-colors)
|
||||
- [Ant Design Pro Layout](https://github.com/ant-design/ant-design-pro-layout)
|
||||
- [Ant Design Pro Blocks](https://github.com/ant-design/pro-blocks)
|
||||
- [Dark Theme](https://github.com/ant-design/ant-design-dark-theme)
|
||||
- [Landing Pages](https://landing.ant.design)
|
||||
- [Motion](https://motion.ant.design)
|
||||
- [Scaffold Market](http://scaffold.ant.design)
|
||||
@@ -124,8 +100,8 @@ Dozens of languages supported in `antd`, see [i18n](https://ant.design/docs/reac
|
||||
- [Versioning Release Note](https://github.com/ant-design/ant-design/wiki/%E8%BD%AE%E5%80%BC%E8%A7%84%E5%88%99%E5%92%8C%E7%89%88%E6%9C%AC%E5%8F%91%E5%B8%83%E6%B5%81%E7%A8%8B)
|
||||
- [FAQ](https://ant.design/docs/react/faq)
|
||||
- [CodeSandbox Template](https://u.ant.design/codesandbox-repro) for bug reports
|
||||
- [Customize Theme](https://ant.design/docs/react/customize-theme)
|
||||
- [How to Apply for Being A Collaborator](https://github.com/ant-design/ant-design/wiki/Collaborators#how-to-apply-for-being-a-collaborator)
|
||||
- [Awesome Ant Design](https://github.com/websemantics/awesome-ant-design)
|
||||
- [Customize Theme](http://ant.design/docs/react/customize-theme)
|
||||
|
||||
## ⌨️ Development
|
||||
|
||||
|
||||
16
SECURITY.md
16
SECURITY.md
@@ -1,16 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Use this section to tell people about which versions of your project are currently being supported with security updates.
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 4.x | :white_check_mark: |
|
||||
| < 4.0 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Use this section to tell people how to report a vulnerability.
|
||||
|
||||
Tell them where to go, how often they can expect to get an update on a reported vulnerability, what to expect if the vulnerability is accepted or declined, etc.
|
||||
@@ -1,62 +1,69 @@
|
||||
name: Ant Design
|
||||
# Node.js
|
||||
# Build a general Node.js project with npm.
|
||||
# Add steps that analyze code, save build artifacts, deploy, and more:
|
||||
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
|
||||
name: ant design
|
||||
|
||||
trigger: none
|
||||
|
||||
pr:
|
||||
autoCancel: true
|
||||
trigger:
|
||||
batch: true
|
||||
branches:
|
||||
exclude:
|
||||
- gh-pages
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
stages:
|
||||
- stage: site
|
||||
condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest'))
|
||||
jobs:
|
||||
- job: Build_Site
|
||||
steps:
|
||||
- checkout: self
|
||||
displayName: 'Checkout'
|
||||
clean: true
|
||||
fetchDepth: 1
|
||||
- task: NodeTool@0
|
||||
displayName: 'Install Node.js'
|
||||
inputs:
|
||||
versionSpec: '14.7.0'
|
||||
- script: npm install
|
||||
displayName: 'Install modules'
|
||||
- script: |
|
||||
node ./scripts/azure-github-comment.js "[](https://dev.azure.com/ant-design/ant-design/_build/results?buildId=$(Build.BuildId))"
|
||||
displayName: 'Comment on github'
|
||||
- script: npm run site
|
||||
displayName: 'Build sites'
|
||||
- script: ls -al _site/
|
||||
displayName: 'List build'
|
||||
- script: |
|
||||
export DEPLOY_DOMAIN=https://preview-${SYSTEM_PULLREQUEST_PULLREQUESTNUMBER}-ant-design.surge.sh
|
||||
echo "Deploy to $DEPLOY_DOMAIN"
|
||||
npx surge --project ./_site --domain $DEPLOY_DOMAIN
|
||||
displayName: 'Deploy Site'
|
||||
- script: |
|
||||
export DEPLOY_DOMAIN=https://preview-${SYSTEM_PULLREQUEST_PULLREQUESTNUMBER}-ant-design.surge.sh
|
||||
node ./scripts/azure-github-comment.js "[<img width="306" src="https://user-images.githubusercontent.com/5378891/72400743-23dbb200-3785-11ea-9d13-1a2d92743846.png">]($DEPLOY_DOMAIN)"
|
||||
displayName: 'Update comment on github'
|
||||
- job: Build_Site_Failed
|
||||
dependsOn: Build_Site
|
||||
condition: failed()
|
||||
steps:
|
||||
- checkout: self
|
||||
displayName: 'Checkout'
|
||||
clean: true
|
||||
fetchDepth: 1
|
||||
- task: NodeTool@0
|
||||
displayName: 'Install Node.js'
|
||||
inputs:
|
||||
versionSpec: '14.7.0'
|
||||
- script: npm install
|
||||
displayName: 'Install modules'
|
||||
- script: |
|
||||
node ./scripts/azure-github-comment.js "[<img width="534" src="https://user-images.githubusercontent.com/5378891/75333447-1e63a280-58c1-11ea-975d-235367fd1522.png">](https://dev.azure.com/ant-design/ant-design/_build/results?buildId=$(Build.BuildId))"
|
||||
displayName: 'Comment on github'
|
||||
jobs:
|
||||
- job: test_
|
||||
pool:
|
||||
vmImage: 'Ubuntu-16.04'
|
||||
strategy:
|
||||
matrix:
|
||||
Lint:
|
||||
TEST_TYPE: lint
|
||||
dist-react@16:
|
||||
REACT: 16
|
||||
TEST_TYPE: test:dist
|
||||
lib-react@16:
|
||||
REACT: 16
|
||||
TEST_TYPE: test:lib
|
||||
es-react@16:
|
||||
REACT: 16
|
||||
TEST_TYPE: test:es
|
||||
dom-react@16:
|
||||
REACT: 16
|
||||
TEST_TYPE: test:dom
|
||||
node-react@16:
|
||||
REACT: 16
|
||||
TEST_TYPE: test:node
|
||||
dist-react@15:
|
||||
REACT: 15
|
||||
TEST_TYPE: test:dist
|
||||
lib-react@15:
|
||||
REACT: 15
|
||||
TEST_TYPE: test:lib
|
||||
es-react@15:
|
||||
REACT: 15
|
||||
TEST_TYPE: test:es
|
||||
dom-react@15:
|
||||
REACT: 15
|
||||
TEST_TYPE: test:dom
|
||||
node-react@15:
|
||||
REACT: 15
|
||||
TEST_TYPE: test:node
|
||||
steps:
|
||||
- checkout: self
|
||||
fetchDepth: 1
|
||||
clean: false
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: '10.x'
|
||||
- script: npm install
|
||||
displayName: install
|
||||
- script: scripts/install-react.sh
|
||||
displayName: install-react
|
||||
- script: scripts/travis-script.sh
|
||||
displayName: test
|
||||
- task: PublishBuildArtifacts@1
|
||||
# 主分支,并且运行成功
|
||||
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
|
||||
inputs:
|
||||
pathtoPublish: './package-lock.json'
|
||||
artifactName: lock
|
||||
|
||||
@@ -27,12 +27,12 @@ Array [
|
||||
"Drawer",
|
||||
"Empty",
|
||||
"Form",
|
||||
"Grid",
|
||||
"Icon",
|
||||
"Input",
|
||||
"Image",
|
||||
"InputNumber",
|
||||
"Layout",
|
||||
"List",
|
||||
"LocaleProvider",
|
||||
"message",
|
||||
"Menu",
|
||||
"Mentions",
|
||||
@@ -51,7 +51,6 @@ Array [
|
||||
"Select",
|
||||
"Skeleton",
|
||||
"Slider",
|
||||
"Space",
|
||||
"Spin",
|
||||
"Steps",
|
||||
"Switch",
|
||||
@@ -65,6 +64,7 @@ Array [
|
||||
"Timeline",
|
||||
"Tooltip",
|
||||
"Typography",
|
||||
"Mention",
|
||||
"Upload",
|
||||
"version",
|
||||
]
|
||||
|
||||
58
components/__tests__/util/domHook.js
Normal file
58
components/__tests__/util/domHook.js
Normal file
@@ -0,0 +1,58 @@
|
||||
const __NULL__ = { notExist: true };
|
||||
|
||||
export function spyElementPrototypes(Element, properties) {
|
||||
const propNames = Object.keys(properties);
|
||||
const originDescriptors = {};
|
||||
|
||||
propNames.forEach(propName => {
|
||||
const originDescriptor = Object.getOwnPropertyDescriptor(Element.prototype, propName);
|
||||
originDescriptors[propName] = originDescriptor || __NULL__;
|
||||
|
||||
const spyProp = properties[propName];
|
||||
|
||||
if (typeof spyProp === 'function') {
|
||||
// If is a function
|
||||
Element.prototype[propName] = function spyFunc(...args) {
|
||||
return spyProp.call(this, originDescriptor, ...args);
|
||||
};
|
||||
} else {
|
||||
// Otherwise tread as a property
|
||||
Object.defineProperty(Element.prototype, propName, {
|
||||
...spyProp,
|
||||
set(value) {
|
||||
if (spyProp.set) {
|
||||
return spyProp.set.call(this, originDescriptor, value);
|
||||
}
|
||||
return originDescriptor.set(value);
|
||||
},
|
||||
get() {
|
||||
if (spyProp.get) {
|
||||
return spyProp.get.call(this, originDescriptor);
|
||||
}
|
||||
return originDescriptor.get();
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
mockRestore() {
|
||||
propNames.forEach(propName => {
|
||||
const originDescriptor = originDescriptors[propName];
|
||||
if (originDescriptor === __NULL__) {
|
||||
delete Element.prototype[propName];
|
||||
} else if (typeof originDescriptor === 'function') {
|
||||
Element.prototype[propName] = originDescriptor;
|
||||
} else {
|
||||
Object.defineProperty(Element.prototype, propName, originDescriptor);
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function spyElementPrototype(Element, propName, property) {
|
||||
return spyElementPrototypes(Element, {
|
||||
[propName]: property,
|
||||
});
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
import getScroll from '../getScroll';
|
||||
|
||||
describe('getScroll', () => {
|
||||
it('getScroll target null', async () => {
|
||||
expect(getScroll(null, true)).toBe(0);
|
||||
expect(getScroll(null, false)).toBe(0);
|
||||
});
|
||||
|
||||
it('getScroll window', async () => {
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
|
||||
window.pageXOffset = x;
|
||||
window.pageYOffset = y;
|
||||
});
|
||||
window.scrollTo(200, 400);
|
||||
expect(getScroll(window, true)).toBe(400);
|
||||
expect(getScroll(window, false)).toBe(200);
|
||||
scrollToSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('getScroll document', async () => {
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
|
||||
document.documentElement.scrollLeft = x;
|
||||
document.documentElement.scrollTop = y;
|
||||
});
|
||||
window.scrollTo(200, 400);
|
||||
expect(getScroll(document, true)).toBe(400);
|
||||
expect(getScroll(document, false)).toBe(200);
|
||||
scrollToSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('getScroll div', async () => {
|
||||
const div = document.createElement('div');
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
|
||||
div.scrollLeft = x;
|
||||
div.scrollTop = y;
|
||||
});
|
||||
window.scrollTo(200, 400);
|
||||
expect(getScroll(div, true)).toBe(400);
|
||||
expect(getScroll(div, false)).toBe(200);
|
||||
scrollToSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('getScroll documentElement', async () => {
|
||||
const div = {};
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
|
||||
div.scrollLeft = null;
|
||||
div.scrollTop = null;
|
||||
div.documentElement = {};
|
||||
div.documentElement.scrollLeft = x;
|
||||
div.documentElement.scrollTop = y;
|
||||
});
|
||||
window.scrollTo(200, 400);
|
||||
expect(getScroll(div, true)).toBe(400);
|
||||
expect(getScroll(div, false)).toBe(200);
|
||||
scrollToSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* @jest-environment node
|
||||
*/
|
||||
import getScroll from '../getScroll';
|
||||
|
||||
describe('getScroll node', () => {
|
||||
it('getScroll return 0 in node environment', async () => {
|
||||
expect(getScroll(null, true)).toBe(0);
|
||||
expect(getScroll(null, false)).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
import ResponsiveObserve, { responsiveMap } from '../responsiveObserve';
|
||||
|
||||
describe('Test ResponsiveObserve', () => {
|
||||
it('test ResponsiveObserve subscribe and unsubscribe', () => {
|
||||
const { xs } = responsiveMap;
|
||||
const subscribeFunc = jest.fn();
|
||||
const token = ResponsiveObserve.subscribe(subscribeFunc);
|
||||
expect(ResponsiveObserve.matchHandlers[xs].mql.matches).toBeTruthy();
|
||||
expect(subscribeFunc).toBeCalledTimes(1);
|
||||
|
||||
ResponsiveObserve.unsubscribe(token);
|
||||
expect(ResponsiveObserve.matchHandlers[xs].mql.removeListener).toBeCalled();
|
||||
});
|
||||
});
|
||||
@@ -1,9 +1,16 @@
|
||||
import scrollTo from '../scrollTo';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
|
||||
describe('Test ScrollTo function', () => {
|
||||
let dateNowMock;
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
dateNowMock = jest
|
||||
.spyOn(Date, 'now')
|
||||
@@ -22,8 +29,8 @@ describe('Test ScrollTo function', () => {
|
||||
});
|
||||
|
||||
scrollTo(1000);
|
||||
await sleep(20);
|
||||
|
||||
jest.runAllTimers();
|
||||
expect(window.pageYOffset).toBe(1000);
|
||||
|
||||
scrollToSpy.mockRestore();
|
||||
@@ -34,7 +41,7 @@ describe('Test ScrollTo function', () => {
|
||||
scrollTo(1000, {
|
||||
callback: cbMock,
|
||||
});
|
||||
await sleep(20);
|
||||
jest.runAllTimers();
|
||||
expect(cbMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -43,24 +50,7 @@ describe('Test ScrollTo function', () => {
|
||||
scrollTo(1000, {
|
||||
getContainer: () => div,
|
||||
});
|
||||
await sleep(20);
|
||||
jest.runAllTimers();
|
||||
expect(div.scrollTop).toBe(1000);
|
||||
});
|
||||
|
||||
it('test getContainer document - option', async () => {
|
||||
scrollTo(1000, {
|
||||
getContainer: () => document,
|
||||
});
|
||||
await sleep(20);
|
||||
expect(document.documentElement.scrollTop).toBe(1000);
|
||||
});
|
||||
|
||||
it('test duration - option', async () => {
|
||||
scrollTo(1000, {
|
||||
duration: 1100,
|
||||
getContainer: () => document,
|
||||
});
|
||||
await sleep(20);
|
||||
expect(document.documentElement.scrollTop).toBe(1000);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import TransButton from '../transButton';
|
||||
|
||||
describe('transButton component', () => {
|
||||
it('disabled should update style', () => {
|
||||
const wrapper = mount(<TransButton disabled />);
|
||||
expect(wrapper.find('div').first().props().style).toEqual(
|
||||
expect.objectContaining({ pointerEvents: 'none' }),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,8 +0,0 @@
|
||||
import UnreachableException from '../unreachableException';
|
||||
|
||||
describe('UnreachableException', () => {
|
||||
it('error thrown matches snapshot', () => {
|
||||
const exception = new UnreachableException('some value');
|
||||
expect(exception.message).toMatchInlineSnapshot(`"unreachable case: \\"some value\\""`);
|
||||
});
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import useSyncState from '../hooks/useSyncState';
|
||||
|
||||
describe('Table', () => {
|
||||
it('useSyncState', () => {
|
||||
const Test = () => {
|
||||
const [getVal, setVal] = useSyncState('light');
|
||||
|
||||
return (
|
||||
<span
|
||||
onClick={() => {
|
||||
setVal('bamboo');
|
||||
}}
|
||||
>
|
||||
{getVal()}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
const wrapper = mount(<Test />);
|
||||
expect(wrapper.text()).toEqual('light');
|
||||
wrapper.find('span').simulate('click');
|
||||
expect(wrapper.text()).toEqual('bamboo');
|
||||
});
|
||||
});
|
||||
@@ -1,60 +1,46 @@
|
||||
import raf from 'rc-util/lib/raf';
|
||||
import raf from 'raf';
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import KeyCode from 'rc-util/lib/KeyCode';
|
||||
import delayRaf from '../raf';
|
||||
import {
|
||||
throttleByAnimationFrame,
|
||||
throttleByAnimationFrameDecorator,
|
||||
} from '../throttleByAnimationFrame';
|
||||
import throttleByAnimationFrame from '../throttleByAnimationFrame';
|
||||
import getDataOrAriaProps from '../getDataOrAriaProps';
|
||||
import triggerEvent from '../triggerEvent';
|
||||
import Wave from '../wave';
|
||||
import TransButton from '../transButton';
|
||||
import { isStyleSupport, isFlexSupported } from '../styleChecker';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
import openAnimation from '../openAnimation';
|
||||
|
||||
describe('Test utils function', () => {
|
||||
describe('throttle', () => {
|
||||
it('throttle function should work', async () => {
|
||||
const callback = jest.fn();
|
||||
const throttled = throttleByAnimationFrame(callback);
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
throttled();
|
||||
throttled();
|
||||
await sleep(20);
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
expect(callback.mock.calls.length).toBe(1);
|
||||
});
|
||||
it('throttle function should work', () => {
|
||||
const callback = jest.fn();
|
||||
const throttled = throttleByAnimationFrame(callback);
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
it('throttle function should be canceled', async () => {
|
||||
const callback = jest.fn();
|
||||
const throttled = throttleByAnimationFrame(callback);
|
||||
throttled();
|
||||
throttled();
|
||||
|
||||
throttled();
|
||||
throttled.cancel();
|
||||
await sleep(20);
|
||||
jest.runAllTimers();
|
||||
expect(callback).toHaveBeenCalled();
|
||||
expect(callback.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
it('throttle function should be canceled', () => {
|
||||
const callback = jest.fn();
|
||||
const throttled = throttleByAnimationFrame(callback);
|
||||
|
||||
it('throttleByAnimationFrameDecorator should works', async () => {
|
||||
const callbackFn = jest.fn();
|
||||
class Test {
|
||||
@throttleByAnimationFrameDecorator()
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
callback() {
|
||||
callbackFn();
|
||||
}
|
||||
}
|
||||
const test = new Test();
|
||||
test.callback();
|
||||
test.callback();
|
||||
test.callback();
|
||||
await sleep(30);
|
||||
expect(callbackFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
throttled();
|
||||
throttled.cancel();
|
||||
|
||||
jest.runAllTimers();
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('getDataOrAriaProps', () => {
|
||||
@@ -140,6 +126,19 @@ describe('Test utils function', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('triggerEvent', () => {
|
||||
const button = document.createElement('button');
|
||||
button.addEventListener(
|
||||
'click',
|
||||
() => {
|
||||
button.style.width = '100px';
|
||||
},
|
||||
true,
|
||||
);
|
||||
triggerEvent(button, 'click');
|
||||
expect(button.style.width).toBe('100px');
|
||||
});
|
||||
|
||||
describe('wave', () => {
|
||||
it('bindAnimationEvent should return when node is null', () => {
|
||||
const wrapper = mount(
|
||||
@@ -184,16 +183,18 @@ describe('Test utils function', () => {
|
||||
});
|
||||
|
||||
it('should not throw when no children', () => {
|
||||
if (process.env.REACT === '15') {
|
||||
return;
|
||||
}
|
||||
expect(() => mount(<Wave />)).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('TransButton', () => {
|
||||
it('can be focus/blur', () => {
|
||||
const ref = React.createRef();
|
||||
mount(<TransButton ref={ref}>TransButton</TransButton>);
|
||||
expect(typeof ref.current.focus).toBe('function');
|
||||
expect(typeof ref.current.blur).toBe('function');
|
||||
const wrapper = mount(<TransButton>TransButton</TransButton>);
|
||||
expect(typeof wrapper.instance().focus).toBe('function');
|
||||
expect(typeof wrapper.instance().blur).toBe('function');
|
||||
});
|
||||
|
||||
it('should trigger onClick when press enter', () => {
|
||||
@@ -207,23 +208,20 @@ describe('Test utils function', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('style', () => {
|
||||
it('isFlexSupported', () => {
|
||||
expect(isFlexSupported).toBe(true);
|
||||
});
|
||||
|
||||
it('isStyleSupport', () => {
|
||||
expect(isStyleSupport('color')).toBe(true);
|
||||
expect(isStyleSupport('not-existed')).toBe(false);
|
||||
});
|
||||
|
||||
it('isStyleSupport return false in service side', () => {
|
||||
const spy = jest
|
||||
.spyOn(window.document, 'documentElement', 'get')
|
||||
.mockImplementation(() => undefined);
|
||||
expect(isStyleSupport('color')).toBe(false);
|
||||
expect(isStyleSupport('not-existed')).toBe(false);
|
||||
spy.mockRestore();
|
||||
describe('openAnimation', () => {
|
||||
it('should support openAnimation', () => {
|
||||
const done = jest.fn();
|
||||
const domNode = document.createElement('div');
|
||||
expect(typeof openAnimation.enter).toBe('function');
|
||||
expect(typeof openAnimation.leave).toBe('function');
|
||||
expect(typeof openAnimation.appear).toBe('function');
|
||||
const appear = openAnimation.appear(domNode, done);
|
||||
const enter = openAnimation.enter(domNode, done);
|
||||
const leave = openAnimation.leave(domNode, done);
|
||||
expect(typeof appear.stop).toBe('function');
|
||||
expect(typeof enter.stop).toBe('function');
|
||||
expect(typeof leave.stop).toBe('function');
|
||||
expect(done).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Wave from '../wave';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
|
||||
describe('Wave component', () => {
|
||||
mountTest(Wave);
|
||||
|
||||
afterEach(() => {
|
||||
const styles = document.getElementsByTagName('style');
|
||||
for (let i = 0; i < styles.length; i += 1) {
|
||||
styles[i].remove();
|
||||
}
|
||||
});
|
||||
|
||||
it('isHidden works', () => {
|
||||
const TEST_NODE_ENV = process.env.NODE_ENV;
|
||||
process.env.NODE_ENV = 'development';
|
||||
const wrapper = mount(
|
||||
<Wave>
|
||||
<button type="button">button</button>
|
||||
</Wave>,
|
||||
);
|
||||
expect(wrapper.find('button').getDOMNode().className).toBe('');
|
||||
wrapper.find('button').getDOMNode().click();
|
||||
expect(
|
||||
wrapper.find('button').getDOMNode().hasAttribute('ant-click-animating-without-extra-node'),
|
||||
).toBe(false);
|
||||
wrapper.unmount();
|
||||
process.env.NODE_ENV = TEST_NODE_ENV;
|
||||
});
|
||||
|
||||
it('isHidden is mocked', () => {
|
||||
const wrapper = mount(
|
||||
<Wave>
|
||||
<button type="button">button</button>
|
||||
</Wave>,
|
||||
);
|
||||
expect(wrapper.find('button').getDOMNode().className).toBe('');
|
||||
wrapper.find('button').getDOMNode().click();
|
||||
expect(
|
||||
wrapper.find('button').getDOMNode().getAttribute('ant-click-animating-without-extra-node'),
|
||||
).toBe('false');
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('wave color is grey', async () => {
|
||||
const wrapper = mount(
|
||||
<Wave>
|
||||
<button type="button" style={{ borderColor: 'rgb(0, 0, 0)' }}>
|
||||
button
|
||||
</button>
|
||||
</Wave>,
|
||||
);
|
||||
wrapper.find('button').getDOMNode().click();
|
||||
await sleep(0);
|
||||
const styles = document.getElementsByTagName('style');
|
||||
expect(styles.length).toBe(0);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('wave color is not grey', async () => {
|
||||
const wrapper = mount(
|
||||
<Wave>
|
||||
<button type="button" style={{ borderColor: 'red' }}>
|
||||
button
|
||||
</button>
|
||||
</Wave>,
|
||||
);
|
||||
wrapper.find('button').getDOMNode().click();
|
||||
await sleep(200);
|
||||
const styles = document.getElementsByTagName('style');
|
||||
expect(styles.length).toBe(1);
|
||||
expect(styles[0].innerHTML).toContain('--antd-wave-shadow-color: red;');
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('read wave color from border-top-color', async () => {
|
||||
const wrapper = mount(
|
||||
<Wave>
|
||||
<div style={{ borderTopColor: 'blue' }}>button</div>
|
||||
</Wave>,
|
||||
);
|
||||
wrapper.find('div').getDOMNode().click();
|
||||
await sleep(0);
|
||||
const styles = document.getElementsByTagName('style');
|
||||
expect(styles.length).toBe(1);
|
||||
expect(styles[0].innerHTML).toContain('--antd-wave-shadow-color: blue;');
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('read wave color from background color', async () => {
|
||||
const wrapper = mount(
|
||||
<Wave>
|
||||
<div style={{ backgroundColor: 'green' }}>button</div>
|
||||
</Wave>,
|
||||
);
|
||||
wrapper.find('div').getDOMNode().click();
|
||||
await sleep(0);
|
||||
const styles = document.getElementsByTagName('style');
|
||||
expect(styles.length).toBe(1);
|
||||
expect(styles[0].innerHTML).toContain('--antd-wave-shadow-color: green;');
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('read wave color from border firstly', async () => {
|
||||
const wrapper = mount(
|
||||
<Wave>
|
||||
<div style={{ borderColor: 'yellow', backgroundColor: 'green' }}>button</div>
|
||||
</Wave>,
|
||||
);
|
||||
wrapper.find('div').getDOMNode().click();
|
||||
await sleep(0);
|
||||
const styles = document.getElementsByTagName('style');
|
||||
expect(styles.length).toBe(1);
|
||||
expect(styles[0].innerHTML).toContain('--antd-wave-shadow-color: yellow;');
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('hidden element with -leave className', async () => {
|
||||
const wrapper = mount(
|
||||
<Wave>
|
||||
<button type="button" className="xx-leave">
|
||||
button
|
||||
</button>
|
||||
</Wave>,
|
||||
);
|
||||
wrapper.find('button').getDOMNode().click();
|
||||
await sleep(0);
|
||||
const styles = document.getElementsByTagName('style');
|
||||
expect(styles.length).toBe(0);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('ConfigProvider csp', async () => {
|
||||
const wrapper = mount(
|
||||
<ConfigProvider csp={{ nonce: 'YourNonceCode' }}>
|
||||
<Wave>
|
||||
<button type="button">button</button>
|
||||
</Wave>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
wrapper.find('button').getDOMNode().click();
|
||||
await sleep(0);
|
||||
const styles = document.getElementsByTagName('style');
|
||||
expect(styles[0].getAttribute('nonce')).toBe('YourNonceCode');
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ElementOf, tuple } from './type';
|
||||
import { tuple } from './type';
|
||||
|
||||
export const PresetStatusColorTypes = tuple('success', 'processing', 'error', 'default', 'warning');
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const PresetColorTypes = tuple(
|
||||
'pink',
|
||||
@@ -18,5 +17,4 @@ export const PresetColorTypes = tuple(
|
||||
'lime',
|
||||
);
|
||||
|
||||
export type PresetColorType = ElementOf<typeof PresetColorTypes>;
|
||||
export type PresetStatusColorType = ElementOf<typeof PresetStatusColorTypes>;
|
||||
export type PresetColorType = (typeof PresetColorTypes)[number];
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import devWarning, { resetWarned } from 'rc-util/lib/warning';
|
||||
|
||||
export { resetWarned };
|
||||
|
||||
export default (valid: boolean, component: string, message: string): void => {
|
||||
devWarning(valid, `[antd: ${component}] ${message}`);
|
||||
};
|
||||
@@ -5,6 +5,5 @@ export function easeInOutCubic(t: number, b: number, c: number, d: number) {
|
||||
if (t < 1) {
|
||||
return (cc / 2) * t * t * t + b;
|
||||
}
|
||||
// eslint-disable-next-line no-return-assign
|
||||
return (cc / 2) * ((t -= 2) * t * t + 2) + b;
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export type RenderFunction = () => React.ReactNode;
|
||||
|
||||
export const getRenderPropValue = (
|
||||
propValue?: React.ReactNode | RenderFunction,
|
||||
): React.ReactNode => {
|
||||
if (!propValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isRenderFunction = typeof propValue === 'function';
|
||||
if (isRenderFunction) {
|
||||
return (propValue as RenderFunction)();
|
||||
}
|
||||
|
||||
return propValue;
|
||||
};
|
||||
@@ -1,27 +1,17 @@
|
||||
export function isWindow(obj: any) {
|
||||
return obj !== null && obj !== undefined && obj === obj.window;
|
||||
}
|
||||
|
||||
export default function getScroll(
|
||||
target: HTMLElement | Window | Document | null,
|
||||
top: boolean,
|
||||
): number {
|
||||
export default function getScroll(target: HTMLElement | Window | null, top: boolean): number {
|
||||
if (typeof window === 'undefined') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const prop = top ? 'pageYOffset' : 'pageXOffset';
|
||||
const method = top ? 'scrollTop' : 'scrollLeft';
|
||||
let result = 0;
|
||||
if (isWindow(target)) {
|
||||
result = (target as Window)[top ? 'pageYOffset' : 'pageXOffset'];
|
||||
} else if (target instanceof Document) {
|
||||
result = target.documentElement[method];
|
||||
} else if (target) {
|
||||
result = (target as HTMLElement)[method];
|
||||
const isWindow = target === window;
|
||||
|
||||
let ret = isWindow ? (target as Window)[prop] : (target as HTMLElement)[method];
|
||||
// ie6,7,8 standard mode
|
||||
if (isWindow && typeof ret !== 'number') {
|
||||
ret = (document.documentElement as HTMLElement)[method];
|
||||
}
|
||||
if (target && !isWindow(target) && typeof result !== 'number') {
|
||||
result = ((target as HTMLElement).ownerDocument || (target as Document)).documentElement[
|
||||
method
|
||||
];
|
||||
}
|
||||
return result;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export default function useForceUpdate() {
|
||||
const [, forceUpdate] = React.useReducer(x => x + 1, 0);
|
||||
return forceUpdate;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export default function usePatchElement(): [
|
||||
React.ReactElement[],
|
||||
(element: React.ReactElement) => Function,
|
||||
] {
|
||||
const [elements, setElements] = React.useState<React.ReactElement[]>([]);
|
||||
|
||||
const patchElement = React.useCallback((element: React.ReactElement) => {
|
||||
// append a new element to elements (and create a new ref)
|
||||
setElements(originElements => [...originElements, element]);
|
||||
|
||||
// return a function that removes the new element out of elements (and create a new ref)
|
||||
// it works a little like useEffect
|
||||
return () => {
|
||||
setElements(originElements => originElements.filter(ele => ele !== element));
|
||||
};
|
||||
}, []);
|
||||
|
||||
return [elements, patchElement];
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import useForceUpdate from './useForceUpdate';
|
||||
|
||||
type UseSyncStateProps<T> = [() => T, (newValue: T) => void];
|
||||
|
||||
export default function useSyncState<T>(initialValue: T): UseSyncStateProps<T> {
|
||||
const ref = React.useRef<T>(initialValue);
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
return [
|
||||
() => ref.current,
|
||||
(newValue: T) => {
|
||||
ref.current = newValue;
|
||||
// re-render
|
||||
forceUpdate();
|
||||
},
|
||||
];
|
||||
}
|
||||
5
components/_util/interopDefault.ts
Normal file
5
components/_util/interopDefault.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
// https://github.com/moment/moment/issues/3650
|
||||
// since we are using ts 3.5.1, it should be safe to remove.
|
||||
export default function interopDefault(m: any) {
|
||||
return m.default || m;
|
||||
}
|
||||
@@ -1,13 +1,33 @@
|
||||
import { CSSMotionProps, MotionEventHandler, MotionEndEventHandler } from 'rc-motion';
|
||||
import * as React from 'react';
|
||||
|
||||
type MotionFunc = (element: HTMLElement) => React.CSSProperties;
|
||||
|
||||
interface Motion {
|
||||
visible?: boolean;
|
||||
motionName?: string; // It also support object, but we only use string here.
|
||||
motionAppear?: boolean;
|
||||
motionEnter?: boolean;
|
||||
motionLeave?: boolean;
|
||||
motionLeaveImmediately?: boolean; // Trigger leave motion immediately
|
||||
removeOnLeave?: boolean;
|
||||
leavedClassName?: string;
|
||||
onAppearStart?: MotionFunc;
|
||||
onAppearActive?: MotionFunc;
|
||||
onAppearEnd?: MotionFunc;
|
||||
onEnterStart?: MotionFunc;
|
||||
onEnterActive?: MotionFunc;
|
||||
onEnterEnd?: MotionFunc;
|
||||
onLeaveStart?: MotionFunc;
|
||||
onLeaveActive?: MotionFunc;
|
||||
onLeaveEnd?: MotionFunc;
|
||||
}
|
||||
|
||||
// ================== Collapse Motion ==================
|
||||
const getCollapsedHeight: MotionEventHandler = () => ({ height: 0, opacity: 0 });
|
||||
const getRealHeight: MotionEventHandler = node => ({ height: node.scrollHeight, opacity: 1 });
|
||||
const getCurrentHeight: MotionEventHandler = node => ({ height: node.offsetHeight });
|
||||
const skipOpacityTransition: MotionEndEventHandler = (_, event) =>
|
||||
(event as TransitionEvent).propertyName === 'height';
|
||||
const getCollapsedHeight: MotionFunc = () => ({ height: 0, opacity: 0 });
|
||||
const getRealHeight: MotionFunc = node => ({ height: node.scrollHeight, opacity: 1 });
|
||||
const getCurrentHeight: MotionFunc = node => ({ height: node.offsetHeight });
|
||||
|
||||
const collapseMotion: CSSMotionProps = {
|
||||
const collapseMotion: Motion = {
|
||||
motionName: 'ant-motion-collapse',
|
||||
onAppearStart: getCollapsedHeight,
|
||||
onEnterStart: getCollapsedHeight,
|
||||
@@ -15,10 +35,6 @@ const collapseMotion: CSSMotionProps = {
|
||||
onEnterActive: getRealHeight,
|
||||
onLeaveStart: getCurrentHeight,
|
||||
onLeaveActive: getCollapsedHeight,
|
||||
onAppearEnd: skipOpacityTransition,
|
||||
onEnterEnd: skipOpacityTransition,
|
||||
onLeaveEnd: skipOpacityTransition,
|
||||
motionDeadline: 500,
|
||||
};
|
||||
|
||||
export default collapseMotion;
|
||||
|
||||
54
components/_util/openAnimation.tsx
Normal file
54
components/_util/openAnimation.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Deprecated. We should replace the animation with pure react motion instead of modify style directly.
|
||||
* If you are creating new component with animation, please use `./motion`.
|
||||
*/
|
||||
import cssAnimation from 'css-animation';
|
||||
import raf from 'raf';
|
||||
|
||||
function animate(node: HTMLElement, show: boolean, done: () => void) {
|
||||
let height: number;
|
||||
let requestAnimationFrameId: number;
|
||||
return cssAnimation(node, 'ant-motion-collapse-legacy', {
|
||||
start() {
|
||||
if (!show) {
|
||||
node.style.height = `${node.offsetHeight}px`;
|
||||
node.style.opacity = '1';
|
||||
} else {
|
||||
height = node.offsetHeight;
|
||||
node.style.height = '0px';
|
||||
node.style.opacity = '0';
|
||||
}
|
||||
},
|
||||
active() {
|
||||
if (requestAnimationFrameId) {
|
||||
raf.cancel(requestAnimationFrameId);
|
||||
}
|
||||
requestAnimationFrameId = raf(() => {
|
||||
node.style.height = `${show ? height : 0}px`;
|
||||
node.style.opacity = show ? '1' : '0';
|
||||
});
|
||||
},
|
||||
end() {
|
||||
if (requestAnimationFrameId) {
|
||||
raf.cancel(requestAnimationFrameId);
|
||||
}
|
||||
node.style.height = '';
|
||||
node.style.opacity = '';
|
||||
done();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const animation = {
|
||||
enter(node: HTMLElement, done: () => void) {
|
||||
return animate(node, true, done);
|
||||
},
|
||||
leave(node: HTMLElement, done: () => void) {
|
||||
return animate(node, false, done);
|
||||
},
|
||||
appear(node: HTMLElement, done: () => void) {
|
||||
return animate(node, true, done);
|
||||
},
|
||||
};
|
||||
|
||||
export default animation;
|
||||
@@ -1,4 +1,4 @@
|
||||
import raf from 'rc-util/lib/raf';
|
||||
import raf from 'raf';
|
||||
|
||||
interface RafMap {
|
||||
[id: number]: number;
|
||||
|
||||
@@ -1,24 +1,8 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export const { isValidElement } = React;
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export function cloneElement(element: React.ReactNode, ...restArgs: any[]) {
|
||||
if (!React.isValidElement(element)) return element;
|
||||
|
||||
type AnyObject = Record<any, any>;
|
||||
|
||||
type RenderProps = undefined | AnyObject | ((originProps: AnyObject) => AnyObject | undefined);
|
||||
|
||||
export function replaceElement(
|
||||
element: React.ReactNode,
|
||||
replacement: React.ReactNode,
|
||||
props: RenderProps,
|
||||
): React.ReactNode {
|
||||
if (!isValidElement(element)) return replacement;
|
||||
|
||||
return React.cloneElement(
|
||||
element,
|
||||
typeof props === 'function' ? props(element.props || {}) : props,
|
||||
);
|
||||
}
|
||||
|
||||
export function cloneElement(element: React.ReactNode, props?: RenderProps): React.ReactElement {
|
||||
return replaceElement(element, element, props) as React.ReactElement;
|
||||
return React.cloneElement(element, ...restArgs);
|
||||
}
|
||||
|
||||
17
components/_util/ref.ts
Normal file
17
components/_util/ref.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
|
||||
export function fillRef<T>(ref: React.Ref<T>, node: T) {
|
||||
if (typeof ref === 'function') {
|
||||
ref(node);
|
||||
} else if (typeof ref === 'object' && ref && 'current' in ref) {
|
||||
(ref as any).current = node;
|
||||
}
|
||||
}
|
||||
|
||||
export function composeRef<T>(...refs: React.Ref<T>[]): React.Ref<T> {
|
||||
return (node: T) => {
|
||||
refs.forEach(ref => {
|
||||
fillRef(ref, node);
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,25 @@
|
||||
// matchMedia polyfill for
|
||||
// https://github.com/WickyNilliams/enquire.js/issues/82
|
||||
let enquire: any;
|
||||
|
||||
// TODO: Will be removed in antd 4.0 because we will no longer support ie9
|
||||
if (typeof window !== 'undefined') {
|
||||
const matchMediaPolyfill = (mediaQuery: string) => {
|
||||
return {
|
||||
media: mediaQuery,
|
||||
matches: false,
|
||||
addListener() {},
|
||||
removeListener() {},
|
||||
};
|
||||
};
|
||||
// ref: https://github.com/ant-design/ant-design/issues/18774
|
||||
if (!window.matchMedia) window.matchMedia = matchMediaPolyfill as any;
|
||||
// eslint-disable-next-line global-require
|
||||
enquire = require('enquire.js');
|
||||
}
|
||||
|
||||
export type Breakpoint = 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs';
|
||||
export type BreakpointMap = Record<Breakpoint, string>;
|
||||
export type ScreenMap = Partial<Record<Breakpoint, boolean>>;
|
||||
export type ScreenSizeMap = Partial<Record<Breakpoint, number>>;
|
||||
export type BreakpointMap = Partial<Record<Breakpoint, string>>;
|
||||
|
||||
export const responsiveArray: Breakpoint[] = ['xxl', 'xl', 'lg', 'md', 'sm', 'xs'];
|
||||
|
||||
@@ -14,60 +32,72 @@ export const responsiveMap: BreakpointMap = {
|
||||
xxl: '(min-width: 1600px)',
|
||||
};
|
||||
|
||||
type SubscribeFunc = (screens: ScreenMap) => void;
|
||||
const subscribers = new Map<Number, SubscribeFunc>();
|
||||
type SubscribeFunc = (screens: BreakpointMap) => void;
|
||||
|
||||
let subscribers: Array<{
|
||||
token: string;
|
||||
func: SubscribeFunc;
|
||||
}> = [];
|
||||
let subUid = -1;
|
||||
let screens = {};
|
||||
|
||||
const responsiveObserve = {
|
||||
matchHandlers: {} as {
|
||||
[prop: string]: {
|
||||
mql: MediaQueryList;
|
||||
listener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | null;
|
||||
};
|
||||
},
|
||||
dispatch(pointMap: ScreenMap) {
|
||||
dispatch(pointMap: BreakpointMap) {
|
||||
screens = pointMap;
|
||||
subscribers.forEach(func => func(screens));
|
||||
return subscribers.size >= 1;
|
||||
if (subscribers.length < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
subscribers.forEach(item => {
|
||||
item.func(screens);
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
subscribe(func: SubscribeFunc): number {
|
||||
if (!subscribers.size) this.register();
|
||||
subUid += 1;
|
||||
subscribers.set(subUid, func);
|
||||
subscribe(func: SubscribeFunc) {
|
||||
if (subscribers.length === 0) {
|
||||
this.register();
|
||||
}
|
||||
const token = (++subUid).toString();
|
||||
subscribers.push({
|
||||
token,
|
||||
func,
|
||||
});
|
||||
func(screens);
|
||||
return subUid;
|
||||
return token;
|
||||
},
|
||||
unsubscribe(token: number) {
|
||||
subscribers.delete(token);
|
||||
if (!subscribers.size) this.unregister();
|
||||
unsubscribe(token: string) {
|
||||
subscribers = subscribers.filter(item => item.token !== token);
|
||||
if (subscribers.length === 0) {
|
||||
this.unregister();
|
||||
}
|
||||
},
|
||||
unregister() {
|
||||
Object.keys(responsiveMap).forEach((screen: Breakpoint) => {
|
||||
const matchMediaQuery = responsiveMap[screen];
|
||||
const handler = this.matchHandlers[matchMediaQuery];
|
||||
handler?.mql.removeListener(handler?.listener);
|
||||
});
|
||||
subscribers.clear();
|
||||
Object.keys(responsiveMap).map((screen: Breakpoint) =>
|
||||
enquire.unregister(responsiveMap[screen]),
|
||||
);
|
||||
},
|
||||
register() {
|
||||
Object.keys(responsiveMap).forEach((screen: Breakpoint) => {
|
||||
const matchMediaQuery = responsiveMap[screen];
|
||||
const listener = ({ matches }: { matches: boolean }) => {
|
||||
this.dispatch({
|
||||
...screens,
|
||||
[screen]: matches,
|
||||
});
|
||||
};
|
||||
const mql = window.matchMedia(matchMediaQuery);
|
||||
mql.addListener(listener);
|
||||
this.matchHandlers[matchMediaQuery] = {
|
||||
mql,
|
||||
listener,
|
||||
};
|
||||
|
||||
listener(mql);
|
||||
});
|
||||
Object.keys(responsiveMap).map((screen: Breakpoint) =>
|
||||
enquire.register(responsiveMap[screen], {
|
||||
match: () => {
|
||||
const pointMap = {
|
||||
...screens,
|
||||
[screen]: true,
|
||||
};
|
||||
this.dispatch(pointMap);
|
||||
},
|
||||
unmatch: () => {
|
||||
const pointMap = {
|
||||
...screens,
|
||||
[screen]: false,
|
||||
};
|
||||
this.dispatch(pointMap);
|
||||
},
|
||||
// Keep a empty destory to avoid triggering unmatch when unregister
|
||||
destroy() {},
|
||||
}),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import raf from 'rc-util/lib/raf';
|
||||
import getScroll, { isWindow } from './getScroll';
|
||||
import raf from 'raf';
|
||||
import getScroll from './getScroll';
|
||||
import { easeInOutCubic } from './easings';
|
||||
|
||||
interface ScrollToOptions {
|
||||
/** Scroll container, default as window */
|
||||
getContainer?: () => HTMLElement | Window | Document;
|
||||
getContainer?: () => HTMLElement | Window;
|
||||
/** Scroll end callback */
|
||||
callback?: () => any;
|
||||
/** Animation duration, default as 450 */
|
||||
@@ -13,6 +13,7 @@ interface ScrollToOptions {
|
||||
|
||||
export default function scrollTo(y: number, options: ScrollToOptions = {}) {
|
||||
const { getContainer = () => window, callback, duration = 450 } = options;
|
||||
|
||||
const container = getContainer();
|
||||
const scrollTop = getScroll(container, true);
|
||||
const startTime = Date.now();
|
||||
@@ -21,10 +22,8 @@ export default function scrollTo(y: number, options: ScrollToOptions = {}) {
|
||||
const timestamp = Date.now();
|
||||
const time = timestamp - startTime;
|
||||
const nextScrollTop = easeInOutCubic(time > duration ? duration : time, scrollTop, y, duration);
|
||||
if (isWindow(container)) {
|
||||
(container as Window).scrollTo(window.pageXOffset, nextScrollTop);
|
||||
} else if (container instanceof HTMLDocument || container.constructor.name === 'HTMLDocument') {
|
||||
(container as HTMLDocument).documentElement.scrollTop = nextScrollTop;
|
||||
if (container === window) {
|
||||
window.scrollTo(window.pageXOffset, nextScrollTop);
|
||||
} else {
|
||||
(container as HTMLElement).scrollTop = nextScrollTop;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export const isStyleSupport = (styleName: string | Array<string>): boolean => {
|
||||
const isStyleSupport = (styleName: string | Array<string>): boolean => {
|
||||
if (typeof window !== 'undefined' && window.document && window.document.documentElement) {
|
||||
const styleNameList = Array.isArray(styleName) ? styleName : [styleName];
|
||||
const { documentElement } = window.document;
|
||||
@@ -9,3 +9,5 @@ export const isStyleSupport = (styleName: string | Array<string>): boolean => {
|
||||
};
|
||||
|
||||
export const isFlexSupported = isStyleSupport(['flex', 'webkitFlex', 'Flex', 'msFlex']);
|
||||
|
||||
export default isStyleSupport;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import raf from 'rc-util/lib/raf';
|
||||
import raf from 'raf';
|
||||
|
||||
export function throttleByAnimationFrame(fn: (...args: any[]) => void) {
|
||||
export default function throttleByAnimationFrame(fn: (...args: any[]) => void) {
|
||||
let requestId: number | null;
|
||||
|
||||
const later = (args: any[]) => () => {
|
||||
@@ -20,18 +20,15 @@ export function throttleByAnimationFrame(fn: (...args: any[]) => void) {
|
||||
}
|
||||
|
||||
export function throttleByAnimationFrameDecorator() {
|
||||
return function throttle(target: any, key: string, descriptor: any) {
|
||||
// eslint-disable-next-line func-names
|
||||
return function(target: any, key: string, descriptor: any) {
|
||||
const fn = descriptor.value;
|
||||
let definingProperty = false;
|
||||
return {
|
||||
configurable: true,
|
||||
get() {
|
||||
// In IE11 calling Object.defineProperty has a side-effect of evaluating the
|
||||
// getter for the property which is being replaced. This causes infinite
|
||||
// recursion and an "Out of stack space" error.
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
if (definingProperty || this === target.prototype || this.hasOwnProperty(key)) {
|
||||
/* istanbul ignore next */
|
||||
return fn;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,6 @@ import KeyCode from 'rc-util/lib/KeyCode';
|
||||
interface TransButtonProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
onClick?: (e?: React.MouseEvent<HTMLDivElement>) => void;
|
||||
noStyle?: boolean;
|
||||
autoFocus?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const inlineStyle: React.CSSProperties = {
|
||||
@@ -20,52 +18,57 @@ const inlineStyle: React.CSSProperties = {
|
||||
display: 'inline-block',
|
||||
};
|
||||
|
||||
const TransButton = React.forwardRef<HTMLDivElement, TransButtonProps>((props, ref) => {
|
||||
const onKeyDown: React.KeyboardEventHandler<HTMLDivElement> = event => {
|
||||
class TransButton extends React.Component<TransButtonProps> {
|
||||
div?: HTMLDivElement;
|
||||
|
||||
lastKeyCode?: number;
|
||||
|
||||
onKeyDown: React.KeyboardEventHandler<HTMLDivElement> = event => {
|
||||
const { keyCode } = event;
|
||||
if (keyCode === KeyCode.ENTER) {
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
const onKeyUp: React.KeyboardEventHandler<HTMLDivElement> = event => {
|
||||
onKeyUp: React.KeyboardEventHandler<HTMLDivElement> = event => {
|
||||
const { keyCode } = event;
|
||||
const { onClick } = props;
|
||||
const { onClick } = this.props;
|
||||
if (keyCode === KeyCode.ENTER && onClick) {
|
||||
onClick();
|
||||
}
|
||||
};
|
||||
|
||||
const { style, noStyle, disabled, ...restProps } = props;
|
||||
|
||||
let mergedStyle: React.CSSProperties = {};
|
||||
|
||||
if (!noStyle) {
|
||||
mergedStyle = {
|
||||
...inlineStyle,
|
||||
};
|
||||
}
|
||||
|
||||
if (disabled) {
|
||||
mergedStyle.pointerEvents = 'none';
|
||||
}
|
||||
|
||||
mergedStyle = {
|
||||
...mergedStyle,
|
||||
...style,
|
||||
setRef = (btn: HTMLDivElement) => {
|
||||
this.div = btn;
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
ref={ref}
|
||||
{...restProps}
|
||||
onKeyDown={onKeyDown}
|
||||
onKeyUp={onKeyUp}
|
||||
style={mergedStyle}
|
||||
/>
|
||||
);
|
||||
});
|
||||
focus() {
|
||||
if (this.div) {
|
||||
this.div.focus();
|
||||
}
|
||||
}
|
||||
|
||||
blur() {
|
||||
if (this.div) {
|
||||
this.div.blur();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { style, noStyle, ...restProps } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
ref={this.setRef}
|
||||
{...restProps}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onKeyUp={this.onKeyUp}
|
||||
style={{ ...(!noStyle ? inlineStyle : null), ...style }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TransButton;
|
||||
|
||||
8
components/_util/triggerEvent.tsx
Normal file
8
components/_util/triggerEvent.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
export default function triggerEvent(el: Element, type: string) {
|
||||
if ('createEvent' in document) {
|
||||
// modern browsers, IE9+
|
||||
const e = document.createEvent('HTMLEvents');
|
||||
e.initEvent(type, false, true);
|
||||
el.dispatchEvent(e);
|
||||
}
|
||||
}
|
||||
@@ -3,14 +3,3 @@ export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
|
||||
export const tuple = <T extends string[]>(...args: T) => args;
|
||||
|
||||
export const tupleNum = <T extends number[]>(...args: T) => args;
|
||||
|
||||
/**
|
||||
* https://stackoverflow.com/a/59187769
|
||||
* Extract the type of an element of an array/tuple without performing indexing
|
||||
*/
|
||||
export type ElementOf<T> = T extends (infer E)[] ? E : T extends readonly (infer F)[] ? F : never;
|
||||
|
||||
/**
|
||||
* https://github.com/Microsoft/TypeScript/issues/29729
|
||||
*/
|
||||
export type LiteralUnion<T extends U, U> = T | (U & {});
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
export default class UnreachableException {
|
||||
constructor(value: never) {
|
||||
return new Error(`unreachable case: ${JSON.stringify(value)}`);
|
||||
}
|
||||
}
|
||||
7
components/_util/warning.ts
Normal file
7
components/_util/warning.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import warning, { resetWarned } from 'rc-util/lib/warning';
|
||||
|
||||
export { resetWarned };
|
||||
|
||||
export default (valid: boolean, component: string, message: string): void => {
|
||||
warning(valid, `[antd: ${component}] ${message}`);
|
||||
};
|
||||
@@ -1,10 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import { supportRef, composeRef } from 'rc-util/lib/ref';
|
||||
import { findDOMNode } from 'react-dom';
|
||||
import TransitionEvents from 'css-animation/lib/Event';
|
||||
import raf from './raf';
|
||||
import { ConfigConsumer, ConfigConsumerProps, CSPConfig, ConfigContext } from '../config-provider';
|
||||
import { cloneElement } from './reactNode';
|
||||
import { ConfigConsumer, ConfigConsumerProps, CSPConfig } from '../config-provider';
|
||||
|
||||
let styleForPseudo: HTMLStyleElement | null;
|
||||
let styleForPesudo: HTMLStyleElement | null;
|
||||
|
||||
// Where el is the DOM element you'd like to test for visibility
|
||||
function isHidden(element: HTMLElement) {
|
||||
@@ -16,7 +16,7 @@ function isHidden(element: HTMLElement) {
|
||||
|
||||
function isNotGrey(color: string) {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
const match = (color || '').match(/rgba?\((\d*), (\d*), (\d*)(, [\d.]*)?\)/);
|
||||
const match = (color || '').match(/rgba?\((\d*), (\d*), (\d*)(, [\.\d]*)?\)/);
|
||||
if (match && match[1] && match[2] && match[3]) {
|
||||
return !(match[1] === match[2] && match[2] === match[3]);
|
||||
}
|
||||
@@ -24,14 +24,10 @@ function isNotGrey(color: string) {
|
||||
}
|
||||
|
||||
export default class Wave extends React.Component<{ insertExtraNode?: boolean }> {
|
||||
static contextType = ConfigContext;
|
||||
|
||||
private instance?: {
|
||||
cancel: () => void;
|
||||
};
|
||||
|
||||
private containerRef = React.createRef<HTMLDivElement>();
|
||||
|
||||
private extraNode: HTMLDivElement;
|
||||
|
||||
private clickWaveTimeoutId: number;
|
||||
@@ -40,14 +36,12 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
|
||||
|
||||
private animationStart: boolean = false;
|
||||
|
||||
private destroyed: boolean = false;
|
||||
private destroy: boolean = false;
|
||||
|
||||
private csp?: CSPConfig;
|
||||
|
||||
context: ConfigConsumerProps;
|
||||
|
||||
componentDidMount() {
|
||||
const node = this.containerRef.current as HTMLDivElement;
|
||||
const node = findDOMNode(this) as HTMLElement;
|
||||
if (!node || node.nodeType !== 1) {
|
||||
return;
|
||||
}
|
||||
@@ -62,7 +56,7 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
|
||||
clearTimeout(this.clickWaveTimeoutId);
|
||||
}
|
||||
|
||||
this.destroyed = true;
|
||||
this.destroy = true;
|
||||
}
|
||||
|
||||
onClick = (node: HTMLElement, waveColor: string) => {
|
||||
@@ -72,55 +66,51 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
|
||||
const { insertExtraNode } = this.props;
|
||||
this.extraNode = document.createElement('div');
|
||||
const { extraNode } = this;
|
||||
const { getPrefixCls } = this.context;
|
||||
extraNode.className = `${getPrefixCls('')}-click-animating-node`;
|
||||
extraNode.className = 'ant-click-animating-node';
|
||||
const attributeName = this.getAttributeName();
|
||||
node.setAttribute(attributeName, 'true');
|
||||
// Not white or transparent or grey
|
||||
styleForPseudo = styleForPseudo || document.createElement('style');
|
||||
// Not white or transparnt or grey
|
||||
styleForPesudo = styleForPesudo || document.createElement('style');
|
||||
if (
|
||||
waveColor &&
|
||||
waveColor !== '#ffffff' &&
|
||||
waveColor !== 'rgb(255, 255, 255)' &&
|
||||
isNotGrey(waveColor) &&
|
||||
!/rgba\((?:\d*, ){3}0\)/.test(waveColor) && // any transparent rgba color
|
||||
!/rgba\(\d*, \d*, \d*, 0\)/.test(waveColor) && // any transparent rgba color
|
||||
waveColor !== 'transparent'
|
||||
) {
|
||||
// Add nonce if CSP exist
|
||||
if (this.csp && this.csp.nonce) {
|
||||
styleForPseudo.nonce = this.csp.nonce;
|
||||
styleForPesudo.nonce = this.csp.nonce;
|
||||
}
|
||||
|
||||
extraNode.style.borderColor = waveColor;
|
||||
styleForPseudo.innerHTML = `
|
||||
[${getPrefixCls('')}-click-animating-without-extra-node='true']::after, .${getPrefixCls(
|
||||
'',
|
||||
)}-click-animating-node {
|
||||
styleForPesudo.innerHTML = `
|
||||
[ant-click-animating-without-extra-node='true']::after, .ant-click-animating-node {
|
||||
--antd-wave-shadow-color: ${waveColor};
|
||||
}`;
|
||||
if (!document.body.contains(styleForPseudo)) {
|
||||
document.body.appendChild(styleForPseudo);
|
||||
if (!document.body.contains(styleForPesudo)) {
|
||||
document.body.appendChild(styleForPesudo);
|
||||
}
|
||||
}
|
||||
if (insertExtraNode) {
|
||||
node.appendChild(extraNode);
|
||||
}
|
||||
['transition', 'animation'].forEach(name => {
|
||||
node.addEventListener(`${name}start`, this.onTransitionStart);
|
||||
node.addEventListener(`${name}end`, this.onTransitionEnd);
|
||||
});
|
||||
TransitionEvents.addStartEventListener(node, this.onTransitionStart);
|
||||
TransitionEvents.addEndEventListener(node, this.onTransitionEnd);
|
||||
};
|
||||
|
||||
onTransitionStart = (e: AnimationEvent) => {
|
||||
if (this.destroyed) {
|
||||
if (this.destroy) return;
|
||||
|
||||
const node = findDOMNode(this) as HTMLElement;
|
||||
if (!e || e.target !== node) {
|
||||
return;
|
||||
}
|
||||
|
||||
const node = this.containerRef.current as HTMLDivElement;
|
||||
if (!e || e.target !== node || this.animationStart) {
|
||||
return;
|
||||
if (!this.animationStart) {
|
||||
this.resetEffect(node);
|
||||
}
|
||||
this.resetEffect(node);
|
||||
};
|
||||
|
||||
onTransitionEnd = (e: AnimationEvent) => {
|
||||
@@ -131,11 +121,8 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
|
||||
};
|
||||
|
||||
getAttributeName() {
|
||||
const { getPrefixCls } = this.context;
|
||||
const { insertExtraNode } = this.props;
|
||||
return insertExtraNode
|
||||
? `${getPrefixCls('')}-click-animating`
|
||||
: `${getPrefixCls('')}-click-animating-without-extra-node`;
|
||||
return insertExtraNode ? 'ant-click-animating' : 'ant-click-animating-without-extra-node';
|
||||
}
|
||||
|
||||
bindAnimationEvent = (node: HTMLElement) => {
|
||||
@@ -184,31 +171,22 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
|
||||
const attributeName = this.getAttributeName();
|
||||
node.setAttribute(attributeName, 'false'); // edge has bug on `removeAttribute` #14466
|
||||
|
||||
if (styleForPseudo) {
|
||||
styleForPseudo.innerHTML = '';
|
||||
if (styleForPesudo) {
|
||||
styleForPesudo.innerHTML = '';
|
||||
}
|
||||
|
||||
if (insertExtraNode && this.extraNode && node.contains(this.extraNode)) {
|
||||
node.removeChild(this.extraNode);
|
||||
}
|
||||
['transition', 'animation'].forEach(name => {
|
||||
node.removeEventListener(`${name}start`, this.onTransitionStart);
|
||||
node.removeEventListener(`${name}end`, this.onTransitionEnd);
|
||||
});
|
||||
TransitionEvents.removeStartEventListener(node, this.onTransitionStart);
|
||||
TransitionEvents.removeEndEventListener(node, this.onTransitionEnd);
|
||||
}
|
||||
|
||||
renderWave = ({ csp }: ConfigConsumerProps) => {
|
||||
const { children } = this.props;
|
||||
this.csp = csp;
|
||||
|
||||
if (!React.isValidElement(children)) return children;
|
||||
|
||||
let ref: React.Ref<any> = this.containerRef;
|
||||
if (supportRef(children)) {
|
||||
ref = composeRef((children as any).ref, this.containerRef as any);
|
||||
}
|
||||
|
||||
return cloneElement(children, { ref });
|
||||
return children;
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
198
components/affix/__tests__/Affix.test.js
Normal file
198
components/affix/__tests__/Affix.test.js
Normal file
@@ -0,0 +1,198 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Affix from '..';
|
||||
import { getObserverEntities } from '../utils';
|
||||
import Button from '../../button';
|
||||
import { spyElementPrototype } from '../../__tests__/util/domHook';
|
||||
|
||||
const events = {};
|
||||
|
||||
class AffixMounter extends React.Component {
|
||||
componentDidMount() {
|
||||
this.container.addEventListener = jest.fn().mockImplementation((event, cb) => {
|
||||
events[event] = cb;
|
||||
});
|
||||
}
|
||||
|
||||
getTarget = () => this.container;
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
ref={node => {
|
||||
this.container = node;
|
||||
}}
|
||||
className="container"
|
||||
>
|
||||
<Affix
|
||||
className="fixed"
|
||||
target={this.getTarget}
|
||||
ref={ele => {
|
||||
this.affix = ele;
|
||||
}}
|
||||
{...this.props}
|
||||
>
|
||||
<Button type="primary">Fixed at the top of container</Button>
|
||||
</Affix>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
describe('Affix Render', () => {
|
||||
let wrapper;
|
||||
let domMock;
|
||||
|
||||
const classRect = {
|
||||
container: {
|
||||
top: 0,
|
||||
bottom: 100,
|
||||
},
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
domMock = spyElementPrototype(HTMLElement, 'getBoundingClientRect', function mockBounding() {
|
||||
return (
|
||||
classRect[this.className] || {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
domMock.mockRestore();
|
||||
});
|
||||
const movePlaceholder = top => {
|
||||
classRect.fixed = {
|
||||
top,
|
||||
bottom: top,
|
||||
};
|
||||
events.scroll({
|
||||
type: 'scroll',
|
||||
});
|
||||
jest.runAllTimers();
|
||||
};
|
||||
|
||||
it('Anchor render perfectly', () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
|
||||
wrapper = mount(<AffixMounter />, { attachTo: document.getElementById('mounter') });
|
||||
jest.runAllTimers();
|
||||
|
||||
movePlaceholder(0);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBeFalsy();
|
||||
|
||||
movePlaceholder(-100);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBeTruthy();
|
||||
|
||||
movePlaceholder(0);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBeFalsy();
|
||||
});
|
||||
|
||||
it('support offsetBottom', () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
|
||||
wrapper = mount(<AffixMounter offsetBottom={0} />, {
|
||||
attachTo: document.getElementById('mounter'),
|
||||
});
|
||||
|
||||
jest.runAllTimers();
|
||||
|
||||
movePlaceholder(300);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBeTruthy();
|
||||
|
||||
movePlaceholder(0);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBeFalsy();
|
||||
|
||||
movePlaceholder(300);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBeTruthy();
|
||||
});
|
||||
|
||||
it('updatePosition when offsetTop changed', () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
|
||||
wrapper = mount(<AffixMounter offsetTop={0} />, {
|
||||
attachTo: document.getElementById('mounter'),
|
||||
});
|
||||
jest.runAllTimers();
|
||||
|
||||
movePlaceholder(-100);
|
||||
expect(wrapper.instance().affix.state.affixStyle.top).toBe(0);
|
||||
wrapper.setProps({
|
||||
offsetTop: 10,
|
||||
});
|
||||
jest.runAllTimers();
|
||||
expect(wrapper.instance().affix.state.affixStyle.top).toBe(10);
|
||||
});
|
||||
|
||||
describe('updatePosition when target changed', () => {
|
||||
it('function change', () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
const container = document.querySelector('#id');
|
||||
const getTarget = () => container;
|
||||
wrapper = mount(<Affix target={getTarget} />);
|
||||
wrapper.setProps({ target: null });
|
||||
expect(wrapper.instance().state.status).toBe(0);
|
||||
expect(wrapper.instance().state.affixStyle).toBe(undefined);
|
||||
expect(wrapper.instance().state.placeholderStyle).toBe(undefined);
|
||||
});
|
||||
|
||||
it('instance change', () => {
|
||||
const getObserverLength = () => Object.keys(getObserverEntities()).length;
|
||||
|
||||
const container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
let target = container;
|
||||
|
||||
const originLength = getObserverLength();
|
||||
const getTarget = () => target;
|
||||
wrapper = mount(<Affix target={getTarget} />);
|
||||
jest.runAllTimers();
|
||||
|
||||
expect(getObserverLength()).toBe(originLength + 1);
|
||||
target = null;
|
||||
wrapper.setProps({});
|
||||
wrapper.update();
|
||||
jest.runAllTimers();
|
||||
expect(getObserverLength()).toBe(originLength);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updatePosition when size changed', () => {
|
||||
function test(name, index) {
|
||||
it(name, () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
|
||||
const updateCalled = jest.fn();
|
||||
wrapper = mount(<AffixMounter offsetBottom={0} onTestUpdatePosition={updateCalled} />, {
|
||||
attachTo: document.getElementById('mounter'),
|
||||
});
|
||||
|
||||
jest.runAllTimers();
|
||||
|
||||
movePlaceholder(300);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBeTruthy();
|
||||
jest.runAllTimers();
|
||||
wrapper.update();
|
||||
|
||||
// Mock trigger resize
|
||||
updateCalled.mockReset();
|
||||
wrapper
|
||||
.find('ResizeObserver')
|
||||
.at(index)
|
||||
.instance()
|
||||
.onResize([{ target: { getBoundingClientRect: () => ({ width: 99, height: 99 }) } }]);
|
||||
jest.runAllTimers();
|
||||
|
||||
expect(updateCalled).toHaveBeenCalled();
|
||||
});
|
||||
}
|
||||
|
||||
test('inner', 0);
|
||||
test('outer', 1);
|
||||
});
|
||||
});
|
||||
@@ -1,233 +0,0 @@
|
||||
import React from 'react';
|
||||
import { mount, ReactWrapper, HTMLAttributes } from 'enzyme';
|
||||
import ResizeObserverImpl from 'rc-resize-observer';
|
||||
import Affix, { AffixProps, AffixState } from '..';
|
||||
import { getObserverEntities } from '../utils';
|
||||
import Button from '../../button';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
|
||||
const events: Partial<Record<keyof HTMLElementEventMap, (ev: Partial<Event>) => void>> = {};
|
||||
|
||||
class AffixMounter extends React.Component<{
|
||||
offsetBottom?: number;
|
||||
offsetTop?: number;
|
||||
onTestUpdatePosition?(): void;
|
||||
onChange?: () => void;
|
||||
}> {
|
||||
private container: HTMLDivElement;
|
||||
|
||||
public affix: Affix;
|
||||
|
||||
componentDidMount() {
|
||||
this.container.addEventListener = jest
|
||||
.fn()
|
||||
.mockImplementation((event: keyof HTMLElementEventMap, cb: (ev: Partial<Event>) => void) => {
|
||||
events[event] = cb;
|
||||
});
|
||||
}
|
||||
|
||||
getTarget = () => this.container;
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
ref={node => {
|
||||
this.container = node!;
|
||||
}}
|
||||
className="container"
|
||||
>
|
||||
<Affix
|
||||
className="fixed"
|
||||
target={this.getTarget}
|
||||
ref={ele => {
|
||||
this.affix = ele!;
|
||||
}}
|
||||
{...this.props}
|
||||
>
|
||||
<Button type="primary">Fixed at the top of container</Button>
|
||||
</Affix>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
describe('Affix Render', () => {
|
||||
rtlTest(Affix);
|
||||
|
||||
const domMock = jest.spyOn(HTMLElement.prototype, 'getBoundingClientRect');
|
||||
let affixMounterWrapper: ReactWrapper<unknown, unknown, AffixMounter>;
|
||||
let affixWrapper: ReactWrapper<AffixProps, AffixState, Affix>;
|
||||
|
||||
const classRect: Record<string, DOMRect> = {
|
||||
container: {
|
||||
top: 0,
|
||||
bottom: 100,
|
||||
} as DOMRect,
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
domMock.mockImplementation(function fn(this: HTMLElement) {
|
||||
return (
|
||||
classRect[this.className] || {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
domMock.mockRestore();
|
||||
});
|
||||
|
||||
const movePlaceholder = async (top: number) => {
|
||||
classRect.fixed = {
|
||||
top,
|
||||
bottom: top,
|
||||
} as DOMRect;
|
||||
if (events.scroll == null) {
|
||||
throw new Error('scroll should be set');
|
||||
}
|
||||
events.scroll({
|
||||
type: 'scroll',
|
||||
});
|
||||
await sleep(20);
|
||||
};
|
||||
|
||||
it('Anchor render perfectly', async () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
|
||||
affixMounterWrapper = mount(<AffixMounter />, { attachTo: document.getElementById('mounter') });
|
||||
await sleep(20);
|
||||
|
||||
await movePlaceholder(0);
|
||||
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeFalsy();
|
||||
|
||||
await movePlaceholder(-100);
|
||||
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeTruthy();
|
||||
|
||||
await movePlaceholder(0);
|
||||
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeFalsy();
|
||||
});
|
||||
|
||||
it('support offsetBottom', async () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
|
||||
affixMounterWrapper = mount(<AffixMounter offsetBottom={0} />, {
|
||||
attachTo: document.getElementById('mounter'),
|
||||
});
|
||||
|
||||
await sleep(20);
|
||||
|
||||
await movePlaceholder(300);
|
||||
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeTruthy();
|
||||
|
||||
await movePlaceholder(0);
|
||||
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeFalsy();
|
||||
|
||||
await movePlaceholder(300);
|
||||
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeTruthy();
|
||||
});
|
||||
|
||||
it('updatePosition when offsetTop changed', async () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
const onChange = jest.fn();
|
||||
|
||||
affixMounterWrapper = mount(<AffixMounter offsetTop={0} onChange={onChange} />, {
|
||||
attachTo: document.getElementById('mounter'),
|
||||
});
|
||||
await sleep(20);
|
||||
|
||||
await movePlaceholder(-100);
|
||||
expect(onChange).toHaveBeenLastCalledWith(true);
|
||||
expect(affixMounterWrapper.instance().affix.state.affixStyle?.top).toBe(0);
|
||||
affixMounterWrapper.setProps({
|
||||
offsetTop: 10,
|
||||
});
|
||||
await sleep(20);
|
||||
expect(affixMounterWrapper.instance().affix.state.affixStyle?.top).toBe(10);
|
||||
});
|
||||
|
||||
describe('updatePosition when target changed', () => {
|
||||
it('function change', () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
const container = document.querySelector('#id') as HTMLDivElement;
|
||||
const getTarget = () => container;
|
||||
affixWrapper = mount(<Affix target={getTarget}>{null}</Affix>);
|
||||
affixWrapper.setProps({ target: () => null });
|
||||
expect(affixWrapper.instance().state.status).toBe(0);
|
||||
expect(affixWrapper.instance().state.affixStyle).toBe(undefined);
|
||||
expect(affixWrapper.instance().state.placeholderStyle).toBe(undefined);
|
||||
});
|
||||
|
||||
it('instance change', async () => {
|
||||
const getObserverLength = () => Object.keys(getObserverEntities()).length;
|
||||
|
||||
const container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
let target: HTMLDivElement | null = container;
|
||||
|
||||
const originLength = getObserverLength();
|
||||
const getTarget = () => target;
|
||||
affixWrapper = mount(<Affix target={getTarget}>{null}</Affix>);
|
||||
await sleep(50);
|
||||
|
||||
expect(getObserverLength()).toBe(originLength + 1);
|
||||
target = null;
|
||||
affixWrapper.setProps({});
|
||||
affixWrapper.update();
|
||||
await sleep(50);
|
||||
expect(getObserverLength()).toBe(originLength);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updatePosition when size changed', () => {
|
||||
it.each([
|
||||
{ name: 'inner', index: 0 },
|
||||
{ name: 'outer', index: 1 },
|
||||
])('inner or outer', async ({ index }) => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
|
||||
const updateCalled = jest.fn();
|
||||
affixMounterWrapper = mount(
|
||||
<AffixMounter offsetBottom={0} onTestUpdatePosition={updateCalled} />,
|
||||
{
|
||||
attachTo: document.getElementById('mounter'),
|
||||
},
|
||||
);
|
||||
|
||||
await sleep(20);
|
||||
|
||||
await movePlaceholder(300);
|
||||
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeTruthy();
|
||||
await sleep(20);
|
||||
affixMounterWrapper.update();
|
||||
|
||||
// Mock trigger resize
|
||||
updateCalled.mockReset();
|
||||
const resizeObserverInstance: ReactWrapper<
|
||||
HTMLAttributes,
|
||||
unknown,
|
||||
ResizeObserverImpl
|
||||
> = affixMounterWrapper.find('ResizeObserver') as any;
|
||||
resizeObserverInstance
|
||||
.at(index)
|
||||
.instance()
|
||||
.onResize(
|
||||
[
|
||||
{
|
||||
target: {
|
||||
getBoundingClientRect: () => ({ width: 99, height: 99 }),
|
||||
} as Element,
|
||||
contentRect: {} as DOMRect,
|
||||
},
|
||||
],
|
||||
({} as unknown) as ResizeObserver,
|
||||
);
|
||||
await sleep(20);
|
||||
|
||||
expect(updateCalled).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,9 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Affix Render rtl render component should be rendered correctly in RTL direction 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
@@ -1,7 +1,7 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders ./components/affix/demo/basic.md correctly 1`] = `
|
||||
Array [
|
||||
<div>
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
@@ -15,8 +15,8 @@ Array [
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
<br />,
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
@@ -30,8 +30,8 @@ Array [
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/affix/demo/debug.md correctly 1`] = `
|
||||
@@ -1,5 +0,0 @@
|
||||
import { imageDemoTest } from '../../../tests/shared/imageTest';
|
||||
|
||||
describe('Affix image', () => {
|
||||
imageDemoTest('affix');
|
||||
});
|
||||
@@ -13,30 +13,47 @@ title:
|
||||
|
||||
The simplest usage.
|
||||
|
||||
```tsx
|
||||
import React, { useState } from 'react';
|
||||
```jsx
|
||||
import { Affix, Button } from 'antd';
|
||||
|
||||
const Demo: React.FC = () => {
|
||||
const [top, setTop] = useState(10);
|
||||
const [bottom, setBottom] = useState(10);
|
||||
class Demo extends React.Component {
|
||||
state = {
|
||||
top: 10,
|
||||
bottom: 10,
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Affix offsetTop={top}>
|
||||
<Button type="primary" onClick={() => setTop(top + 10)}>
|
||||
Affix top
|
||||
</Button>
|
||||
</Affix>
|
||||
<br />
|
||||
<Affix offsetBottom={bottom}>
|
||||
<Button type="primary" onClick={() => setBottom(bottom + 10)}>
|
||||
Affix bottom
|
||||
</Button>
|
||||
</Affix>
|
||||
</>
|
||||
);
|
||||
};
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Affix offsetTop={this.state.top}>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
top: this.state.top + 10,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Affix top
|
||||
</Button>
|
||||
</Affix>
|
||||
<br />
|
||||
<Affix offsetBottom={this.state.bottom}>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
bottom: this.state.bottom + 10,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Affix bottom
|
||||
</Button>
|
||||
</Affix>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<Demo />, mountNode);
|
||||
```
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
order: 99
|
||||
title:
|
||||
zh-CN: 调整浏览器大小,观察 Affix 容器是否发生变化。跟随变化为正常。#17678
|
||||
en-US: debug
|
||||
zh-CN: 调试
|
||||
en-US: Debug
|
||||
debug: true
|
||||
---
|
||||
|
||||
@@ -14,26 +14,37 @@ DEBUG
|
||||
|
||||
DEBUG
|
||||
|
||||
```tsx
|
||||
import React, { useState } from 'react';
|
||||
```jsx
|
||||
import { Affix, Button } from 'antd';
|
||||
|
||||
const Demo: React.FC = () => {
|
||||
const [top, setTop] = useState(10);
|
||||
return (
|
||||
<div style={{ height: 10000 }}>
|
||||
<div>Top</div>
|
||||
<Affix offsetTop={top}>
|
||||
<div style={{ background: 'red' }}>
|
||||
<Button type="primary" onClick={() => setTop(top + 10)}>
|
||||
Affix top
|
||||
</Button>
|
||||
</div>
|
||||
</Affix>
|
||||
<div>Bottom</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
class Demo extends React.Component {
|
||||
state = {
|
||||
top: 10,
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div style={{ height: 10000 }}>
|
||||
<div>Top</div>
|
||||
<Affix offsetTop={this.state.top}>
|
||||
<div style={{ background: 'red' }}>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
top: this.state.top + 10,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Affix top
|
||||
</Button>
|
||||
</div>
|
||||
</Affix>
|
||||
<div>Bottom</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<Demo />, mountNode);
|
||||
```
|
||||
|
||||
@@ -13,7 +13,7 @@ title:
|
||||
|
||||
Callback with affixed state.
|
||||
|
||||
```tsx
|
||||
```jsx
|
||||
import { Affix, Button } from 'antd';
|
||||
|
||||
ReactDOM.render(
|
||||
|
||||
@@ -13,22 +13,27 @@ title:
|
||||
|
||||
Set a `target` for 'Affix', which is listen to scroll event of target element (default is `window`).
|
||||
|
||||
```tsx
|
||||
import React, { useState } from 'react';
|
||||
```jsx
|
||||
import { Affix, Button } from 'antd';
|
||||
|
||||
const Demo: React.FC = () => {
|
||||
const [container, setContainer] = useState(null);
|
||||
return (
|
||||
<div className="scrollable-container" ref={setContainer}>
|
||||
<div className="background">
|
||||
<Affix target={() => container}>
|
||||
<Button type="primary">Fixed at the top of container</Button>
|
||||
</Affix>
|
||||
class Demo extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
className="scrollable-container"
|
||||
ref={node => {
|
||||
this.container = node;
|
||||
}}
|
||||
>
|
||||
<div className="background">
|
||||
<Affix target={() => this.container}>
|
||||
<Button type="primary">Fixed at the top of container</Button>
|
||||
</Affix>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<Demo />, mountNode);
|
||||
```
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
category: Components
|
||||
type: Navigation
|
||||
title: Affix
|
||||
cover: https://gw.alipayobjects.com/zos/alicdn/tX6-md4H6/Affix.svg
|
||||
---
|
||||
|
||||
Wrap Affix around another component to make it stick the viewport.
|
||||
@@ -15,12 +14,12 @@ Please note that Affix should not cover other content on the page, especially wh
|
||||
|
||||
## API
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| offsetBottom | Offset from the bottom of the viewport (in pixels) | number | - |
|
||||
| offsetTop | Offset from the top of the viewport (in pixels) | number | 0 |
|
||||
| target | Specifies the scrollable area DOM node | () => HTMLElement | () => window |
|
||||
| onChange | Callback for when Affix state is changed | function(affixed) | - |
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| offsetBottom | Offset from the bottom of the viewport (in pixels) | number | - | |
|
||||
| offsetTop | Offset from the top of the viewport (in pixels) | number | 0 | |
|
||||
| target | Specifies the scrollable area DOM node | () => HTMLElement | () => window | |
|
||||
| onChange | Callback for when Affix state is changed | Function(affixed) | - | |
|
||||
|
||||
**Note:** Children of `Affix` must not have the property `position: absolute`, but you can set `position: absolute` on `Affix` itself:
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import * as React from 'react';
|
||||
import { polyfill } from 'react-lifecycles-compat';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'omit.js';
|
||||
import ResizeObserver from 'rc-resize-observer';
|
||||
import { ConfigContext, ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import { throttleByAnimationFrameDecorator } from '../_util/throttleByAnimationFrame';
|
||||
|
||||
import warning from '../_util/warning';
|
||||
import {
|
||||
addObserveTarget,
|
||||
removeObserveTarget,
|
||||
@@ -23,6 +25,7 @@ export interface AffixProps {
|
||||
* 距离窗口顶部达到指定偏移量后触发
|
||||
*/
|
||||
offsetTop?: number;
|
||||
offset?: number;
|
||||
/** 距离窗口底部达到指定偏移量后触发 */
|
||||
offsetBottom?: number;
|
||||
style?: React.CSSProperties;
|
||||
@@ -32,7 +35,7 @@ export interface AffixProps {
|
||||
target?: () => Window | HTMLElement | null;
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
children: React.ReactElement;
|
||||
}
|
||||
|
||||
enum AffixStatus {
|
||||
@@ -50,7 +53,9 @@ export interface AffixState {
|
||||
}
|
||||
|
||||
class Affix extends React.Component<AffixProps, AffixState> {
|
||||
static contextType = ConfigContext;
|
||||
static defaultProps = {
|
||||
target: getDefaultTarget,
|
||||
};
|
||||
|
||||
state: AffixState = {
|
||||
status: AffixStatus.None,
|
||||
@@ -64,27 +69,14 @@ class Affix extends React.Component<AffixProps, AffixState> {
|
||||
|
||||
private timeout: number;
|
||||
|
||||
context: ConfigConsumerProps;
|
||||
|
||||
private getTargetFunc() {
|
||||
const { getTargetContainer } = this.context;
|
||||
const { target } = this.props;
|
||||
|
||||
if (target !== undefined) {
|
||||
return target;
|
||||
}
|
||||
|
||||
return getTargetContainer || getDefaultTarget;
|
||||
}
|
||||
|
||||
// Event handler
|
||||
componentDidMount() {
|
||||
const targetFunc = this.getTargetFunc();
|
||||
if (targetFunc) {
|
||||
const { target } = this.props;
|
||||
if (target) {
|
||||
// [Legacy] Wait for parent component ref has its value.
|
||||
// We should use target as directly element instead of function which makes element check hard.
|
||||
this.timeout = setTimeout(() => {
|
||||
addObserveTarget(targetFunc(), this);
|
||||
addObserveTarget(target(), this);
|
||||
// Mock Event object.
|
||||
this.updatePosition();
|
||||
});
|
||||
@@ -93,10 +85,10 @@ class Affix extends React.Component<AffixProps, AffixState> {
|
||||
|
||||
componentDidUpdate(prevProps: AffixProps) {
|
||||
const { prevTarget } = this.state;
|
||||
const targetFunc = this.getTargetFunc();
|
||||
const { target } = this.props;
|
||||
let newTarget = null;
|
||||
if (targetFunc) {
|
||||
newTarget = targetFunc() || null;
|
||||
if (target) {
|
||||
newTarget = target() || null;
|
||||
}
|
||||
|
||||
if (prevTarget !== newTarget) {
|
||||
@@ -107,7 +99,6 @@ class Affix extends React.Component<AffixProps, AffixState> {
|
||||
this.updatePosition();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/no-did-update-set-state
|
||||
this.setState({ prevTarget: newTarget });
|
||||
}
|
||||
|
||||
@@ -130,8 +121,17 @@ class Affix extends React.Component<AffixProps, AffixState> {
|
||||
}
|
||||
|
||||
getOffsetTop = () => {
|
||||
const { offsetBottom } = this.props;
|
||||
const { offset, offsetBottom } = this.props;
|
||||
let { offsetTop } = this.props;
|
||||
if (typeof offsetTop === 'undefined') {
|
||||
offsetTop = offset;
|
||||
warning(
|
||||
typeof offset === 'undefined',
|
||||
'Affix',
|
||||
'`offset` is deprecated. Please use `offsetTop` instead.',
|
||||
);
|
||||
}
|
||||
|
||||
if (offsetBottom === undefined && offsetTop === undefined) {
|
||||
offsetTop = 0;
|
||||
}
|
||||
@@ -153,16 +153,15 @@ class Affix extends React.Component<AffixProps, AffixState> {
|
||||
// =================== Measure ===================
|
||||
measure = () => {
|
||||
const { status, lastAffix } = this.state;
|
||||
const { onChange } = this.props;
|
||||
const targetFunc = this.getTargetFunc();
|
||||
if (status !== AffixStatus.Prepare || !this.fixedNode || !this.placeholderNode || !targetFunc) {
|
||||
const { target, onChange } = this.props;
|
||||
if (status !== AffixStatus.Prepare || !this.fixedNode || !this.placeholderNode || !target) {
|
||||
return;
|
||||
}
|
||||
|
||||
const offsetTop = this.getOffsetTop();
|
||||
const offsetBottom = this.getOffsetBottom();
|
||||
|
||||
const targetNode = targetFunc();
|
||||
const targetNode = target();
|
||||
if (!targetNode) {
|
||||
return;
|
||||
}
|
||||
@@ -233,15 +232,15 @@ class Affix extends React.Component<AffixProps, AffixState> {
|
||||
|
||||
@throttleByAnimationFrameDecorator()
|
||||
lazyUpdatePosition() {
|
||||
const targetFunc = this.getTargetFunc();
|
||||
const { target } = this.props;
|
||||
const { affixStyle } = this.state;
|
||||
|
||||
// Check position change before measure to make Safari smooth
|
||||
if (targetFunc && affixStyle) {
|
||||
if (target && affixStyle) {
|
||||
const offsetTop = this.getOffsetTop();
|
||||
const offsetBottom = this.getOffsetBottom();
|
||||
|
||||
const targetNode = targetFunc();
|
||||
const targetNode = target();
|
||||
if (targetNode && this.placeholderNode) {
|
||||
const targetRect = getTargetRect(targetNode);
|
||||
const placeholderReact = getTargetRect(this.placeholderNode);
|
||||
@@ -262,8 +261,7 @@ class Affix extends React.Component<AffixProps, AffixState> {
|
||||
}
|
||||
|
||||
// =================== Render ===================
|
||||
render = () => {
|
||||
const { getPrefixCls } = this.context;
|
||||
renderAffix = ({ getPrefixCls }: ConfigConsumerProps) => {
|
||||
const { affixStyle, placeholderStyle } = this.state;
|
||||
const { prefixCls, children } = this.props;
|
||||
const className = classNames({
|
||||
@@ -297,6 +295,12 @@ class Affix extends React.Component<AffixProps, AffixState> {
|
||||
</ResizeObserver>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderAffix}</ConfigConsumer>;
|
||||
}
|
||||
}
|
||||
|
||||
polyfill(Affix);
|
||||
|
||||
export default Affix;
|
||||
|
||||
@@ -3,7 +3,6 @@ category: Components
|
||||
subtitle: 固钉
|
||||
type: 导航
|
||||
title: Affix
|
||||
cover: https://gw.alipayobjects.com/zos/alicdn/tX6-md4H6/Affix.svg
|
||||
---
|
||||
|
||||
将页面元素钉在可视范围。
|
||||
@@ -16,12 +15,12 @@ cover: https://gw.alipayobjects.com/zos/alicdn/tX6-md4H6/Affix.svg
|
||||
|
||||
## API
|
||||
|
||||
| 成员 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| offsetBottom | 距离窗口底部达到指定偏移量后触发 | number | - |
|
||||
| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | - |
|
||||
| target | 设置 `Affix` 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 | () => HTMLElement | () => window |
|
||||
| onChange | 固定状态改变时触发的回调函数 | function(affixed) | - |
|
||||
| 成员 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| offsetBottom | 距离窗口底部达到指定偏移量后触发 | number | | |
|
||||
| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | | |
|
||||
| target | 设置 `Affix` 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 | () => HTMLElement | () => window | |
|
||||
| onChange | 固定状态改变时触发的回调函数 | Function(affixed) | 无 | |
|
||||
|
||||
**注意:**`Affix` 内的元素不要使用绝对定位,如需要绝对定位的效果,可以直接设置 `Affix` 为绝对定位:
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import Alert from '.';
|
||||
|
||||
interface ErrorBoundaryProps {
|
||||
message?: React.ReactNode;
|
||||
description?: React.ReactNode;
|
||||
}
|
||||
|
||||
export default class ErrorBoundary extends React.Component<
|
||||
ErrorBoundaryProps,
|
||||
{
|
||||
error?: Error | null;
|
||||
info: {
|
||||
componentStack?: string;
|
||||
};
|
||||
}
|
||||
> {
|
||||
state = {
|
||||
error: undefined,
|
||||
info: {
|
||||
componentStack: '',
|
||||
},
|
||||
};
|
||||
|
||||
componentDidCatch(error: Error | null, info: object) {
|
||||
this.setState({ error, info });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { message, description, children } = this.props;
|
||||
const { error, info } = this.state;
|
||||
const componentStack = info && info.componentStack ? info.componentStack : null;
|
||||
const errorMessage = typeof message === 'undefined' ? (error || '').toString() : message;
|
||||
const errorDescription = typeof description === 'undefined' ? componentStack : description;
|
||||
if (error) {
|
||||
return (
|
||||
<Alert type="error" message={errorMessage} description={<pre>{errorDescription}</pre>} />
|
||||
);
|
||||
}
|
||||
return children;
|
||||
}
|
||||
}
|
||||
1000
components/alert/__tests__/__snapshots__/demo.test.js.snap
Normal file
1000
components/alert/__tests__/__snapshots__/demo.test.js.snap
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,123 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Alert action of Alert custom action 1`] = `
|
||||
<div
|
||||
class="ant-alert ant-alert-success"
|
||||
data-show="true"
|
||||
role="alert"
|
||||
>
|
||||
<span
|
||||
aria-label="check-circle"
|
||||
class="anticon anticon-check-circle ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="check-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 01-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<div
|
||||
class="ant-alert-content"
|
||||
>
|
||||
<div
|
||||
class="ant-alert-message"
|
||||
>
|
||||
Success Tips
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert-action"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-text ant-btn-sm"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
UNDO
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
class="ant-alert-close-icon"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Alert could accept none react element icon 1`] = `
|
||||
<div
|
||||
class="ant-alert ant-alert-success"
|
||||
data-show="true"
|
||||
role="alert"
|
||||
>
|
||||
<span
|
||||
class="ant-alert-icon"
|
||||
>
|
||||
icon
|
||||
</span>
|
||||
<div
|
||||
class="ant-alert-content"
|
||||
>
|
||||
<div
|
||||
class="ant-alert-message"
|
||||
>
|
||||
Success Tips
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Alert rtl render component should be rendered correctly in RTL direction 1`] = `
|
||||
<div
|
||||
class="ant-alert ant-alert-info ant-alert-no-icon ant-alert-rtl"
|
||||
data-show="true"
|
||||
role="alert"
|
||||
>
|
||||
<div
|
||||
class="ant-alert-content"
|
||||
>
|
||||
<div
|
||||
class="ant-alert-message"
|
||||
/>
|
||||
<div
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -1,5 +0,0 @@
|
||||
import { imageDemoTest } from '../../../tests/shared/imageTest';
|
||||
|
||||
describe('Alert image', () => {
|
||||
imageDemoTest('alert');
|
||||
});
|
||||
67
components/alert/__tests__/index.test.js
Normal file
67
components/alert/__tests__/index.test.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Alert from '..';
|
||||
|
||||
describe('Alert', () => {
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('could be closed', () => {
|
||||
const onClose = jest.fn();
|
||||
const afterClose = jest.fn();
|
||||
const wrapper = mount(
|
||||
<Alert
|
||||
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
|
||||
type="warning"
|
||||
closable
|
||||
onClose={onClose}
|
||||
afterClose={afterClose}
|
||||
/>,
|
||||
);
|
||||
wrapper.find('.ant-alert-close-icon').simulate('click');
|
||||
expect(onClose).toHaveBeenCalled();
|
||||
jest.runAllTimers();
|
||||
expect(afterClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('data and aria props', () => {
|
||||
it('sets data attributes on input', () => {
|
||||
const wrapper = mount(<Alert data-test="test-id" data-id="12345" />);
|
||||
const input = wrapper.find('.ant-alert').getDOMNode();
|
||||
expect(input.getAttribute('data-test')).toBe('test-id');
|
||||
expect(input.getAttribute('data-id')).toBe('12345');
|
||||
});
|
||||
|
||||
it('sets aria attributes on input', () => {
|
||||
const wrapper = mount(<Alert aria-describedby="some-label" />);
|
||||
const input = wrapper.find('.ant-alert').getDOMNode();
|
||||
expect(input.getAttribute('aria-describedby')).toBe('some-label');
|
||||
});
|
||||
|
||||
it('sets role attribute on input', () => {
|
||||
const wrapper = mount(<Alert role="status" />);
|
||||
const input = wrapper.find('.ant-alert').getDOMNode();
|
||||
expect(input.getAttribute('role')).toBe('status');
|
||||
});
|
||||
});
|
||||
|
||||
it('warning for props#iconType', () => {
|
||||
const warnSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
mount(
|
||||
<Alert
|
||||
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
|
||||
type="warning"
|
||||
iconType="up"
|
||||
/>,
|
||||
);
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Alert] `iconType` is deprecated. Please use `icon` instead.',
|
||||
);
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user