mirror of
https://github.com/ant-design/ant-design.git
synced 2026-02-16 22:32:29 +08:00
Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90da05750c | ||
|
|
e2b9e40d9d | ||
|
|
226bce705a | ||
|
|
df2425652c | ||
|
|
4751d109e3 | ||
|
|
2c8107c8bd | ||
|
|
56870dd7b3 | ||
|
|
e94133a20b | ||
|
|
50f1605acc | ||
|
|
96f22840d9 | ||
|
|
6f78142360 | ||
|
|
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,5 @@ module.exports = {
|
||||
dist: {
|
||||
finalize: finalizeDist,
|
||||
},
|
||||
generateThemeFileContent,
|
||||
tag: 'previous',
|
||||
};
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"test": "./dist/antd.min.js",
|
||||
"maxSize": "300 kB",
|
||||
"compression": "gzip"
|
||||
},
|
||||
{
|
||||
"test": "./dist/antd.min.css",
|
||||
"maxSize": "65 kB",
|
||||
"compression": "gzip"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -10,6 +10,13 @@ references:
|
||||
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
|
||||
@@ -22,10 +29,10 @@ references:
|
||||
ignore: gh-pages
|
||||
- dist:
|
||||
requires:
|
||||
- setup
|
||||
- setup
|
||||
- compile:
|
||||
requires:
|
||||
- setup
|
||||
- setup
|
||||
- lint:
|
||||
requires:
|
||||
- setup
|
||||
@@ -44,6 +51,21 @@ references:
|
||||
- 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
|
||||
@@ -151,6 +173,69 @@ jobs:
|
||||
- *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:
|
||||
@@ -166,7 +251,7 @@ workflows:
|
||||
<<: *workflow
|
||||
triggers:
|
||||
- schedule:
|
||||
cron: '0 0 * * *'
|
||||
cron: "0 0 * * *"
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
|
||||
@@ -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,19 +4,8 @@ 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/**/*
|
||||
@@ -24,5 +13,3 @@ node_modules
|
||||
_site
|
||||
dist
|
||||
**/*.d.ts
|
||||
# Scripts
|
||||
scripts/previewEditor/**/*
|
||||
62
.eslintrc.js
62
.eslintrc.js
@@ -1,4 +1,4 @@
|
||||
module.exports = {
|
||||
const eslintrc = {
|
||||
extends: [
|
||||
'airbnb',
|
||||
'prettier',
|
||||
@@ -20,38 +20,13 @@ 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,
|
||||
'import/no-unresolved': 0,
|
||||
'jsx-a11y/control-has-associated-label': 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -62,7 +37,6 @@ module.exports = {
|
||||
'react/forbid-prop-types': 0,
|
||||
'react/jsx-indent': 0,
|
||||
'react/jsx-wrap-multilines': ['error', { declaration: false, assignment: false }],
|
||||
'import/extensions': 0,
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{
|
||||
@@ -113,13 +87,35 @@ module.exports = {
|
||||
'react/static-property-placement': 0,
|
||||
'jest/no-test-callback': 0,
|
||||
'jest/expect-expect': 0,
|
||||
'react-hooks/rules-of-hooks': 2, // Checks rules of Hooks
|
||||
'unicorn/better-regex': 2,
|
||||
'unicorn/prefer-trim-start-end': 2,
|
||||
'unicorn/expiring-todo-comments': 2,
|
||||
'unicorn/no-abusive-eslint-disable': 2,
|
||||
'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.
|
||||
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -4,20 +4,17 @@ First of all, thank you for your contribution! 😄
|
||||
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
|
||||
- [ ] Perfermance optimization
|
||||
- [ ] Refactoring
|
||||
- [ ] Code style optimization
|
||||
- [ ] Test Case
|
||||
@@ -51,8 +48,6 @@ Describe changes from userside, and list all potential break changes or other ri
|
||||
|
||||
### ☑️ 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
|
||||
|
||||
7
.github/PULL_REQUEST_TEMPLATE/pr_cn.md
vendored
7
.github/PULL_REQUEST_TEMPLATE/pr_cn.md
vendored
@@ -13,11 +13,8 @@
|
||||
- [ ] 新特性提交
|
||||
- [ ] 日常 bug 修复
|
||||
- [ ] 站点、文档改进
|
||||
- [ ] 演示代码改进
|
||||
- [ ] 组件样式改进
|
||||
- [ ] TypeScript 定义更新
|
||||
- [ ] 包体积优化
|
||||
- [ ] 性能优化
|
||||
- [ ] 重构
|
||||
- [ ] 代码风格优化
|
||||
- [ ] 测试用例
|
||||
@@ -49,9 +46,9 @@
|
||||
| 🇺🇸 英文 | |
|
||||
| 🇨🇳 中文 | |
|
||||
|
||||
### ☑️ 请求合并前的自查清单
|
||||
- 中文(可选):
|
||||
|
||||
⚠️ 请自检并全部**勾选全部选项**。⚠️
|
||||
### ☑️ 请求合并前的自查清单
|
||||
|
||||
- [ ] 文档已补充或无须补充
|
||||
- [ ] 代码演示已提供或无须提供
|
||||
|
||||
30
.github/workflows/deploy-site.yml
vendored
30
.github/workflows/deploy-site.yml
vendored
@@ -1,30 +0,0 @@
|
||||
name: Deploy website
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: install
|
||||
run: npm install
|
||||
|
||||
- name: build
|
||||
run: npm run predeploy
|
||||
|
||||
- name: deploy
|
||||
uses: peaceiris/actions-gh-pages@v2
|
||||
env:
|
||||
ACTIONS_DEPLOY_KEY: ${{ secrets.ACTIONS_DEPLOY_KEY }}
|
||||
# PERSONAL_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PUBLISH_BRANCH: gh-pages
|
||||
PUBLISH_DIR: ./_site
|
||||
with:
|
||||
emptyCommits: false
|
||||
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
|
||||
24
.github/workflows/lighthouse-ci.yml
vendored
24
.github/workflows/lighthouse-ci.yml
vendored
@@ -1,11 +1,19 @@
|
||||
name: CI
|
||||
on: [push]
|
||||
name: Lighthouse
|
||||
on: push
|
||||
jobs:
|
||||
lighthouseci:
|
||||
lighthouse:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
- run: npm install && npm install -g @lhci/cli@0.4.x
|
||||
- run: npm run site
|
||||
- run: lhci autorun --upload.target=temporary-public-storage
|
||||
- uses: actions/checkout@v1
|
||||
- name: Audit URLs using Lighthouse
|
||||
uses: treosh/lighthouse-ci-action@v2
|
||||
with:
|
||||
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 }}
|
||||
|
||||
21
.gitignore
vendored
21
.gitignore
vendored
@@ -25,7 +25,6 @@ nohup.out
|
||||
_site
|
||||
_data
|
||||
dist
|
||||
report.html
|
||||
/lib
|
||||
/es
|
||||
elasticsearch-*
|
||||
@@ -39,26 +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__/
|
||||
/jest-stare
|
||||
*.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\\.js$',
|
||||
testEnvironment: 'node',
|
||||
transformIgnorePatterns,
|
||||
snapshotSerializers: ['enzyme-to-json/serializer'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsConfigFile: './tsconfig.test.json',
|
||||
},
|
||||
},
|
||||
reporters: ['default', 'jest-stare'],
|
||||
};
|
||||
18
.jest.js
18
.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: {
|
||||
@@ -26,14 +18,14 @@ module.exports = {
|
||||
'^react-dnd-test-backend$': 'react-dnd-test-backend/dist/cjs',
|
||||
'^react-dnd-test-utils$': 'react-dnd-test-utils/dist/cjs',
|
||||
},
|
||||
testPathIgnorePatterns: ['/node_modules/', 'dekko', 'node', 'image.test.js'],
|
||||
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',
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
18
.travis.yml
18
.travis.yml
@@ -13,11 +13,19 @@ matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- env: TEST_TYPE=lint
|
||||
- env: TEST_TYPE=test:dist
|
||||
- env: TEST_TYPE=test:lib
|
||||
- env: TEST_TYPE=test:es
|
||||
- env: TEST_TYPE=test:dom
|
||||
- env: TEST_TYPE=test:node
|
||||
- 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
|
||||
|
||||
@@ -1010,6 +1010,7 @@ zytjs <yitongzhao@163.com>
|
||||
郭延豪(708674) <gyh9457@163.com>
|
||||
愚指导-TZ <yutingzhao1991@sina.com>
|
||||
杨小事er <Uiryzd@163.com>
|
||||
杨小事er <uiryzd@163.com>
|
||||
超能刚哥 <margox@foxmail.com>
|
||||
马金花儿 <o.o@mug.dog>
|
||||
रोहन मल्होत्रा <rohan.malhotra@adwyze.com>
|
||||
|
||||
3507
CHANGELOG.en-US.md
3507
CHANGELOG.en-US.md
File diff suppressed because it is too large
Load Diff
3512
CHANGELOG.zh-CN.md
3512
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,7 +0,0 @@
|
||||
FROM buildkite/puppeteer:latest
|
||||
RUN mkdir /app
|
||||
WORKDIR /app
|
||||
COPY package.json ./
|
||||
RUN npm install
|
||||
ENV PATH="${PATH}:/app/node_modules/.bin"
|
||||
COPY . .
|
||||
166
README-pt_BR.md
166
README-pt_BR.md
@@ -1,166 +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.
|
||||
|
||||
[![CircleCI status][circleci-image]][circleci-url] [![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
|
||||
[circleci-image]: https://img.shields.io/travis/com/ant-design/ant-design.svg?style=flat-square
|
||||
[circleci-url]: https://travis-ci.com/ant-design/ant-design
|
||||
[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,46 +10,17 @@
|
||||
|
||||
一套企业级 UI 设计语言和 React 组件库。
|
||||
|
||||
[![CircleCI status][circleci-image]][circleci-url] [![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
|
||||
[circleci-image]: https://img.shields.io/travis/com/ant-design/ant-design.svg?style=flat-square
|
||||
[circleci-url]: https://travis-ci.com/ant-design/ant-design
|
||||
[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
|
||||
[](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) | 简体中文
|
||||
|
||||
## ✨ 特性
|
||||
|
||||
@@ -60,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 |
|
||||
|
||||
## 📦 安装
|
||||
|
||||
@@ -82,7 +53,7 @@ yarn add antd
|
||||
|
||||
## 🔨 示例
|
||||
|
||||
```jsx
|
||||
````jsx
|
||||
import { Button, DatePicker } from 'antd';
|
||||
|
||||
const App = () => (
|
||||
@@ -91,32 +62,28 @@ 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/)
|
||||
- [Ant Design Charts](https://charts.ant.design)
|
||||
- [更新日志](CHANGELOG.en-US.md)
|
||||
- [React 底层基础组件](http://react-component.github.io/)
|
||||
- [移动端组件](http://mobile.ant.design)
|
||||
@@ -134,8 +101,7 @@ import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
|
||||
- [常见问题](https://ant.design/docs/react/faq-cn)
|
||||
- [CodeSandbox 模板](https://u.ant.design/codesandbox-repro) for bug reports
|
||||
- [Awesome Ant Design](https://github.com/websemantics/awesome-ant-design)
|
||||
- [定制主题](https://ant.design/docs/react/customize-theme-cn)
|
||||
- [成为社区协作成员](https://github.com/ant-design/ant-design/wiki/Collaborators#how-to-apply-for-being-a-collaborator)
|
||||
- [定制主题](http://ant.design/docs/react/customize-theme-cn)
|
||||
|
||||
## ⌨️ 本地开发
|
||||
|
||||
|
||||
67
README.md
67
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,46 +10,17 @@
|
||||
|
||||
An enterprise-class UI design language and React UI library.
|
||||
|
||||
[![CircleCI status][circleci-image]][circleci-url] [![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) [](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
|
||||
[circleci-image]: https://img.shields.io/travis/com/ant-design/ant-design.svg?style=flat-square
|
||||
[circleci-url]: https://travis-ci.com/ant-design/ant-design
|
||||
[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
|
||||
[](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
|
||||
|
||||
@@ -62,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
|
||||
|
||||
@@ -88,7 +59,7 @@ import { Button, DatePicker } from 'antd';
|
||||
const App = () => (
|
||||
<>
|
||||
<Button type="primary">PRESS ME</Button>
|
||||
<DatePicker placeholder="select date" />
|
||||
<DatePicker />
|
||||
</>
|
||||
);
|
||||
```
|
||||
@@ -99,20 +70,21 @@ 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](https://ant.design/components/overview)
|
||||
- [Home page](http://ant.design/)
|
||||
- [Components](http://ant.design/docs/react/introduce)
|
||||
- [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)
|
||||
@@ -129,8 +101,7 @@ Dozens of languages supported in `antd`, see [i18n](https://ant.design/docs/reac
|
||||
- [FAQ](https://ant.design/docs/react/faq)
|
||||
- [CodeSandbox Template](https://u.ant.design/codesandbox-repro) for bug reports
|
||||
- [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)
|
||||
- [Customize Theme](http://ant.design/docs/react/customize-theme)
|
||||
|
||||
## ⌨️ Development
|
||||
|
||||
|
||||
@@ -1,117 +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
|
||||
jobs:
|
||||
- job: Build_Site
|
||||
steps:
|
||||
- checkout: self
|
||||
displayName: 'Checkout'
|
||||
clean: true
|
||||
fetchDepth: 1
|
||||
- task: NodeTool@0
|
||||
displayName: 'Install Node.js'
|
||||
inputs:
|
||||
versionSpec: '12.13.1'
|
||||
- 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: '12.13.1'
|
||||
- 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'
|
||||
- stage: ui
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- job: UI_Test
|
||||
steps:
|
||||
- checkout: self
|
||||
displayName: 'Checkout'
|
||||
clean: true
|
||||
fetchDepth: 1
|
||||
- task: NodeTool@0
|
||||
displayName: 'Install Node.js'
|
||||
inputs:
|
||||
versionSpec: '12.16.3'
|
||||
- script: npm install
|
||||
displayName: 'Install modules'
|
||||
- script: |
|
||||
node ./scripts/azure-github-comment.js -ui "[](https://dev.azure.com/ant-design/ant-design/_build/results?buildId=$(Build.BuildId))"
|
||||
displayName: 'Comment on github'
|
||||
- script: npm run test-image
|
||||
displayName: 'UI Test'
|
||||
- task: PublishPipelineArtifact@1
|
||||
inputs:
|
||||
targetPath: $(System.DefaultWorkingDirectory)/jest-stare
|
||||
artifactName: jestStare
|
||||
condition: failed()
|
||||
- script: |
|
||||
node ./scripts/azure-github-comment.js -ui "[<img width="306" src="https://user-images.githubusercontent.com/14831261/82744259-6e5ee200-9da8-11ea-8479-685f6e280b77.jpg">](https://dev.azure.com/ant-design/ant-design/_build/results?buildId=$(Build.BuildId))"
|
||||
displayName: 'Update comment on github'
|
||||
- job: UI_Test_Failed
|
||||
dependsOn: UI_Test
|
||||
condition: failed()
|
||||
steps:
|
||||
- checkout: self
|
||||
displayName: 'Checkout'
|
||||
clean: true
|
||||
fetchDepth: 1
|
||||
- task: NodeTool@0
|
||||
displayName: 'Install Node.js'
|
||||
inputs:
|
||||
versionSpec: '12.13.1'
|
||||
- script: npm install
|
||||
displayName: 'Install modules'
|
||||
- task: DownloadPipelineArtifact@2
|
||||
inputs:
|
||||
artifact: jestStare
|
||||
path: './jest-stare'
|
||||
- script: ls -al ./jest-stare
|
||||
displayName: 'List report'
|
||||
- script: |
|
||||
export DEPLOY_DOMAIN=https://ui-test-${SYSTEM_PULLREQUEST_PULLREQUESTNUMBER}-ant-design.surge.sh
|
||||
echo "Deploy to $DEPLOY_DOMAIN"
|
||||
npx surge --project ./jest-stare --domain $DEPLOY_DOMAIN
|
||||
displayName: 'Deploy Report Site'
|
||||
- script: |
|
||||
node ./scripts/azure-github-comment.js -ui "[<img width="306" src="https://user-images.githubusercontent.com/14831261/82744257-6dc64b80-9da8-11ea-80cf-05b2279a5602.jpg">](https://ui-test-${SYSTEM_PULLREQUEST_PULLREQUESTNUMBER}-ant-design.surge.sh)"
|
||||
displayName: 'Update 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,11 +27,12 @@ Array [
|
||||
"Drawer",
|
||||
"Empty",
|
||||
"Form",
|
||||
"Grid",
|
||||
"Icon",
|
||||
"Input",
|
||||
"InputNumber",
|
||||
"Layout",
|
||||
"List",
|
||||
"LocaleProvider",
|
||||
"message",
|
||||
"Menu",
|
||||
"Mentions",
|
||||
@@ -50,7 +51,6 @@ Array [
|
||||
"Select",
|
||||
"Skeleton",
|
||||
"Slider",
|
||||
"Space",
|
||||
"Spin",
|
||||
"Steps",
|
||||
"Switch",
|
||||
@@ -64,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,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\\""`);
|
||||
});
|
||||
});
|
||||
@@ -5,36 +5,41 @@ import KeyCode from 'rc-util/lib/KeyCode';
|
||||
import delayRaf from '../raf';
|
||||
import throttleByAnimationFrame from '../throttleByAnimationFrame';
|
||||
import getDataOrAriaProps from '../getDataOrAriaProps';
|
||||
import triggerEvent from '../triggerEvent';
|
||||
import Wave from '../wave';
|
||||
import TransButton from '../transButton';
|
||||
import openAnimation from '../openAnimation';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
|
||||
describe('Test utils function', () => {
|
||||
focusTest(TransButton);
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
it('throttle function should work', async () => {
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('throttle function should work', () => {
|
||||
const callback = jest.fn();
|
||||
const throttled = throttleByAnimationFrame(callback);
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
throttled();
|
||||
throttled();
|
||||
await sleep(20);
|
||||
|
||||
jest.runAllTimers();
|
||||
expect(callback).toHaveBeenCalled();
|
||||
expect(callback.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('throttle function should be canceled', async () => {
|
||||
it('throttle function should be canceled', () => {
|
||||
const callback = jest.fn();
|
||||
const throttled = throttleByAnimationFrame(callback);
|
||||
|
||||
throttled();
|
||||
throttled.cancel();
|
||||
await sleep(20);
|
||||
|
||||
jest.runAllTimers();
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -121,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(
|
||||
@@ -165,6 +183,9 @@ describe('Test utils function', () => {
|
||||
});
|
||||
|
||||
it('should not throw when no children', () => {
|
||||
if (process.env.REACT === '15') {
|
||||
return;
|
||||
}
|
||||
expect(() => mount(<Wave />)).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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}`);
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ interface Motion {
|
||||
motionEnter?: boolean;
|
||||
motionLeave?: boolean;
|
||||
motionLeaveImmediately?: boolean; // Trigger leave motion immediately
|
||||
motionDeadline?: number;
|
||||
removeOnLeave?: boolean;
|
||||
leavedClassName?: string;
|
||||
onAppearStart?: MotionFunc;
|
||||
@@ -36,7 +35,6 @@ const collapseMotion: Motion = {
|
||||
onEnterActive: getRealHeight,
|
||||
onLeaveStart: getCurrentHeight,
|
||||
onLeaveActive: getCollapsedHeight,
|
||||
motionDeadline: 500,
|
||||
};
|
||||
|
||||
export default collapseMotion;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 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 '@ant-design/css-animation';
|
||||
import cssAnimation from 'css-animation';
|
||||
import raf from 'raf';
|
||||
|
||||
function animate(node: HTMLElement, show: boolean, done: () => void) {
|
||||
|
||||
@@ -1,17 +1,8 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export const isValidElement = React.isValidElement;
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export function cloneElement(element: React.ReactNode, ...restArgs: any[]) {
|
||||
if (!React.isValidElement(element)) return element;
|
||||
|
||||
export function replaceElement(
|
||||
element: React.ReactNode,
|
||||
replacement: React.ReactNode,
|
||||
props: any,
|
||||
): React.ReactNode {
|
||||
if (!isValidElement(element)) return replacement;
|
||||
|
||||
return React.cloneElement(element, typeof props === 'function' ? props() : props);
|
||||
}
|
||||
|
||||
export function cloneElement(element: React.ReactNode, props?: any): React.ReactElement {
|
||||
return replaceElement(element, element, props) as React.ReactElement;
|
||||
return React.cloneElement(element, ...restArgs);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
|
||||
export function fillRef<T>(ref: React.Ref<T>, node: T) {
|
||||
if (typeof ref === 'function') {
|
||||
|
||||
@@ -1,6 +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 = Partial<Record<Breakpoint, string>>;
|
||||
export type ScreenMap = Partial<Record<Breakpoint, boolean>>;
|
||||
|
||||
export const responsiveArray: Breakpoint[] = ['xxl', 'xl', 'lg', 'md', 'sm', 'xs'];
|
||||
|
||||
@@ -13,7 +32,7 @@ export const responsiveMap: BreakpointMap = {
|
||||
xxl: '(min-width: 1600px)',
|
||||
};
|
||||
|
||||
type SubscribeFunc = (screens: ScreenMap) => void;
|
||||
type SubscribeFunc = (screens: BreakpointMap) => void;
|
||||
|
||||
let subscribers: Array<{
|
||||
token: string;
|
||||
@@ -23,13 +42,17 @@ let subUid = -1;
|
||||
let screens = {};
|
||||
|
||||
const responsiveObserve = {
|
||||
matchHandlers: {},
|
||||
dispatch(pointMap: ScreenMap) {
|
||||
dispatch(pointMap: BreakpointMap) {
|
||||
screens = pointMap;
|
||||
if (subscribers.length < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
subscribers.forEach(item => {
|
||||
item.func(screens);
|
||||
});
|
||||
return subscribers.length >= 1;
|
||||
|
||||
return true;
|
||||
},
|
||||
subscribe(func: SubscribeFunc) {
|
||||
if (subscribers.length === 0) {
|
||||
@@ -50,32 +73,31 @@ const responsiveObserve = {
|
||||
}
|
||||
},
|
||||
unregister() {
|
||||
Object.keys(responsiveMap).forEach((screen: Breakpoint) => {
|
||||
const matchMediaQuery = responsiveMap[screen]!;
|
||||
const handler = this.matchHandlers[matchMediaQuery];
|
||||
if (handler && handler.mql && handler.listener) {
|
||||
handler.mql.removeListener(handler.listener);
|
||||
}
|
||||
});
|
||||
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 'raf';
|
||||
import getScroll, { isWindow } from './getScroll';
|
||||
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 */
|
||||
@@ -22,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;
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
@@ -25,13 +23,6 @@ class TransButton extends React.Component<TransButtonProps> {
|
||||
|
||||
lastKeyCode?: number;
|
||||
|
||||
componentDidMount() {
|
||||
const { autoFocus } = this.props;
|
||||
if (autoFocus) {
|
||||
this.focus();
|
||||
}
|
||||
}
|
||||
|
||||
onKeyDown: React.KeyboardEventHandler<HTMLDivElement> = event => {
|
||||
const { keyCode } = event;
|
||||
if (keyCode === KeyCode.ENTER) {
|
||||
@@ -64,24 +55,7 @@ class TransButton extends React.Component<TransButtonProps> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { style, noStyle, disabled, ...restProps } = this.props;
|
||||
|
||||
let mergedStyle: React.CSSProperties = {};
|
||||
|
||||
if (!noStyle) {
|
||||
mergedStyle = {
|
||||
...inlineStyle,
|
||||
};
|
||||
}
|
||||
|
||||
if (disabled) {
|
||||
mergedStyle.pointerEvents = 'none';
|
||||
}
|
||||
|
||||
mergedStyle = {
|
||||
...mergedStyle,
|
||||
...style,
|
||||
};
|
||||
const { style, noStyle, ...restProps } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -91,7 +65,7 @@ class TransButton extends React.Component<TransButtonProps> {
|
||||
{...restProps}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onKeyUp={this.onKeyUp}
|
||||
style={mergedStyle}
|
||||
style={{ ...(!noStyle ? inlineStyle : null), ...style }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
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 E)[] ? E : 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)}`);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export default function usePatchElement(): [
|
||||
React.ReactElement[],
|
||||
(element: React.ReactElement) => Function,
|
||||
] {
|
||||
const [elements, setElements] = React.useState<React.ReactElement[]>([]);
|
||||
|
||||
function patchElement(element: React.ReactElement) {
|
||||
setElements(originElements => [...originElements, element]);
|
||||
|
||||
return () => {
|
||||
setElements(originElements => originElements.filter(ele => ele !== element));
|
||||
};
|
||||
}
|
||||
|
||||
return [elements, patchElement];
|
||||
}
|
||||
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,8 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import { findDOMNode } from 'react-dom';
|
||||
import TransitionEvents from '@ant-design/css-animation/lib/Event';
|
||||
import TransitionEvents from 'css-animation/lib/Event';
|
||||
import raf from './raf';
|
||||
import { ConfigConsumer, ConfigConsumerProps, CSPConfig, ConfigContext } from '../config-provider';
|
||||
import { ConfigConsumer, ConfigConsumerProps, CSPConfig } from '../config-provider';
|
||||
|
||||
let styleForPesudo: HTMLStyleElement | null;
|
||||
|
||||
@@ -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,8 +24,6 @@ function isNotGrey(color: string) {
|
||||
}
|
||||
|
||||
export default class Wave extends React.Component<{ insertExtraNode?: boolean }> {
|
||||
static contextType = ConfigContext;
|
||||
|
||||
private instance?: {
|
||||
cancel: () => void;
|
||||
};
|
||||
@@ -38,12 +36,10 @@ 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 = findDOMNode(this) as HTMLElement;
|
||||
if (!node || node.nodeType !== 1) {
|
||||
@@ -60,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) => {
|
||||
@@ -70,8 +66,7 @@ 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 transparnt or grey
|
||||
@@ -81,7 +76,7 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
|
||||
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
|
||||
@@ -91,9 +86,7 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
|
||||
|
||||
extraNode.style.borderColor = waveColor;
|
||||
styleForPesudo.innerHTML = `
|
||||
[${getPrefixCls('')}-click-animating-without-extra-node='true']::after, .${getPrefixCls(
|
||||
'',
|
||||
)}-click-animating-node {
|
||||
[ant-click-animating-without-extra-node='true']::after, .ant-click-animating-node {
|
||||
--antd-wave-shadow-color: ${waveColor};
|
||||
}`;
|
||||
if (!document.body.contains(styleForPesudo)) {
|
||||
@@ -108,16 +101,16 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
|
||||
};
|
||||
|
||||
onTransitionStart = (e: AnimationEvent) => {
|
||||
if (this.destroyed) {
|
||||
return;
|
||||
}
|
||||
if (this.destroy) return;
|
||||
|
||||
const node = findDOMNode(this) as HTMLElement;
|
||||
if (!e || e.target !== node || this.animationStart) {
|
||||
if (!e || e.target !== node) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.resetEffect(node);
|
||||
if (!this.animationStart) {
|
||||
this.resetEffect(node);
|
||||
}
|
||||
};
|
||||
|
||||
onTransitionEnd = (e: AnimationEvent) => {
|
||||
@@ -128,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) => {
|
||||
|
||||
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,230 +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;
|
||||
}> {
|
||||
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" />';
|
||||
|
||||
affixMounterWrapper = mount(<AffixMounter offsetTop={0} />, {
|
||||
attachTo: document.getElementById('mounter'),
|
||||
});
|
||||
await sleep(20);
|
||||
|
||||
await movePlaceholder(-100);
|
||||
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 },
|
||||
])(name, 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>
|
||||
`;
|
||||
@@ -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 (
|
||||
<div>
|
||||
<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>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
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:
|
||||
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/SQGCQ7gOO/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) {
|
||||
@@ -129,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;
|
||||
}
|
||||
@@ -152,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;
|
||||
}
|
||||
@@ -232,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);
|
||||
@@ -261,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({
|
||||
@@ -296,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/SQGCQ7gOO/Affix.svg
|
||||
---
|
||||
|
||||
将页面元素钉在可视范围。
|
||||
@@ -16,12 +15,12 @@ cover: https://gw.alipayobjects.com/zos/alicdn/SQGCQ7gOO/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;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,14 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders ./components/alert/demo/banner.md correctly 1`] = `
|
||||
Array [
|
||||
<div>
|
||||
<div
|
||||
class="ant-alert ant-alert-warning ant-alert-banner"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
aria-label="exclamation-circle"
|
||||
<i
|
||||
aria-label="icon: exclamation-circle"
|
||||
class="anticon anticon-exclamation-circle ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -22,10 +21,10 @@ Array [
|
||||
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 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 0 1 0-96 48.01 48.01 0 0 1 0 96z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</i>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
@@ -34,16 +33,15 @@ Array [
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</div>,
|
||||
<br />,
|
||||
</div>
|
||||
<br />
|
||||
<div
|
||||
class="ant-alert ant-alert-warning ant-alert-banner ant-alert-closable"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
aria-label="exclamation-circle"
|
||||
<i
|
||||
aria-label="icon: exclamation-circle"
|
||||
class="anticon anticon-exclamation-circle ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -56,10 +54,10 @@ Array [
|
||||
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 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 0 1 0-96 48.01 48.01 0 0 1 0 96z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</i>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
@@ -73,10 +71,9 @@ Array [
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
<i
|
||||
aria-label="icon: close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -89,13 +86,13 @@ Array [
|
||||
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"
|
||||
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 0 0 203 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>
|
||||
</i>
|
||||
</button>
|
||||
</div>,
|
||||
<br />,
|
||||
</div>
|
||||
<br />
|
||||
<div
|
||||
class="ant-alert ant-alert-warning ant-alert-no-icon ant-alert-banner"
|
||||
data-show="true"
|
||||
@@ -108,16 +105,15 @@ Array [
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</div>,
|
||||
<br />,
|
||||
</div>
|
||||
<br />
|
||||
<div
|
||||
class="ant-alert ant-alert-error ant-alert-banner"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
<i
|
||||
aria-label="icon: close-circle"
|
||||
class="anticon anticon-close-circle ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -130,10 +126,10 @@ Array [
|
||||
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 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 0 1-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</i>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
@@ -142,8 +138,8 @@ Array [
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</div>,
|
||||
]
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/alert/demo/basic.md correctly 1`] = `
|
||||
@@ -163,7 +159,7 @@ exports[`renders ./components/alert/demo/basic.md correctly 1`] = `
|
||||
`;
|
||||
|
||||
exports[`renders ./components/alert/demo/closable.md correctly 1`] = `
|
||||
Array [
|
||||
<div>
|
||||
<div
|
||||
class="ant-alert ant-alert-warning ant-alert-no-icon ant-alert-closable"
|
||||
data-show="true"
|
||||
@@ -181,10 +177,9 @@ Array [
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
<i
|
||||
aria-label="icon: close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -197,12 +192,12 @@ Array [
|
||||
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"
|
||||
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 0 0 203 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>
|
||||
</i>
|
||||
</button>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-error ant-alert-with-description ant-alert-no-icon ant-alert-closable"
|
||||
data-show="true"
|
||||
@@ -222,10 +217,9 @@ Array [
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
<i
|
||||
aria-label="icon: close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -238,13 +232,13 @@ Array [
|
||||
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"
|
||||
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 0 0 203 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>
|
||||
</i>
|
||||
</button>
|
||||
</div>,
|
||||
]
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/alert/demo/close-text.md correctly 1`] = `
|
||||
@@ -275,7 +269,7 @@ exports[`renders ./components/alert/demo/close-text.md correctly 1`] = `
|
||||
`;
|
||||
|
||||
exports[`renders ./components/alert/demo/custom-icon.md correctly 1`] = `
|
||||
Array [
|
||||
<div>
|
||||
<div
|
||||
class="ant-alert ant-alert-success ant-alert-no-icon"
|
||||
data-show="true"
|
||||
@@ -288,15 +282,14 @@ Array [
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-success"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
aria-label="smile"
|
||||
<i
|
||||
aria-label="icon: smile"
|
||||
class="anticon anticon-smile ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -309,10 +302,10 @@ Array [
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4z"
|
||||
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</i>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
@@ -321,15 +314,14 @@ Array [
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-info"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
aria-label="smile"
|
||||
<i
|
||||
aria-label="icon: smile"
|
||||
class="anticon anticon-smile ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -342,10 +334,10 @@ Array [
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4z"
|
||||
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</i>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
@@ -354,15 +346,14 @@ Array [
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-warning"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
aria-label="smile"
|
||||
<i
|
||||
aria-label="icon: smile"
|
||||
class="anticon anticon-smile ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -375,10 +366,10 @@ Array [
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4z"
|
||||
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</i>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
@@ -387,15 +378,14 @@ Array [
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-error"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
aria-label="smile"
|
||||
<i
|
||||
aria-label="icon: smile"
|
||||
class="anticon anticon-smile ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -408,10 +398,10 @@ Array [
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4z"
|
||||
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</i>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
@@ -420,15 +410,14 @@ Array [
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-success ant-alert-with-description"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
aria-label="smile"
|
||||
<i
|
||||
aria-label="icon: smile"
|
||||
class="anticon anticon-smile ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -441,10 +430,10 @@ Array [
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4z"
|
||||
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</i>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
@@ -455,15 +444,14 @@ Array [
|
||||
>
|
||||
Detailed description and advices about successful copywriting.
|
||||
</span>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-info ant-alert-with-description"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
aria-label="smile"
|
||||
<i
|
||||
aria-label="icon: smile"
|
||||
class="anticon anticon-smile ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -476,10 +464,10 @@ Array [
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4z"
|
||||
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</i>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
@@ -490,15 +478,14 @@ Array [
|
||||
>
|
||||
Additional description and informations about copywriting.
|
||||
</span>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-warning ant-alert-with-description"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
aria-label="smile"
|
||||
<i
|
||||
aria-label="icon: smile"
|
||||
class="anticon anticon-smile ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -511,10 +498,10 @@ Array [
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4z"
|
||||
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</i>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
@@ -525,15 +512,14 @@ Array [
|
||||
>
|
||||
This is a warning notice about copywriting.
|
||||
</span>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-error ant-alert-with-description"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
aria-label="smile"
|
||||
<i
|
||||
aria-label="icon: smile"
|
||||
class="anticon anticon-smile ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -546,10 +532,10 @@ Array [
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4z"
|
||||
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</i>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
@@ -560,12 +546,12 @@ Array [
|
||||
>
|
||||
This is an error message about copywriting.
|
||||
</span>
|
||||
</div>,
|
||||
]
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/alert/demo/description.md correctly 1`] = `
|
||||
Array [
|
||||
<div>
|
||||
<div
|
||||
class="ant-alert ant-alert-success ant-alert-with-description ant-alert-no-icon"
|
||||
data-show="true"
|
||||
@@ -580,7 +566,7 @@ Array [
|
||||
>
|
||||
Success Description Success Description Success Description
|
||||
</span>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-info ant-alert-with-description ant-alert-no-icon"
|
||||
data-show="true"
|
||||
@@ -595,7 +581,7 @@ Array [
|
||||
>
|
||||
Info Description Info Description Info Description Info Description
|
||||
</span>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-warning ant-alert-with-description ant-alert-no-icon"
|
||||
data-show="true"
|
||||
@@ -610,7 +596,7 @@ Array [
|
||||
>
|
||||
Warning Description Warning Description Warning Description Warning Description
|
||||
</span>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-error ant-alert-with-description ant-alert-no-icon"
|
||||
data-show="true"
|
||||
@@ -625,31 +611,19 @@ Array [
|
||||
>
|
||||
Error Description Error Description Error Description Error Description
|
||||
</span>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/alert/demo/error-boundary.md correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-danger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Click me to throw a error
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/alert/demo/icon.md correctly 1`] = `
|
||||
Array [
|
||||
<div>
|
||||
<div
|
||||
class="ant-alert ant-alert-success"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
aria-label="check-circle"
|
||||
<i
|
||||
aria-label="icon: check-circle"
|
||||
class="anticon anticon-check-circle ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -662,10 +636,10 @@ Array [
|
||||
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"
|
||||
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 0 1-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>
|
||||
</i>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
@@ -674,15 +648,14 @@ Array [
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-info"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
aria-label="info-circle"
|
||||
<i
|
||||
aria-label="icon: info-circle"
|
||||
class="anticon anticon-info-circle ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -695,10 +668,10 @@ Array [
|
||||
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 64zm32 664c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V456c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272zm-32-344a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm32 664c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V456c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272zm-32-344a48.01 48.01 0 0 1 0-96 48.01 48.01 0 0 1 0 96z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</i>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
@@ -707,15 +680,14 @@ Array [
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-warning ant-alert-closable"
|
||||
class="ant-alert ant-alert-warning"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
aria-label="exclamation-circle"
|
||||
<i
|
||||
aria-label="icon: exclamation-circle"
|
||||
class="anticon anticon-exclamation-circle ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -728,10 +700,10 @@ Array [
|
||||
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 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 0 1 0-96 48.01 48.01 0 0 1 0 96z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</i>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
@@ -740,41 +712,14 @@ Array [
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
<button
|
||||
class="ant-alert-close-icon"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
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>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-error"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
<i
|
||||
aria-label="icon: close-circle"
|
||||
class="anticon anticon-close-circle ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -787,10 +732,10 @@ Array [
|
||||
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 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 0 1-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</i>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
@@ -799,15 +744,14 @@ Array [
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-success ant-alert-with-description"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
aria-label="check-circle"
|
||||
<i
|
||||
aria-label="icon: check-circle"
|
||||
class="anticon anticon-check-circle ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -820,13 +764,13 @@ Array [
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M699 353h-46.9c-10.2 0-19.9 4.9-25.9 13.3L469 584.3l-71.2-98.8c-6-8.3-15.6-13.3-25.9-13.3H325c-6.5 0-10.3 7.4-6.5 12.7l124.6 172.8a31.8 31.8 0 0051.7 0l210.6-292c3.9-5.3.1-12.7-6.4-12.7z"
|
||||
d="M699 353h-46.9c-10.2 0-19.9 4.9-25.9 13.3L469 584.3l-71.2-98.8c-6-8.3-15.6-13.3-25.9-13.3H325c-6.5 0-10.3 7.4-6.5 12.7l124.6 172.8a31.8 31.8 0 0 0 51.7 0l210.6-292c3.9-5.3.1-12.7-6.4-12.7z"
|
||||
/>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</i>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
@@ -837,15 +781,14 @@ Array [
|
||||
>
|
||||
Detailed description and advice about successful copywriting.
|
||||
</span>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-info ant-alert-with-description"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
aria-label="info-circle"
|
||||
<i
|
||||
aria-label="icon: info-circle"
|
||||
class="anticon anticon-info-circle ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -861,10 +804,10 @@ Array [
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
|
||||
/>
|
||||
<path
|
||||
d="M464 336a48 48 0 1096 0 48 48 0 10-96 0zm72 112h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V456c0-4.4-3.6-8-8-8z"
|
||||
d="M464 336a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm72 112h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V456c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</i>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
@@ -875,15 +818,14 @@ Array [
|
||||
>
|
||||
Additional description and information about copywriting.
|
||||
</span>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-warning ant-alert-with-description ant-alert-closable"
|
||||
class="ant-alert ant-alert-warning ant-alert-with-description"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
aria-label="exclamation-circle"
|
||||
<i
|
||||
aria-label="icon: exclamation-circle"
|
||||
class="anticon anticon-exclamation-circle ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -899,10 +841,10 @@ Array [
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
|
||||
/>
|
||||
<path
|
||||
d="M464 688a48 48 0 1096 0 48 48 0 10-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"
|
||||
d="M464 688a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</i>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
@@ -913,41 +855,14 @@ Array [
|
||||
>
|
||||
This is a warning notice about copywriting.
|
||||
</span>
|
||||
<button
|
||||
class="ant-alert-close-icon"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
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>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-error ant-alert-with-description"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
<i
|
||||
aria-label="icon: close-circle"
|
||||
class="anticon anticon-close-circle ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -960,13 +875,13 @@ Array [
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M685.4 354.8c0-4.4-3.6-8-8-8l-66 .3L512 465.6l-99.3-118.4-66.1-.3c-4.4 0-8 3.5-8 8 0 1.9.7 3.7 1.9 5.2l130.1 155L340.5 670a8.32 8.32 0 00-1.9 5.2c0 4.4 3.6 8 8 8l66.1-.3L512 564.4l99.3 118.4 66 .3c4.4 0 8-3.5 8-8 0-1.9-.7-3.7-1.9-5.2L553.5 515l130.1-155c1.2-1.4 1.8-3.3 1.8-5.2z"
|
||||
d="M685.4 354.8c0-4.4-3.6-8-8-8l-66 .3L512 465.6l-99.3-118.4-66.1-.3c-4.4 0-8 3.5-8 8 0 1.9.7 3.7 1.9 5.2l130.1 155L340.5 670a8.32 8.32 0 0 0-1.9 5.2c0 4.4 3.6 8 8 8l66.1-.3L512 564.4l99.3 118.4 66 .3c4.4 0 8-3.5 8-8 0-1.9-.7-3.7-1.9-5.2L553.5 515l130.1-155c1.2-1.4 1.8-3.3 1.8-5.2z"
|
||||
/>
|
||||
<path
|
||||
d="M512 65C264.6 65 64 265.6 64 513s200.6 448 448 448 448-200.6 448-448S759.4 65 512 65zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</i>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
@@ -977,58 +892,7 @@ Array [
|
||||
>
|
||||
This is an error message about copywriting.
|
||||
</span>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/alert/demo/loop-banner.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-alert ant-alert-warning ant-alert-banner"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
aria-label="exclamation-circle"
|
||||
class="anticon anticon-exclamation-circle ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="exclamation-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 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
<div
|
||||
class="x0 x1 x2 x3 "
|
||||
>
|
||||
<div
|
||||
style="transition:width 150ms linear;height:auto;width:auto"
|
||||
>
|
||||
<div
|
||||
class="x1 x4 x5 x6"
|
||||
style="opacity:1;transform:translateY(0px);position:relative"
|
||||
>
|
||||
<div>
|
||||
Notice message one
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -1051,10 +915,9 @@ exports[`renders ./components/alert/demo/smooth-closed.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
<i
|
||||
aria-label="icon: close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -1067,10 +930,10 @@ exports[`renders ./components/alert/demo/smooth-closed.md correctly 1`] = `
|
||||
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"
|
||||
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 0 0 203 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>
|
||||
</i>
|
||||
</button>
|
||||
</div>
|
||||
<p>
|
||||
@@ -1080,7 +943,7 @@ exports[`renders ./components/alert/demo/smooth-closed.md correctly 1`] = `
|
||||
`;
|
||||
|
||||
exports[`renders ./components/alert/demo/style.md correctly 1`] = `
|
||||
Array [
|
||||
<div>
|
||||
<div
|
||||
class="ant-alert ant-alert-success ant-alert-no-icon"
|
||||
data-show="true"
|
||||
@@ -1093,7 +956,7 @@ Array [
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-info ant-alert-no-icon"
|
||||
data-show="true"
|
||||
@@ -1106,7 +969,7 @@ Array [
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-warning ant-alert-no-icon"
|
||||
data-show="true"
|
||||
@@ -1119,7 +982,7 @@ Array [
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-error ant-alert-no-icon"
|
||||
data-show="true"
|
||||
@@ -1132,6 +995,6 @@ Array [
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</div>,
|
||||
]
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -1,58 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Alert ErrorBoundary 1`] = `
|
||||
<div
|
||||
class="ant-alert ant-alert-error ant-alert-with-description ant-alert-no-icon"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
ReferenceError: NotExisted is not defined
|
||||
</span>
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
>
|
||||
<pre>
|
||||
in ThrowError
|
||||
in ErrorBoundary (created by WrapperComponent)
|
||||
in WrapperComponent
|
||||
</pre>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Alert could accept none react element icon 1`] = `
|
||||
<div
|
||||
class="ant-alert ant-alert-success"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
class="ant-alert-icon"
|
||||
>
|
||||
icon
|
||||
</span>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
>
|
||||
Success Tips
|
||||
</span>
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</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"
|
||||
>
|
||||
<span
|
||||
class="ant-alert-message"
|
||||
/>
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
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();
|
||||
});
|
||||
});
|
||||
@@ -1,113 +0,0 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Alert from '..';
|
||||
import Tooltip from '../../tooltip';
|
||||
import Popconfirm from '../../popconfirm';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
|
||||
const { ErrorBoundary } = Alert;
|
||||
|
||||
describe('Alert', () => {
|
||||
rtlTest(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" message={null} />);
|
||||
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" message={null} />);
|
||||
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" message={null} />);
|
||||
const input = wrapper.find('.ant-alert').getDOMNode();
|
||||
expect(input.getAttribute('role')).toBe('status');
|
||||
});
|
||||
});
|
||||
|
||||
const testIt = process.env.REACT === '15' ? it.skip : it;
|
||||
testIt('ErrorBoundary', () => {
|
||||
// @ts-expect-error
|
||||
// eslint-disable-next-line react/jsx-no-undef
|
||||
const ThrowError = () => <NotExisted />;
|
||||
const wrapper = mount(
|
||||
<ErrorBoundary>
|
||||
<ThrowError />
|
||||
</ErrorBoundary>,
|
||||
);
|
||||
// eslint-disable-next-line jest/no-standalone-expect
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('could be used with Tooltip', async () => {
|
||||
const ref = React.createRef<any>();
|
||||
jest.useRealTimers();
|
||||
const wrapper = mount(
|
||||
<Tooltip title="xxx" mouseEnterDelay={0} ref={ref}>
|
||||
<Alert
|
||||
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
|
||||
type="warning"
|
||||
/>
|
||||
</Tooltip>,
|
||||
);
|
||||
wrapper.find('.ant-alert').simulate('mouseenter');
|
||||
await sleep(0);
|
||||
expect(ref.current.getPopupDomNode()).toBeTruthy();
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
it('could be used with Popconfirm', async () => {
|
||||
const ref = React.createRef<any>();
|
||||
jest.useRealTimers();
|
||||
const wrapper = mount(
|
||||
<Popconfirm ref={ref} title="xxx">
|
||||
<Alert
|
||||
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
|
||||
type="warning"
|
||||
/>
|
||||
</Popconfirm>,
|
||||
);
|
||||
wrapper.find('.ant-alert').simulate('click');
|
||||
await sleep(0);
|
||||
expect(ref.current.getPopupDomNode()).toBeTruthy();
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
it('could accept none react element icon', () => {
|
||||
const wrapper = mount(<Alert message="Success Tips" type="success" showIcon icon="icon" />);
|
||||
expect(wrapper).toMatchRenderedSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -8,17 +8,17 @@ title:
|
||||
|
||||
## zh-CN
|
||||
|
||||
页面顶部通告形式,默认有图标且 `type` 为 'warning'。
|
||||
页面顶部通告形式,默认有图标且`type` 为 'warning'。
|
||||
|
||||
## en-US
|
||||
|
||||
Display Alert as a banner at top of page.
|
||||
|
||||
```tsx
|
||||
```jsx
|
||||
import { Alert } from 'antd';
|
||||
|
||||
ReactDOM.render(
|
||||
<>
|
||||
<div>
|
||||
<Alert message="Warning text" banner />
|
||||
<br />
|
||||
<Alert
|
||||
@@ -30,7 +30,7 @@ ReactDOM.render(
|
||||
<Alert showIcon={false} message="Warning text without icon" banner />
|
||||
<br />
|
||||
<Alert type="error" message="Error text" banner />
|
||||
</>,
|
||||
</div>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
||||
@@ -13,14 +13,14 @@ title:
|
||||
|
||||
The simplest usage for short messages.
|
||||
|
||||
```tsx
|
||||
```jsx
|
||||
import { Alert } from 'antd';
|
||||
|
||||
ReactDOM.render(<Alert message="Success Text" type="success" />, mountNode);
|
||||
```
|
||||
|
||||
<style>
|
||||
.code-box-demo .ant-alert {
|
||||
.ant-alert {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -13,15 +13,15 @@ title:
|
||||
|
||||
To show close button.
|
||||
|
||||
```tsx
|
||||
```jsx
|
||||
import { Alert } from 'antd';
|
||||
|
||||
const onClose = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
const onClose = e => {
|
||||
console.log(e, 'I was closed.');
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
<>
|
||||
<div>
|
||||
<Alert
|
||||
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
|
||||
type="warning"
|
||||
@@ -35,7 +35,7 @@ ReactDOM.render(
|
||||
closable
|
||||
onClose={onClose}
|
||||
/>
|
||||
</>,
|
||||
</div>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
||||
@@ -13,7 +13,7 @@ title:
|
||||
|
||||
Replace the default icon with customized text.
|
||||
|
||||
```tsx
|
||||
```jsx
|
||||
import { Alert } from 'antd';
|
||||
|
||||
ReactDOM.render(<Alert message="Info Text" type="info" closeText="Close Now" />, mountNode);
|
||||
|
||||
@@ -14,14 +14,13 @@ title:
|
||||
|
||||
A relevant icon makes information clearer and more friendly.
|
||||
|
||||
```tsx
|
||||
import { Alert } from 'antd';
|
||||
import { SmileOutlined } from '@ant-design/icons';
|
||||
```jsx
|
||||
import { Alert, Icon } from 'antd';
|
||||
|
||||
const icon = <SmileOutlined />;
|
||||
const icon = <Icon type="smile" />;
|
||||
|
||||
ReactDOM.render(
|
||||
<>
|
||||
<div>
|
||||
<Alert icon={icon} message="showIcon = false" type="success" />
|
||||
<Alert icon={icon} message="Success Tips" type="success" showIcon />
|
||||
<Alert icon={icon} message="Informational Notes" type="info" showIcon />
|
||||
@@ -55,7 +54,7 @@ ReactDOM.render(
|
||||
type="error"
|
||||
showIcon
|
||||
/>
|
||||
</>,
|
||||
</div>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
||||
@@ -13,11 +13,11 @@ title:
|
||||
|
||||
Additional description for alert message.
|
||||
|
||||
```tsx
|
||||
```jsx
|
||||
import { Alert } from 'antd';
|
||||
|
||||
ReactDOM.render(
|
||||
<>
|
||||
<div>
|
||||
<Alert
|
||||
message="Success Text"
|
||||
description="Success Description Success Description Success Description"
|
||||
@@ -38,7 +38,7 @@ ReactDOM.render(
|
||||
description="Error Description Error Description Error Description Error Description"
|
||||
type="error"
|
||||
/>
|
||||
</>,
|
||||
</div>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
---
|
||||
order: 8
|
||||
title:
|
||||
zh-CN: ErrorBoundary
|
||||
en-US: React 错误处理
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
友好的 [React 错误处理](https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html) 包裹组件。
|
||||
|
||||
## en-US
|
||||
|
||||
ErrorBoundary Component for making error handling easier in [React](https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html).
|
||||
|
||||
```tsx
|
||||
import React, { useState } from 'react';
|
||||
import { Button, Alert } from 'antd';
|
||||
|
||||
const { ErrorBoundary } = Alert;
|
||||
const ThrowError: React.FC = () => {
|
||||
const [error, setError] = useState<Error>();
|
||||
const onClick = () => {
|
||||
setError(new Error('An Uncaught Error'));
|
||||
};
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
return (
|
||||
<Button type="danger" onClick={onClick}>
|
||||
Click me to throw a error
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
<ErrorBoundary>
|
||||
<ThrowError />
|
||||
</ErrorBoundary>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
@@ -13,14 +13,14 @@ title:
|
||||
|
||||
A relevant icon will make information clearer and more friendly.
|
||||
|
||||
```tsx
|
||||
```jsx
|
||||
import { Alert } from 'antd';
|
||||
|
||||
ReactDOM.render(
|
||||
<>
|
||||
<div>
|
||||
<Alert message="Success Tips" type="success" showIcon />
|
||||
<Alert message="Informational Notes" type="info" showIcon />
|
||||
<Alert message="Warning" type="warning" showIcon closable />
|
||||
<Alert message="Warning" type="warning" showIcon />
|
||||
<Alert message="Error" type="error" showIcon />
|
||||
<Alert
|
||||
message="Success Tips"
|
||||
@@ -39,7 +39,6 @@ ReactDOM.render(
|
||||
description="This is a warning notice about copywriting."
|
||||
type="warning"
|
||||
showIcon
|
||||
closable
|
||||
/>
|
||||
<Alert
|
||||
message="Error"
|
||||
@@ -47,7 +46,7 @@ ReactDOM.render(
|
||||
type="error"
|
||||
showIcon
|
||||
/>
|
||||
</>,
|
||||
</div>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
order: 6.1
|
||||
title:
|
||||
zh-CN: 轮播的公告
|
||||
en-US: Loop Banner
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
配合 [react-text-loop](https://npmjs.com/package/react-text-loop) 实现消息轮播通知栏。
|
||||
|
||||
## en-US
|
||||
|
||||
Show a loop banner by using with [react-text-loop](https://npmjs.com/package/react-text-loop).
|
||||
|
||||
```tsx
|
||||
import { Alert } from 'antd';
|
||||
import TextLoop from 'react-text-loop';
|
||||
|
||||
ReactDOM.render(
|
||||
<Alert
|
||||
banner
|
||||
message={
|
||||
<TextLoop mask>
|
||||
<div>Notice message one</div>
|
||||
<div>Notice message two</div>
|
||||
<div>Notice message three</div>
|
||||
<div>Notice message four</div>
|
||||
</TextLoop>
|
||||
}
|
||||
/>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
@@ -13,24 +13,34 @@ title:
|
||||
|
||||
Smoothly unmount Alert upon close.
|
||||
|
||||
```tsx
|
||||
import React, { useState } from 'react';
|
||||
```jsx
|
||||
import { Alert } from 'antd';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [visible, setVisible] = useState(true);
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
class App extends React.Component {
|
||||
state = {
|
||||
visible: true,
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
{visible ? (
|
||||
<Alert message="Alert Message Text" type="success" closable afterClose={handleClose} />
|
||||
) : null}
|
||||
<p>placeholder text here</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
handleClose = () => {
|
||||
this.setState({ visible: false });
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{this.state.visible ? (
|
||||
<Alert
|
||||
message="Alert Message Text"
|
||||
type="success"
|
||||
closable
|
||||
afterClose={this.handleClose}
|
||||
/>
|
||||
) : null}
|
||||
<p>placeholder text here</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<App />, mountNode);
|
||||
```
|
||||
|
||||
@@ -13,22 +13,16 @@ title:
|
||||
|
||||
There are 4 types of Alert: `success`, `info`, `warning`, `error`.
|
||||
|
||||
```tsx
|
||||
```jsx
|
||||
import { Alert } from 'antd';
|
||||
|
||||
ReactDOM.render(
|
||||
<>
|
||||
<div>
|
||||
<Alert message="Success Text" type="success" />
|
||||
<Alert message="Info Text" type="info" />
|
||||
<Alert message="Warning Text" type="warning" />
|
||||
<Alert message="Error Text" type="error" />
|
||||
</>,
|
||||
</div>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
||||
<style>
|
||||
[data-theme="compact"] .code-box-demo .ant-alert {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
category: Components
|
||||
type: Feedback
|
||||
title: Alert
|
||||
cover: https://gw.alipayobjects.com/zos/antfincdn/nuhf3vdRno/Alert.svg
|
||||
---
|
||||
|
||||
Alert component for feedback.
|
||||
@@ -14,22 +13,15 @@ Alert component for feedback.
|
||||
|
||||
## API
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| afterClose | Called when close animation is finished | () => void | - |
|
||||
| banner | Whether to show as banner | boolean | false |
|
||||
| closable | Whether Alert can be closed | boolean | - |
|
||||
| closeText | Close text to show | string\|ReactNode | - |
|
||||
| description | Additional content of Alert | string\|ReactNode | - |
|
||||
| icon | Custom icon, effective when `showIcon` is `true` | ReactNode | - |
|
||||
| message | Content of Alert | string\|ReactNode | - |
|
||||
| showIcon | Whether to show icon | boolean | false, in `banner` mode default is true |
|
||||
| type | Type of Alert styles, options: `success`, `info`, `warning`, `error` | string | `info`, in `banner` mode default is `warning` |
|
||||
| onClose | Callback when Alert is closed | (e: MouseEvent) => void | - |
|
||||
|
||||
### Alert.ErrorBoundary
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| ----------- | -------------------------------- | --------- | ------------------- | ------- |
|
||||
| message | custom error message to show | ReactNode | `{{ error }}` | |
|
||||
| description | custom error description to show | ReactNode | `{{ error stack }}` | |
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| afterClose | Called when close animation is finished | () => void | - | 3.3.1 |
|
||||
| banner | Whether to show as banner | boolean | false | |
|
||||
| closable | Whether Alert can be closed | boolean | - | |
|
||||
| closeText | Close text to show | string\|ReactNode | - | |
|
||||
| description | Additional content of Alert | string\|ReactNode | - | |
|
||||
| icon | Custom icon, effective when `showIcon` is `true` | ReactNode | - | 3.10.0 |
|
||||
| message | Content of Alert | string\|ReactNode | - | |
|
||||
| showIcon | Whether to show icon | boolean | false, in `banner` mode default is true | |
|
||||
| type | Type of Alert styles, options: `success`, `info`, `warning`, `error` | string | `info`, in `banner` mode default is `warning` | |
|
||||
| onClose | Callback when Alert is closed | (e: MouseEvent) => void | - | |
|
||||
|
||||
@@ -1,20 +1,13 @@
|
||||
import * as React from 'react';
|
||||
import CloseOutlined from '@ant-design/icons/CloseOutlined';
|
||||
import CheckCircleOutlined from '@ant-design/icons/CheckCircleOutlined';
|
||||
import ExclamationCircleOutlined from '@ant-design/icons/ExclamationCircleOutlined';
|
||||
import InfoCircleOutlined from '@ant-design/icons/InfoCircleOutlined';
|
||||
import CloseCircleOutlined from '@ant-design/icons/CloseCircleOutlined';
|
||||
import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
|
||||
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
|
||||
import InfoCircleFilled from '@ant-design/icons/InfoCircleFilled';
|
||||
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import Animate from 'rc-animate';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import Icon, { ThemeType } from '../icon';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import getDataOrAriaProps from '../_util/getDataOrAriaProps';
|
||||
import ErrorBoundary from './ErrorBoundary';
|
||||
import { replaceElement } from '../_util/reactNode';
|
||||
import warning from '../_util/warning';
|
||||
|
||||
function noop() {}
|
||||
|
||||
export interface AlertProps {
|
||||
/**
|
||||
@@ -35,165 +28,167 @@ export interface AlertProps {
|
||||
afterClose?: () => void;
|
||||
/** Whether to show icon */
|
||||
showIcon?: boolean;
|
||||
/** https://www.w3.org/TR/2014/REC-html5-20141028/dom.html#aria-role-attribute */
|
||||
role?: string;
|
||||
iconType?: string;
|
||||
style?: React.CSSProperties;
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
banner?: boolean;
|
||||
icon?: React.ReactNode;
|
||||
onMouseEnter?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onMouseLeave?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onClick?: React.MouseEventHandler<HTMLDivElement>;
|
||||
}
|
||||
|
||||
const iconMapFilled = {
|
||||
success: CheckCircleFilled,
|
||||
info: InfoCircleFilled,
|
||||
error: CloseCircleFilled,
|
||||
warning: ExclamationCircleFilled,
|
||||
};
|
||||
|
||||
const iconMapOutlined = {
|
||||
success: CheckCircleOutlined,
|
||||
info: InfoCircleOutlined,
|
||||
error: CloseCircleOutlined,
|
||||
warning: ExclamationCircleOutlined,
|
||||
};
|
||||
|
||||
interface AlertInterface extends React.FC<AlertProps> {
|
||||
ErrorBoundary: typeof ErrorBoundary;
|
||||
export interface AlertState {
|
||||
closing: boolean;
|
||||
closed: boolean;
|
||||
}
|
||||
|
||||
const Alert: AlertInterface = ({
|
||||
description,
|
||||
prefixCls: customizePrefixCls,
|
||||
message,
|
||||
banner,
|
||||
className = '',
|
||||
style,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
onClick,
|
||||
showIcon,
|
||||
closable,
|
||||
closeText,
|
||||
...props
|
||||
}) => {
|
||||
const [closing, setClosing] = React.useState(false);
|
||||
const [closed, setClosed] = React.useState(false);
|
||||
export default class Alert extends React.Component<AlertProps, AlertState> {
|
||||
constructor(props: AlertProps) {
|
||||
super(props);
|
||||
|
||||
const ref = React.useRef<HTMLElement>();
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
const prefixCls = getPrefixCls('alert', customizePrefixCls);
|
||||
warning(
|
||||
!('iconType' in props),
|
||||
'Alert',
|
||||
'`iconType` is deprecated. Please use `icon` instead.',
|
||||
);
|
||||
|
||||
const handleClose = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
this.state = {
|
||||
closing: false,
|
||||
closed: false,
|
||||
};
|
||||
}
|
||||
|
||||
handleClose = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.preventDefault();
|
||||
const dom = ref.current as HTMLElement;
|
||||
const dom = ReactDOM.findDOMNode(this) as HTMLElement;
|
||||
dom.style.height = `${dom.offsetHeight}px`;
|
||||
// Magic code
|
||||
// 重复一次后才能正确设置 height
|
||||
dom.style.height = `${dom.offsetHeight}px`;
|
||||
|
||||
setClosing(true);
|
||||
props.onClose?.(e);
|
||||
this.setState({
|
||||
closing: true,
|
||||
});
|
||||
(this.props.onClose || noop)(e);
|
||||
};
|
||||
|
||||
const animationEnd = () => {
|
||||
setClosing(false);
|
||||
setClosed(true);
|
||||
props.afterClose?.();
|
||||
animationEnd = () => {
|
||||
this.setState({
|
||||
closing: false,
|
||||
closed: true,
|
||||
});
|
||||
(this.props.afterClose || noop)();
|
||||
};
|
||||
|
||||
const getType = () => {
|
||||
const { type } = props;
|
||||
if (type !== undefined) {
|
||||
return type;
|
||||
renderAlert = ({ getPrefixCls }: ConfigConsumerProps) => {
|
||||
const {
|
||||
description,
|
||||
prefixCls: customizePrefixCls,
|
||||
message,
|
||||
closeText,
|
||||
banner,
|
||||
className = '',
|
||||
style,
|
||||
icon,
|
||||
} = this.props;
|
||||
let { closable, type, showIcon, iconType } = this.props;
|
||||
const { closing, closed } = this.state;
|
||||
|
||||
const prefixCls = getPrefixCls('alert', customizePrefixCls);
|
||||
|
||||
// banner模式默认有 Icon
|
||||
showIcon = banner && showIcon === undefined ? true : showIcon;
|
||||
// banner模式默认为警告
|
||||
type = banner && type === undefined ? 'warning' : type || 'info';
|
||||
|
||||
let iconTheme: ThemeType = 'filled';
|
||||
if (!iconType) {
|
||||
switch (type) {
|
||||
case 'success':
|
||||
iconType = 'check-circle';
|
||||
break;
|
||||
case 'info':
|
||||
iconType = 'info-circle';
|
||||
break;
|
||||
case 'error':
|
||||
iconType = 'close-circle';
|
||||
break;
|
||||
case 'warning':
|
||||
iconType = 'exclamation-circle';
|
||||
break;
|
||||
default:
|
||||
iconType = 'default';
|
||||
}
|
||||
|
||||
// use outline icon in alert with description
|
||||
if (description) {
|
||||
iconTheme = 'outlined';
|
||||
}
|
||||
}
|
||||
// banner 模式默认为警告
|
||||
return banner ? 'warning' : 'info';
|
||||
};
|
||||
|
||||
// closeable when closeText is assigned
|
||||
const isClosable = closeText ? true : closable;
|
||||
const type = getType();
|
||||
|
||||
const renderIconNode = () => {
|
||||
const { icon } = props;
|
||||
// use outline icon in alert with description
|
||||
const iconType = (description ? iconMapOutlined : iconMapFilled)[type] || null;
|
||||
if (icon) {
|
||||
return replaceElement(icon, <span className={`${prefixCls}-icon`}>{icon}</span>, () => ({
|
||||
className: classNames(`${prefixCls}-icon`, {
|
||||
[(icon as any).props.className]: (icon as any).props.className,
|
||||
}),
|
||||
}));
|
||||
// closeable when closeText is assigned
|
||||
if (closeText) {
|
||||
closable = true;
|
||||
}
|
||||
return React.createElement(iconType, { className: `${prefixCls}-icon` });
|
||||
};
|
||||
|
||||
const renderCloseIcon = () => {
|
||||
return isClosable ? (
|
||||
const alertCls = classNames(
|
||||
prefixCls,
|
||||
`${prefixCls}-${type}`,
|
||||
{
|
||||
[`${prefixCls}-closing`]: closing,
|
||||
[`${prefixCls}-with-description`]: !!description,
|
||||
[`${prefixCls}-no-icon`]: !showIcon,
|
||||
[`${prefixCls}-banner`]: !!banner,
|
||||
[`${prefixCls}-closable`]: closable,
|
||||
},
|
||||
className,
|
||||
);
|
||||
|
||||
const closeIcon = closable ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleClose}
|
||||
onClick={this.handleClose}
|
||||
className={`${prefixCls}-close-icon`}
|
||||
tabIndex={0}
|
||||
>
|
||||
{closeText ? (
|
||||
<span className={`${prefixCls}-close-text`}>{closeText}</span>
|
||||
) : (
|
||||
<CloseOutlined />
|
||||
<Icon type="close" />
|
||||
)}
|
||||
</button>
|
||||
) : null;
|
||||
|
||||
const dataOrAriaProps = getDataOrAriaProps(this.props);
|
||||
|
||||
const iconNode = (icon &&
|
||||
(React.isValidElement<{ className?: string }>(icon) ? (
|
||||
React.cloneElement(icon, {
|
||||
className: classNames(`${prefixCls}-icon`, {
|
||||
[icon.props.className as string]: icon.props.className,
|
||||
}),
|
||||
})
|
||||
) : (
|
||||
<span className={`${prefixCls}-icon`}>{icon}</span>
|
||||
))) || <Icon className={`${prefixCls}-icon`} type={iconType} theme={iconTheme} />;
|
||||
|
||||
return closed ? null : (
|
||||
<Animate
|
||||
component=""
|
||||
showProp="data-show"
|
||||
transitionName={`${prefixCls}-slide-up`}
|
||||
onEnd={this.animationEnd}
|
||||
>
|
||||
<div data-show={!closing} className={alertCls} style={style} {...dataOrAriaProps}>
|
||||
{showIcon ? iconNode : null}
|
||||
<span className={`${prefixCls}-message`}>{message}</span>
|
||||
<span className={`${prefixCls}-description`}>{description}</span>
|
||||
{closeIcon}
|
||||
</div>
|
||||
</Animate>
|
||||
);
|
||||
};
|
||||
|
||||
// banner 模式默认有 Icon
|
||||
const isShowIcon = banner && showIcon === undefined ? true : showIcon;
|
||||
|
||||
const alertCls = classNames(
|
||||
prefixCls,
|
||||
`${prefixCls}-${type}`,
|
||||
{
|
||||
[`${prefixCls}-closing`]: closing,
|
||||
[`${prefixCls}-with-description`]: !!description,
|
||||
[`${prefixCls}-no-icon`]: !isShowIcon,
|
||||
[`${prefixCls}-banner`]: !!banner,
|
||||
[`${prefixCls}-closable`]: isClosable,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
);
|
||||
|
||||
const dataOrAriaProps = getDataOrAriaProps(props);
|
||||
|
||||
return closed ? null : (
|
||||
<Animate
|
||||
component=""
|
||||
showProp="data-show"
|
||||
transitionName={`${prefixCls}-slide-up`}
|
||||
onEnd={animationEnd}
|
||||
>
|
||||
<div
|
||||
ref={ref}
|
||||
data-show={!closing}
|
||||
className={alertCls}
|
||||
style={style}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onClick={onClick}
|
||||
{...dataOrAriaProps}
|
||||
>
|
||||
{isShowIcon ? renderIconNode() : null}
|
||||
<span className={`${prefixCls}-message`}>{message}</span>
|
||||
<span className={`${prefixCls}-description`}>{description}</span>
|
||||
{renderCloseIcon()}
|
||||
</div>
|
||||
</Animate>
|
||||
);
|
||||
};
|
||||
|
||||
Alert.ErrorBoundary = ErrorBoundary;
|
||||
|
||||
export default Alert;
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderAlert}</ConfigConsumer>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ category: Components
|
||||
subtitle: 警告提示
|
||||
type: 反馈
|
||||
title: Alert
|
||||
cover: https://gw.alipayobjects.com/zos/antfincdn/nuhf3vdRno/Alert.svg
|
||||
---
|
||||
|
||||
警告提示,展现需要关注的信息。
|
||||
@@ -15,22 +14,15 @@ cover: https://gw.alipayobjects.com/zos/antfincdn/nuhf3vdRno/Alert.svg
|
||||
|
||||
## API
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| afterClose | 关闭动画结束后触发的回调函数 | () => void | - |
|
||||
| banner | 是否用作顶部公告 | boolean | false |
|
||||
| closable | 默认不显示关闭按钮 | boolean | 无 |
|
||||
| closeText | 自定义关闭按钮 | string\|ReactNode | 无 |
|
||||
| description | 警告提示的辅助性文字介绍 | string\|ReactNode | 无 |
|
||||
| icon | 自定义图标,`showIcon` 为 `true` 时有效 | ReactNode | - |
|
||||
| message | 警告提示内容 | string\|ReactNode | 无 |
|
||||
| showIcon | 是否显示辅助图标 | boolean | false,`banner` 模式下默认值为 true |
|
||||
| type | 指定警告提示的样式,有四种选择 `success`、`info`、`warning`、`error` | string | `info`,`banner` 模式下默认值为 `warning` |
|
||||
| onClose | 关闭时触发的回调函数 | (e: MouseEvent) => void | 无 |
|
||||
|
||||
### Alert.ErrorBoundary
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| message | 自定义错误标题,如果未指定会展示原生报错信息 | ReactNode | `{{ error }}` | |
|
||||
| description | 自定义错误内容,如果未指定会展示报错堆栈 | ReactNode | `{{ error stack }}` | |
|
||||
| afterClose | 关闭动画结束后触发的回调函数 | () => void | - | 3.3.1 |
|
||||
| banner | 是否用作顶部公告 | boolean | false | |
|
||||
| closable | 默认不显示关闭按钮 | boolean | 无 | |
|
||||
| closeText | 自定义关闭按钮 | string\|ReactNode | 无 | |
|
||||
| description | 警告提示的辅助性文字介绍 | string\|ReactNode | 无 | |
|
||||
| icon | 自定义图标,`showIcon` 为 `true` 时有效 | ReactNode | - | 3.10.0 |
|
||||
| message | 警告提示内容 | string\|ReactNode | 无 | |
|
||||
| showIcon | 是否显示辅助图标 | boolean | false,`banner` 模式下默认值为 true | |
|
||||
| type | 指定警告提示的样式,有四种选择 `success`、`info`、`warning`、`error` | string | `info`,`banner` 模式下默认值为 `warning` | |
|
||||
| onClose | 关闭时触发的回调函数 | (e: MouseEvent) => void | 无 | |
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
|
||||
@alert-prefix-cls: ~'@{ant-prefix}-alert';
|
||||
|
||||
@alert-message-color: @heading-color;
|
||||
@alert-text-color: @text-color;
|
||||
@alert-close-color: @text-color-secondary;
|
||||
@alert-close-hover-color: @icon-color-hover;
|
||||
|
||||
.@{alert-prefix-cls} {
|
||||
.reset-component;
|
||||
|
||||
@@ -12,11 +17,7 @@
|
||||
border-radius: @border-radius-base;
|
||||
|
||||
&&-no-icon {
|
||||
padding: @alert-no-icon-padding-vertical 15px;
|
||||
.@{alert-prefix-cls}-close-icon {
|
||||
top: @alert-no-icon-padding-vertical + @font-size-base * @line-height-base / 2 -
|
||||
@font-size-base / 2;
|
||||
}
|
||||
padding: 8px 15px;
|
||||
}
|
||||
|
||||
&&-closable {
|
||||
@@ -25,14 +26,14 @@
|
||||
|
||||
&-icon {
|
||||
position: absolute;
|
||||
top: @alert-icon-top;
|
||||
top: 8px + @font-size-base * @line-height-base / 2 - @font-size-base / 2;
|
||||
left: 16px;
|
||||
}
|
||||
|
||||
&-description {
|
||||
display: none;
|
||||
font-size: @font-size-base;
|
||||
line-height: @font-size-base + 8px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
&-success {
|
||||
@@ -62,25 +63,19 @@
|
||||
&-error {
|
||||
background-color: @alert-error-bg-color;
|
||||
border: @border-width-base @border-style-base @alert-error-border-color;
|
||||
|
||||
.@{alert-prefix-cls}-icon {
|
||||
color: @alert-error-icon-color;
|
||||
}
|
||||
|
||||
.@{alert-prefix-cls}-description > pre {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-close-icon {
|
||||
position: absolute;
|
||||
top: 8px + @font-size-base * @line-height-base / 2 - @font-size-base / 2;
|
||||
right: @padding-md;
|
||||
top: 8px;
|
||||
right: 16px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
font-size: @font-size-sm;
|
||||
line-height: @font-size-sm;
|
||||
line-height: 22px;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
@@ -105,27 +100,27 @@
|
||||
|
||||
&-with-description {
|
||||
position: relative;
|
||||
padding: @alert-with-description-padding;
|
||||
padding: 15px 15px 15px 64px;
|
||||
color: @alert-text-color;
|
||||
line-height: @line-height-base;
|
||||
border-radius: @border-radius-base;
|
||||
}
|
||||
|
||||
&-with-description&-no-icon {
|
||||
padding: @alert-with-description-no-icon-padding-vertical 15px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
&-with-description &-icon {
|
||||
position: absolute;
|
||||
top: @alert-with-description-icon-top;
|
||||
left: @alert-with-description-icon-size;
|
||||
font-size: @alert-with-description-icon-size;
|
||||
top: 16px;
|
||||
left: 24px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
&-with-description &-close-icon {
|
||||
position: absolute;
|
||||
top: @padding-md;
|
||||
right: @padding-md;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
font-size: @font-size-base;
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -191,5 +186,3 @@
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@import './rtl';
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
|
||||
@alert-prefix-cls: ~'@{ant-prefix}-alert';
|
||||
|
||||
.@{alert-prefix-cls} {
|
||||
&&-rtl {
|
||||
padding: 8px 37px 8px 15px;
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
&&-no-icon {
|
||||
.@{alert-prefix-cls}-rtl& {
|
||||
padding: @alert-no-icon-padding-vertical 15px;
|
||||
}
|
||||
}
|
||||
|
||||
&&-closable {
|
||||
.@{alert-prefix-cls}.@{alert-prefix-cls}-rtl& {
|
||||
padding-right: 37px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
&&-no-icon&-closable {
|
||||
.@{alert-prefix-cls}.@{alert-prefix-cls}-rtl& {
|
||||
padding-right: 15px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
&-icon {
|
||||
.@{alert-prefix-cls}-rtl & {
|
||||
right: @padding-md;
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&-close-icon {
|
||||
.@{alert-prefix-cls}-rtl & {
|
||||
right: auto;
|
||||
left: @padding-md;
|
||||
}
|
||||
}
|
||||
|
||||
&-with-description,
|
||||
&-with-description&-closable {
|
||||
.@{alert-prefix-cls}.@{alert-prefix-cls}-rtl& {
|
||||
padding: @alert-with-description-padding-vertical @alert-with-description-icon-size * 2 +
|
||||
@alert-with-description-padding-vertical @alert-with-description-no-icon-padding-vertical
|
||||
15px;
|
||||
}
|
||||
}
|
||||
|
||||
&-with-description&-no-icon {
|
||||
.@{alert-prefix-cls}.@{alert-prefix-cls}-rtl& {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
&-with-description &-icon {
|
||||
.@{alert-prefix-cls}-rtl& {
|
||||
right: @alert-with-description-icon-size;
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&-with-description &-close-icon {
|
||||
.@{alert-prefix-cls}-rtl& {
|
||||
right: auto;
|
||||
left: @padding-md;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,23 @@
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import addEventListener from 'rc-util/lib/Dom/addEventListener';
|
||||
import Affix from '../affix';
|
||||
import AnchorLink from './AnchorLink';
|
||||
import { ConfigContext, ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import scrollTo from '../_util/scrollTo';
|
||||
import getScroll from '../_util/getScroll';
|
||||
import AnchorContext from './context';
|
||||
|
||||
function getDefaultContainer() {
|
||||
return window;
|
||||
}
|
||||
|
||||
function getOffsetTop(element: HTMLElement, container: AnchorContainer): number {
|
||||
if (!element) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!element.getClientRects().length) {
|
||||
return 0;
|
||||
}
|
||||
@@ -31,7 +35,7 @@ function getOffsetTop(element: HTMLElement, container: AnchorContainer): number
|
||||
return rect.top;
|
||||
}
|
||||
|
||||
const sharpMatcherRegx = /#(\S+)$/;
|
||||
const sharpMatcherRegx = /#([^#]+)$/;
|
||||
|
||||
type Section = {
|
||||
link: string;
|
||||
@@ -84,22 +88,23 @@ export interface AntAnchor {
|
||||
) => void;
|
||||
}
|
||||
|
||||
export default class Anchor extends React.Component<AnchorProps, AnchorState, ConfigConsumerProps> {
|
||||
export default class Anchor extends React.Component<AnchorProps, AnchorState> {
|
||||
static Link: typeof AnchorLink;
|
||||
|
||||
static defaultProps = {
|
||||
affix: true,
|
||||
showInkInFixed: false,
|
||||
getContainer: getDefaultContainer,
|
||||
};
|
||||
|
||||
static contextType = ConfigContext;
|
||||
static childContextTypes = {
|
||||
antAnchor: PropTypes.object,
|
||||
};
|
||||
|
||||
state = {
|
||||
activeLink: null,
|
||||
};
|
||||
|
||||
content: ConfigConsumerProps;
|
||||
|
||||
private inkNode: HTMLSpanElement;
|
||||
|
||||
// scroll scope's container
|
||||
@@ -113,38 +118,37 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState, Co
|
||||
|
||||
private prefixCls?: string;
|
||||
|
||||
// Context
|
||||
registerLink = (link: string) => {
|
||||
if (!this.links.includes(link)) {
|
||||
this.links.push(link);
|
||||
}
|
||||
};
|
||||
|
||||
unregisterLink = (link: string) => {
|
||||
const index = this.links.indexOf(link);
|
||||
if (index !== -1) {
|
||||
this.links.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
getContainer = () => {
|
||||
const { getTargetContainer } = this.context;
|
||||
const { getContainer } = this.props;
|
||||
|
||||
const getFunc = getContainer || getTargetContainer || getDefaultContainer;
|
||||
|
||||
return getFunc();
|
||||
};
|
||||
getChildContext() {
|
||||
const antAnchor: AntAnchor = {
|
||||
registerLink: (link: string) => {
|
||||
if (!this.links.includes(link)) {
|
||||
this.links.push(link);
|
||||
}
|
||||
},
|
||||
unregisterLink: (link: string) => {
|
||||
const index = this.links.indexOf(link);
|
||||
if (index !== -1) {
|
||||
this.links.splice(index, 1);
|
||||
}
|
||||
},
|
||||
activeLink: this.state.activeLink,
|
||||
scrollTo: this.handleScrollTo,
|
||||
onClick: this.props.onClick,
|
||||
};
|
||||
return { antAnchor };
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.scrollContainer = this.getContainer();
|
||||
const { getContainer } = this.props as AnchorDefaultProps;
|
||||
this.scrollContainer = getContainer();
|
||||
this.scrollEvent = addEventListener(this.scrollContainer, 'scroll', this.handleScroll);
|
||||
this.handleScroll();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.scrollEvent) {
|
||||
const currentContainer = this.getContainer();
|
||||
const { getContainer } = this.props as AnchorDefaultProps;
|
||||
const currentContainer = getContainer();
|
||||
if (this.scrollContainer !== currentContainer) {
|
||||
this.scrollContainer = currentContainer;
|
||||
this.scrollEvent.remove();
|
||||
@@ -168,8 +172,14 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState, Co
|
||||
return getCurrentAnchor();
|
||||
}
|
||||
|
||||
const activeLink = '';
|
||||
if (typeof document === 'undefined') {
|
||||
return activeLink;
|
||||
}
|
||||
|
||||
const linkSections: Array<Section> = [];
|
||||
const container = this.getContainer();
|
||||
const { getContainer } = this.props as AnchorDefaultProps;
|
||||
const container = getContainer();
|
||||
this.links.forEach(link => {
|
||||
const sharpLinkMatch = sharpMatcherRegx.exec(link.toString());
|
||||
if (!sharpLinkMatch) {
|
||||
@@ -195,10 +205,10 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState, Co
|
||||
}
|
||||
|
||||
handleScrollTo = (link: string) => {
|
||||
const { offsetTop, targetOffset } = this.props;
|
||||
const { offsetTop, getContainer, targetOffset } = this.props as AnchorDefaultProps;
|
||||
|
||||
this.setCurrentActiveLink(link);
|
||||
const container = this.getContainer();
|
||||
const container = getContainer();
|
||||
const scrollTop = getScroll(container, true);
|
||||
const sharpLinkMatch = sharpMatcherRegx.exec(link);
|
||||
if (!sharpLinkMatch) {
|
||||
@@ -218,7 +228,7 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState, Co
|
||||
callback: () => {
|
||||
this.animating = false;
|
||||
},
|
||||
getContainer: this.getContainer,
|
||||
getContainer,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -253,6 +263,9 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState, Co
|
||||
};
|
||||
|
||||
updateInk = () => {
|
||||
if (typeof document === 'undefined') {
|
||||
return;
|
||||
}
|
||||
const { prefixCls } = this;
|
||||
const anchorNode = ReactDOM.findDOMNode(this) as Element;
|
||||
const linkNode = anchorNode.getElementsByClassName(`${prefixCls}-link-title-active`)[0];
|
||||
@@ -261,9 +274,7 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState, Co
|
||||
}
|
||||
};
|
||||
|
||||
render = () => {
|
||||
const { getPrefixCls, direction } = this.context;
|
||||
|
||||
renderAnchor = ({ getPrefixCls }: ConfigConsumerProps) => {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
className = '',
|
||||
@@ -272,6 +283,7 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState, Co
|
||||
affix,
|
||||
showInkInFixed,
|
||||
children,
|
||||
getContainer,
|
||||
} = this.props;
|
||||
const { activeLink } = this.state;
|
||||
|
||||
@@ -286,9 +298,7 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState, Co
|
||||
visible: activeLink,
|
||||
});
|
||||
|
||||
const wrapperClass = classNames(className, `${prefixCls}-wrapper`, {
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
});
|
||||
const wrapperClass = classNames(className, `${prefixCls}-wrapper`);
|
||||
|
||||
const anchorClass = classNames(prefixCls, {
|
||||
fixed: !affix && !showInkInFixed,
|
||||
@@ -310,24 +320,16 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState, Co
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<AnchorContext.Provider
|
||||
value={{
|
||||
registerLink: this.registerLink,
|
||||
unregisterLink: this.unregisterLink,
|
||||
activeLink: this.state.activeLink,
|
||||
scrollTo: this.handleScrollTo,
|
||||
onClick: this.props.onClick,
|
||||
}}
|
||||
>
|
||||
{!affix ? (
|
||||
anchorContent
|
||||
) : (
|
||||
<Affix offsetTop={offsetTop} target={this.getContainer}>
|
||||
{anchorContent}
|
||||
</Affix>
|
||||
)}
|
||||
</AnchorContext.Provider>
|
||||
return !affix ? (
|
||||
anchorContent
|
||||
) : (
|
||||
<Affix offsetTop={offsetTop} target={getContainer}>
|
||||
{anchorContent}
|
||||
</Affix>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderAnchor}</ConfigConsumer>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import * as React from 'react';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import { polyfill } from 'react-lifecycles-compat';
|
||||
import classNames from 'classnames';
|
||||
import { AntAnchor } from './Anchor';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import AnchorContext from './context';
|
||||
|
||||
export interface AnchorLinkProps {
|
||||
prefixCls?: string;
|
||||
@@ -13,33 +14,37 @@ export interface AnchorLinkProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
class AnchorLink extends React.Component<AnchorLinkProps, any, AntAnchor> {
|
||||
class AnchorLink extends React.Component<AnchorLinkProps, any> {
|
||||
static defaultProps = {
|
||||
href: '#',
|
||||
};
|
||||
|
||||
static contextType = AnchorContext;
|
||||
static contextTypes = {
|
||||
antAnchor: PropTypes.object,
|
||||
};
|
||||
|
||||
context: AntAnchor;
|
||||
context: {
|
||||
antAnchor: AntAnchor;
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.context.registerLink(this.props.href);
|
||||
this.context.antAnchor.registerLink(this.props.href);
|
||||
}
|
||||
|
||||
componentDidUpdate({ href: prevHref }: AnchorLinkProps) {
|
||||
const { href } = this.props;
|
||||
if (prevHref !== href) {
|
||||
this.context.unregisterLink(prevHref);
|
||||
this.context.registerLink(href);
|
||||
this.context.antAnchor.unregisterLink(prevHref);
|
||||
this.context.antAnchor.registerLink(href);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.context.unregisterLink(this.props.href);
|
||||
this.context.antAnchor.unregisterLink(this.props.href);
|
||||
}
|
||||
|
||||
handleClick = (e: React.MouseEvent<HTMLElement>) => {
|
||||
const { scrollTo, onClick } = this.context;
|
||||
const { scrollTo, onClick } = this.context.antAnchor;
|
||||
const { href, title } = this.props;
|
||||
if (onClick) {
|
||||
onClick(e, { title, href });
|
||||
@@ -50,7 +55,7 @@ class AnchorLink extends React.Component<AnchorLinkProps, any, AntAnchor> {
|
||||
renderAnchorLink = ({ getPrefixCls }: ConfigConsumerProps) => {
|
||||
const { prefixCls: customizePrefixCls, href, title, children, className, target } = this.props;
|
||||
const prefixCls = getPrefixCls('anchor', customizePrefixCls);
|
||||
const active = this.context.activeLink === href;
|
||||
const active = this.context.antAnchor.activeLink === href;
|
||||
const wrapperClassName = classNames(className, `${prefixCls}-link`, {
|
||||
[`${prefixCls}-link-active`]: active,
|
||||
});
|
||||
@@ -78,4 +83,6 @@ class AnchorLink extends React.Component<AnchorLinkProps, any, AntAnchor> {
|
||||
}
|
||||
}
|
||||
|
||||
polyfill(AnchorLink);
|
||||
|
||||
export default AnchorLink;
|
||||
|
||||
339
components/anchor/__tests__/Anchor.test.js
Normal file
339
components/anchor/__tests__/Anchor.test.js
Normal file
@@ -0,0 +1,339 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Anchor from '..';
|
||||
import { spyElementPrototypes } from '../../__tests__/util/domHook';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
|
||||
const { Link } = Anchor;
|
||||
|
||||
describe('Anchor Render', () => {
|
||||
const getBoundingClientRectMock = jest.fn(() => ({
|
||||
width: 100,
|
||||
height: 100,
|
||||
top: 1000,
|
||||
}));
|
||||
const getClientRectsMock = jest.fn(() => ({
|
||||
length: 1,
|
||||
}));
|
||||
const headingSpy = spyElementPrototypes(HTMLHeadingElement, {
|
||||
getBoundingClientRect: getBoundingClientRectMock,
|
||||
getClientRects: getClientRectsMock,
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
headingSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('Anchor render perfectly', () => {
|
||||
const wrapper = mount(
|
||||
<Anchor>
|
||||
<Link href="#API" title="API" />
|
||||
</Anchor>,
|
||||
);
|
||||
|
||||
wrapper.find('a[href="#API"]').simulate('click');
|
||||
|
||||
wrapper.instance().handleScroll();
|
||||
expect(wrapper.instance().state).not.toBe(null);
|
||||
});
|
||||
|
||||
it('Anchor render perfectly for complete href - click', () => {
|
||||
const wrapper = mount(
|
||||
<Anchor>
|
||||
<Link href="http://www.example.com/#API" title="API" />
|
||||
</Anchor>,
|
||||
);
|
||||
wrapper.find('a[href="http://www.example.com/#API"]').simulate('click');
|
||||
expect(wrapper.instance().state.activeLink).toBe('http://www.example.com/#API');
|
||||
});
|
||||
|
||||
it('Anchor render perfectly for complete href - scroll', () => {
|
||||
let root = document.getElementById('root');
|
||||
if (!root) {
|
||||
root = document.createElement('div', { id: 'root' });
|
||||
root.id = 'root';
|
||||
document.body.appendChild(root);
|
||||
}
|
||||
mount(<div id="API">Hello</div>, { attachTo: root });
|
||||
const wrapper = mount(
|
||||
<Anchor>
|
||||
<Link href="http://www.example.com/#API" title="API" />
|
||||
</Anchor>,
|
||||
);
|
||||
wrapper.instance().handleScroll();
|
||||
expect(wrapper.instance().state.activeLink).toBe('http://www.example.com/#API');
|
||||
});
|
||||
|
||||
it('Anchor render perfectly for complete href - scrollTo', async () => {
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo');
|
||||
let root = document.getElementById('root');
|
||||
if (!root) {
|
||||
root = document.createElement('div', { id: 'root' });
|
||||
root.id = 'root';
|
||||
document.body.appendChild(root);
|
||||
}
|
||||
mount(<div id="API">Hello</div>, { attachTo: root });
|
||||
const wrapper = mount(
|
||||
<Anchor>
|
||||
<Link href="##API" title="API" />
|
||||
</Anchor>,
|
||||
);
|
||||
wrapper.instance().handleScrollTo('##API');
|
||||
expect(wrapper.instance().state.activeLink).toBe('##API');
|
||||
expect(scrollToSpy).not.toHaveBeenCalled();
|
||||
await sleep(1000);
|
||||
expect(scrollToSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should remove listener when unmount', async () => {
|
||||
const wrapper = mount(
|
||||
<Anchor>
|
||||
<Link href="#API" title="API" />
|
||||
</Anchor>,
|
||||
);
|
||||
const removeListenerSpy = jest.spyOn(wrapper.instance().scrollEvent, 'remove');
|
||||
wrapper.unmount();
|
||||
expect(removeListenerSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should unregister link when unmount children', async () => {
|
||||
const wrapper = mount(
|
||||
<Anchor>
|
||||
<Link href="#API" title="API" />
|
||||
</Anchor>,
|
||||
);
|
||||
expect(wrapper.instance().links).toEqual(['#API']);
|
||||
wrapper.setProps({ children: null });
|
||||
expect(wrapper.instance().links).toEqual([]);
|
||||
});
|
||||
|
||||
it('should update links when link href update', async () => {
|
||||
let anchorInstance = null;
|
||||
function AnchorUpdate({ href }) {
|
||||
return (
|
||||
<Anchor
|
||||
ref={c => {
|
||||
anchorInstance = c;
|
||||
}}
|
||||
>
|
||||
<Link href={href} title="API" />
|
||||
</Anchor>
|
||||
);
|
||||
}
|
||||
const wrapper = mount(<AnchorUpdate href="#API" />);
|
||||
|
||||
expect(anchorInstance.links).toEqual(['#API']);
|
||||
wrapper.setProps({ href: '#API_1' });
|
||||
expect(anchorInstance.links).toEqual(['#API_1']);
|
||||
});
|
||||
|
||||
it('Anchor onClick event', () => {
|
||||
let event;
|
||||
let link;
|
||||
const handleClick = (...arg) => {
|
||||
[event, link] = arg;
|
||||
};
|
||||
|
||||
const href = '#API';
|
||||
const title = 'API';
|
||||
|
||||
const wrapper = mount(
|
||||
<Anchor onClick={handleClick}>
|
||||
<Link href={href} title={title} />
|
||||
</Anchor>,
|
||||
);
|
||||
|
||||
wrapper.find(`a[href="${href}"]`).simulate('click');
|
||||
|
||||
wrapper.instance().handleScroll();
|
||||
expect(event).not.toBe(undefined);
|
||||
expect(link).toEqual({ href, title });
|
||||
});
|
||||
|
||||
it('Different function returns the same DOM', async () => {
|
||||
let root = document.getElementById('root');
|
||||
if (!root) {
|
||||
root = document.createElement('div', { id: 'root' });
|
||||
root.id = 'root';
|
||||
document.body.appendChild(root);
|
||||
}
|
||||
mount(<div id="API">Hello</div>, { attachTo: root });
|
||||
const getContainerA = () => {
|
||||
return document.getElementById('API');
|
||||
};
|
||||
const getContainerB = () => {
|
||||
return document.getElementById('API');
|
||||
};
|
||||
|
||||
const wrapper = mount(
|
||||
<Anchor getContainer={getContainerA}>
|
||||
<Link href="#API" title="API" />
|
||||
</Anchor>,
|
||||
);
|
||||
const removeListenerSpy = jest.spyOn(wrapper.instance().scrollEvent, 'remove');
|
||||
await sleep(1000);
|
||||
wrapper.setProps({ getContainer: getContainerB });
|
||||
expect(removeListenerSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Different function returns different DOM', async () => {
|
||||
let root = document.getElementById('root');
|
||||
if (!root) {
|
||||
root = document.createElement('div', { id: 'root' });
|
||||
root.id = 'root';
|
||||
document.body.appendChild(root);
|
||||
}
|
||||
mount(
|
||||
<div>
|
||||
<div id="API1">Hello</div>
|
||||
<div id="API2">World</div>
|
||||
</div>,
|
||||
{ attachTo: root },
|
||||
);
|
||||
const getContainerA = () => {
|
||||
return document.getElementById('API1');
|
||||
};
|
||||
const getContainerB = () => {
|
||||
return document.getElementById('API2');
|
||||
};
|
||||
const wrapper = mount(
|
||||
<Anchor getContainer={getContainerA}>
|
||||
<Link href="#API1" title="API1" />
|
||||
<Link href="#API2" title="API2" />
|
||||
</Anchor>,
|
||||
);
|
||||
const removeListenerSpy = jest.spyOn(wrapper.instance().scrollEvent, 'remove');
|
||||
expect(removeListenerSpy).not.toHaveBeenCalled();
|
||||
await sleep(1000);
|
||||
wrapper.setProps({ getContainer: getContainerB });
|
||||
expect(removeListenerSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Same function returns the same DOM', () => {
|
||||
let root = document.getElementById('root');
|
||||
if (!root) {
|
||||
root = document.createElement('div', { id: 'root' });
|
||||
root.id = 'root';
|
||||
document.body.appendChild(root);
|
||||
}
|
||||
mount(<div id="API">Hello</div>, { attachTo: root });
|
||||
const getContainer = () => document.getElementById('API');
|
||||
const wrapper = mount(
|
||||
<Anchor getContainer={getContainer}>
|
||||
<Link href="#API" title="API" />
|
||||
</Anchor>,
|
||||
);
|
||||
wrapper.find('a[href="#API"]').simulate('click');
|
||||
wrapper.instance().handleScroll();
|
||||
expect(wrapper.instance().state).not.toBe(null);
|
||||
});
|
||||
|
||||
it('Same function returns different DOM', async () => {
|
||||
let root = document.getElementById('root');
|
||||
if (!root) {
|
||||
root = document.createElement('div', { id: 'root' });
|
||||
root.id = 'root';
|
||||
document.body.appendChild(root);
|
||||
}
|
||||
mount(
|
||||
<div>
|
||||
<div id="API1">Hello</div>
|
||||
<div id="API2">World</div>
|
||||
</div>,
|
||||
{ attachTo: root },
|
||||
);
|
||||
const holdContainer = {
|
||||
container: document.getElementById('API1'),
|
||||
};
|
||||
const getContainer = () => {
|
||||
return holdContainer.container;
|
||||
};
|
||||
const wrapper = mount(
|
||||
<Anchor getContainer={getContainer}>
|
||||
<Link href="#API1" title="API1" />
|
||||
<Link href="#API2" title="API2" />
|
||||
</Anchor>,
|
||||
);
|
||||
const removeListenerSpy = jest.spyOn(wrapper.instance().scrollEvent, 'remove');
|
||||
expect(removeListenerSpy).not.toHaveBeenCalled();
|
||||
await sleep(1000);
|
||||
holdContainer.container = document.getElementById('API2');
|
||||
wrapper.setProps({ 'data-only-trigger-re-render': true });
|
||||
expect(removeListenerSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Anchor getCurrentAnchor prop', () => {
|
||||
const getCurrentAnchor = () => '#API2';
|
||||
const wrapper = mount(
|
||||
<Anchor getCurrentAnchor={getCurrentAnchor}>
|
||||
<Link href="#API1" title="API1" />
|
||||
<Link href="#API2" title="API2" />
|
||||
</Anchor>,
|
||||
);
|
||||
expect(wrapper.instance().state.activeLink).toBe('#API2');
|
||||
});
|
||||
|
||||
it('Anchor targetOffset prop', () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
let dateNowMock;
|
||||
|
||||
function dataNowMockFn() {
|
||||
let start = 0;
|
||||
|
||||
const handler = () => {
|
||||
return (start += 1000);
|
||||
};
|
||||
|
||||
return jest.spyOn(Date, 'now').mockImplementation(handler);
|
||||
}
|
||||
|
||||
dateNowMock = dataNowMockFn();
|
||||
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo');
|
||||
let root = document.getElementById('root');
|
||||
if (!root) {
|
||||
root = document.createElement('div', { id: 'root' });
|
||||
root.id = 'root';
|
||||
document.body.appendChild(root);
|
||||
}
|
||||
mount(<h1 id="API">Hello</h1>, { attachTo: root });
|
||||
const wrapper = mount(
|
||||
<Anchor>
|
||||
<Link href="#API" title="API" />
|
||||
</Anchor>,
|
||||
);
|
||||
wrapper.instance().handleScrollTo('#API');
|
||||
jest.runAllTimers();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000);
|
||||
dateNowMock = dataNowMockFn();
|
||||
|
||||
wrapper.setProps({ offsetTop: 100 });
|
||||
wrapper.instance().handleScrollTo('#API');
|
||||
jest.runAllTimers();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900);
|
||||
dateNowMock = dataNowMockFn();
|
||||
|
||||
wrapper.setProps({ targetOffset: 200 });
|
||||
wrapper.instance().handleScrollTo('#API');
|
||||
jest.runAllTimers();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
|
||||
|
||||
dateNowMock.mockRestore();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('Anchor onChange prop', async () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
<Anchor onChange={onChange}>
|
||||
<Link href="#API1" title="API1" />
|
||||
<Link href="#API2" title="API2" />
|
||||
</Anchor>,
|
||||
);
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
wrapper.instance().handleScrollTo('#API2');
|
||||
expect(onChange).toHaveBeenCalledTimes(2);
|
||||
expect(onChange).toHaveBeenCalledWith('#API2');
|
||||
});
|
||||
});
|
||||
@@ -1,469 +0,0 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Anchor from '..';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
|
||||
const { Link } = Anchor;
|
||||
|
||||
function createGetContainer(id: string) {
|
||||
return () => {
|
||||
const container = document.getElementById(id);
|
||||
if (container == null) {
|
||||
throw new Error();
|
||||
}
|
||||
return container;
|
||||
};
|
||||
}
|
||||
|
||||
function createDiv() {
|
||||
const root = document.createElement('div');
|
||||
document.body.appendChild(root);
|
||||
return root;
|
||||
}
|
||||
|
||||
let idCounter = 0;
|
||||
const getHashUrl = () => `Anchor-API-${idCounter++}`;
|
||||
|
||||
describe('Anchor Render', () => {
|
||||
const getBoundingClientRectMock = jest.spyOn(
|
||||
HTMLHeadingElement.prototype,
|
||||
'getBoundingClientRect',
|
||||
);
|
||||
const getClientRectsMock = jest.spyOn(HTMLHeadingElement.prototype, 'getClientRects');
|
||||
|
||||
beforeAll(() => {
|
||||
getBoundingClientRectMock.mockReturnValue({
|
||||
width: 100,
|
||||
height: 100,
|
||||
top: 1000,
|
||||
} as DOMRect);
|
||||
getClientRectsMock.mockReturnValue({ length: 1 } as DOMRectList);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
getBoundingClientRectMock.mockRestore();
|
||||
getClientRectsMock.mockRestore();
|
||||
});
|
||||
|
||||
it('Anchor render perfectly', () => {
|
||||
const hash = getHashUrl();
|
||||
const wrapper = mount<Anchor>(
|
||||
<Anchor>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
|
||||
wrapper.find(`a[href="#${hash}"]`).simulate('click');
|
||||
|
||||
wrapper.instance().handleScroll();
|
||||
expect(wrapper.instance().state).not.toBe(null);
|
||||
});
|
||||
|
||||
it('Anchor render perfectly for complete href - click', () => {
|
||||
const hash = getHashUrl();
|
||||
const wrapper = mount<Anchor>(
|
||||
<Anchor>
|
||||
<Link href={`http://www.example.com/#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
wrapper.find(`a[href="http://www.example.com/#${hash}"]`).simulate('click');
|
||||
expect(wrapper.instance().state.activeLink).toBe(`http://www.example.com/#${hash}`);
|
||||
});
|
||||
|
||||
it('Anchor render perfectly for complete href - hash router', async () => {
|
||||
const root = createDiv();
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo');
|
||||
mount(<div id="/faq?locale=en#Q1">Q1</div>, { attachTo: root });
|
||||
const wrapper = mount<Anchor>(
|
||||
<Anchor>
|
||||
<Link href="/#/faq?locale=en#Q1" title="Q1" />
|
||||
</Anchor>,
|
||||
);
|
||||
|
||||
wrapper.instance().handleScrollTo('/#/faq?locale=en#Q1');
|
||||
expect(wrapper.instance().state.activeLink).toBe('/#/faq?locale=en#Q1');
|
||||
expect(scrollToSpy).not.toHaveBeenCalled();
|
||||
await sleep(1000);
|
||||
expect(scrollToSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Anchor render perfectly for complete href - scroll', () => {
|
||||
const hash = getHashUrl();
|
||||
const root = createDiv();
|
||||
mount(<div id={hash}>Hello</div>, { attachTo: root });
|
||||
const wrapper = mount<Anchor>(
|
||||
<Anchor>
|
||||
<Link href={`http://www.example.com/#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
wrapper.instance().handleScroll();
|
||||
expect(wrapper.instance().state.activeLink).toBe(`http://www.example.com/#${hash}`);
|
||||
});
|
||||
|
||||
it('Anchor render perfectly for complete href - scrollTo', async () => {
|
||||
const hash = getHashUrl();
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo');
|
||||
const root = createDiv();
|
||||
mount(<div id={`#${hash}`}>Hello</div>, { attachTo: root });
|
||||
const wrapper = mount<Anchor>(
|
||||
<Anchor>
|
||||
<Link href={`##${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
wrapper.instance().handleScrollTo(`##${hash}`);
|
||||
expect(wrapper.instance().state.activeLink).toBe(`##${hash}`);
|
||||
const calls = scrollToSpy.mock.calls.length;
|
||||
await sleep(1000);
|
||||
expect(scrollToSpy.mock.calls.length).toBeGreaterThan(calls);
|
||||
});
|
||||
|
||||
it('should remove listener when unmount', async () => {
|
||||
const hash = getHashUrl();
|
||||
const wrapper = mount<Anchor>(
|
||||
<Anchor>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
const removeListenerSpy = jest.spyOn((wrapper.instance() as any).scrollEvent, 'remove');
|
||||
wrapper.unmount();
|
||||
expect(removeListenerSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should unregister link when unmount children', async () => {
|
||||
const hash = getHashUrl();
|
||||
const wrapper = mount<Anchor>(
|
||||
<Anchor>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
expect((wrapper.instance() as any).links).toEqual([`#${hash}`]);
|
||||
wrapper.setProps({ children: null });
|
||||
expect((wrapper.instance() as any).links).toEqual([]);
|
||||
});
|
||||
|
||||
it('should update links when link href update', async () => {
|
||||
const hash = getHashUrl();
|
||||
let anchorInstance: Anchor | null = null;
|
||||
function AnchorUpdate({ href }: { href: string }) {
|
||||
return (
|
||||
<Anchor
|
||||
ref={c => {
|
||||
anchorInstance = c;
|
||||
}}
|
||||
>
|
||||
<Link href={href} title={hash} />
|
||||
</Anchor>
|
||||
);
|
||||
}
|
||||
const wrapper = mount(<AnchorUpdate href={`#${hash}`} />);
|
||||
|
||||
if (anchorInstance == null) {
|
||||
throw new Error('anchorInstance should not be null');
|
||||
}
|
||||
expect((anchorInstance as any).links).toEqual([`#${hash}`]);
|
||||
wrapper.setProps({ href: `#${hash}_1` });
|
||||
expect((anchorInstance as any).links).toEqual([`#${hash}_1`]);
|
||||
});
|
||||
|
||||
it('Anchor onClick event', () => {
|
||||
const hash = getHashUrl();
|
||||
let event;
|
||||
let link;
|
||||
const handleClick = (
|
||||
e: React.MouseEvent<HTMLElement>,
|
||||
_link: { title: React.ReactNode; href: string },
|
||||
) => {
|
||||
event = e;
|
||||
link = _link;
|
||||
};
|
||||
|
||||
const href = `#${hash}`;
|
||||
const title = hash;
|
||||
|
||||
const wrapper = mount<Anchor>(
|
||||
<Anchor onClick={handleClick}>
|
||||
<Link href={href} title={title} />
|
||||
</Anchor>,
|
||||
);
|
||||
|
||||
wrapper.find(`a[href="${href}"]`).simulate('click');
|
||||
|
||||
wrapper.instance().handleScroll();
|
||||
expect(event).not.toBe(undefined);
|
||||
expect(link).toEqual({ href, title });
|
||||
});
|
||||
|
||||
it('Different function returns the same DOM', async () => {
|
||||
const hash = getHashUrl();
|
||||
const root = createDiv();
|
||||
mount(<div id={hash}>Hello</div>, { attachTo: root });
|
||||
const getContainerA = createGetContainer(hash);
|
||||
const getContainerB = createGetContainer(hash);
|
||||
|
||||
const wrapper = mount<Anchor>(
|
||||
<Anchor getContainer={getContainerA}>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
const removeListenerSpy = jest.spyOn((wrapper.instance() as any).scrollEvent, 'remove');
|
||||
await sleep(1000);
|
||||
wrapper.setProps({ getContainer: getContainerB });
|
||||
expect(removeListenerSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Different function returns different DOM', async () => {
|
||||
const hash1 = getHashUrl();
|
||||
const hash2 = getHashUrl();
|
||||
const root = createDiv();
|
||||
mount(
|
||||
<div>
|
||||
<div id={hash1}>Hello</div>
|
||||
<div id={hash2}>World</div>
|
||||
</div>,
|
||||
{ attachTo: root },
|
||||
);
|
||||
const getContainerA = createGetContainer(hash1);
|
||||
const getContainerB = createGetContainer(hash2);
|
||||
const wrapper = mount<Anchor>(
|
||||
<Anchor getContainer={getContainerA}>
|
||||
<Link href={`#${hash1}`} title={hash1} />
|
||||
<Link href={`#${hash2}`} title={hash2} />
|
||||
</Anchor>,
|
||||
);
|
||||
const removeListenerSpy = jest.spyOn((wrapper.instance() as any).scrollEvent, 'remove');
|
||||
expect(removeListenerSpy).not.toHaveBeenCalled();
|
||||
await sleep(1000);
|
||||
wrapper.setProps({ getContainer: getContainerB });
|
||||
expect(removeListenerSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Same function returns the same DOM', () => {
|
||||
const hash = getHashUrl();
|
||||
const root = createDiv();
|
||||
mount(<div id={hash}>Hello</div>, { attachTo: root });
|
||||
const getContainer = createGetContainer(hash);
|
||||
const wrapper = mount(
|
||||
<Anchor getContainer={getContainer}>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
wrapper.find(`a[href="#${hash}"]`).simulate('click');
|
||||
(wrapper.instance() as any).handleScroll();
|
||||
expect(wrapper.instance().state).not.toBe(null);
|
||||
});
|
||||
|
||||
it('Same function returns different DOM', async () => {
|
||||
const hash1 = getHashUrl();
|
||||
const hash2 = getHashUrl();
|
||||
const root = createDiv();
|
||||
mount(
|
||||
<div>
|
||||
<div id={hash1}>Hello</div>
|
||||
<div id={hash2}>World</div>
|
||||
</div>,
|
||||
{ attachTo: root },
|
||||
);
|
||||
const holdContainer = {
|
||||
container: document.getElementById(hash1),
|
||||
};
|
||||
const getContainer = () => {
|
||||
if (holdContainer.container == null) {
|
||||
throw new Error('container should not be null');
|
||||
}
|
||||
return holdContainer.container;
|
||||
};
|
||||
const wrapper = mount(
|
||||
<Anchor getContainer={getContainer}>
|
||||
<Link href={`#${hash1}`} title={hash1} />
|
||||
<Link href={`#${hash2}`} title={hash2} />
|
||||
</Anchor>,
|
||||
);
|
||||
const removeListenerSpy = jest.spyOn((wrapper.instance() as any).scrollEvent, 'remove');
|
||||
expect(removeListenerSpy).not.toHaveBeenCalled();
|
||||
await sleep(1000);
|
||||
holdContainer.container = document.getElementById(hash2);
|
||||
wrapper.setProps({ 'data-only-trigger-re-render': true });
|
||||
expect(removeListenerSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Anchor getCurrentAnchor prop', () => {
|
||||
const hash1 = getHashUrl();
|
||||
const hash2 = getHashUrl();
|
||||
const getCurrentAnchor = () => `#${hash2}`;
|
||||
const wrapper = mount<Anchor>(
|
||||
<Anchor getCurrentAnchor={getCurrentAnchor}>
|
||||
<Link href={`#${hash1}`} title={hash1} />
|
||||
<Link href={`#${hash2}`} title={hash2} />
|
||||
</Anchor>,
|
||||
);
|
||||
expect(wrapper.instance().state.activeLink).toBe(`#${hash2}`);
|
||||
});
|
||||
|
||||
it('Anchor targetOffset prop', async () => {
|
||||
const hash = getHashUrl();
|
||||
let dateNowMock;
|
||||
|
||||
function dataNowMockFn() {
|
||||
let start = 0;
|
||||
|
||||
const handler = () => {
|
||||
return (start += 1000);
|
||||
};
|
||||
|
||||
return jest.spyOn(Date, 'now').mockImplementation(handler);
|
||||
}
|
||||
|
||||
dateNowMock = dataNowMockFn();
|
||||
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo');
|
||||
const root = createDiv();
|
||||
mount(<h1 id={hash}>Hello</h1>, { attachTo: root });
|
||||
const wrapper = mount<Anchor>(
|
||||
<Anchor>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
wrapper.instance().handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000);
|
||||
dateNowMock = dataNowMockFn();
|
||||
|
||||
wrapper.setProps({ offsetTop: 100 });
|
||||
wrapper.instance().handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900);
|
||||
dateNowMock = dataNowMockFn();
|
||||
|
||||
wrapper.setProps({ targetOffset: 200 });
|
||||
wrapper.instance().handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
|
||||
|
||||
dateNowMock.mockRestore();
|
||||
});
|
||||
|
||||
it('Anchor onChange prop', async () => {
|
||||
const hash1 = getHashUrl();
|
||||
const hash2 = getHashUrl();
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount<Anchor>(
|
||||
<Anchor onChange={onChange}>
|
||||
<Link href={`#${hash1}`} title={hash1} />
|
||||
<Link href={`#${hash2}`} title={hash2} />
|
||||
</Anchor>,
|
||||
);
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
wrapper.instance().handleScrollTo(hash2);
|
||||
expect(onChange).toHaveBeenCalledTimes(2);
|
||||
expect(onChange).toHaveBeenCalledWith(hash2);
|
||||
});
|
||||
|
||||
it('invalid hash', async () => {
|
||||
const wrapper = mount<Anchor>(
|
||||
<Anchor>
|
||||
<Link href="notexsited" title="title" />
|
||||
</Anchor>,
|
||||
);
|
||||
|
||||
wrapper.find(`a[href="notexsited"]`).simulate('click');
|
||||
|
||||
wrapper.instance().handleScrollTo('notexsited');
|
||||
expect(wrapper.instance().state).not.toBe(null);
|
||||
});
|
||||
|
||||
it('test edge case when getBoundingClientRect return zero size', async () => {
|
||||
getBoundingClientRectMock.mockReturnValue({
|
||||
width: 0,
|
||||
height: 0,
|
||||
top: 1000,
|
||||
} as DOMRect);
|
||||
const hash = getHashUrl();
|
||||
let dateNowMock;
|
||||
|
||||
function dataNowMockFn() {
|
||||
let start = 0;
|
||||
|
||||
const handler = () => {
|
||||
return (start += 1000);
|
||||
};
|
||||
|
||||
return jest.spyOn(Date, 'now').mockImplementation(handler);
|
||||
}
|
||||
|
||||
dateNowMock = dataNowMockFn();
|
||||
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo');
|
||||
const root = createDiv();
|
||||
mount(<h1 id={hash}>Hello</h1>, { attachTo: root });
|
||||
const wrapper = mount<Anchor>(
|
||||
<Anchor>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
wrapper.instance().handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000);
|
||||
dateNowMock = dataNowMockFn();
|
||||
|
||||
wrapper.setProps({ offsetTop: 100 });
|
||||
wrapper.instance().handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900);
|
||||
dateNowMock = dataNowMockFn();
|
||||
|
||||
wrapper.setProps({ targetOffset: 200 });
|
||||
wrapper.instance().handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
|
||||
|
||||
dateNowMock.mockRestore();
|
||||
getBoundingClientRectMock.mockReturnValue({
|
||||
width: 100,
|
||||
height: 100,
|
||||
top: 1000,
|
||||
} as DOMRect);
|
||||
});
|
||||
|
||||
it('test edge case when container is not windows', async () => {
|
||||
const hash = getHashUrl();
|
||||
let dateNowMock;
|
||||
|
||||
function dataNowMockFn() {
|
||||
let start = 0;
|
||||
|
||||
const handler = () => {
|
||||
return (start += 1000);
|
||||
};
|
||||
|
||||
return jest.spyOn(Date, 'now').mockImplementation(handler);
|
||||
}
|
||||
|
||||
dateNowMock = dataNowMockFn();
|
||||
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo');
|
||||
const root = createDiv();
|
||||
mount(<h1 id={hash}>Hello</h1>, { attachTo: root });
|
||||
const wrapper = mount<Anchor>(
|
||||
<Anchor getContainer={() => document.body}>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
wrapper.instance().handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
|
||||
dateNowMock = dataNowMockFn();
|
||||
|
||||
wrapper.setProps({ offsetTop: 100 });
|
||||
wrapper.instance().handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
|
||||
dateNowMock = dataNowMockFn();
|
||||
|
||||
wrapper.setProps({ targetOffset: 200 });
|
||||
wrapper.instance().handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
|
||||
|
||||
dateNowMock.mockRestore();
|
||||
});
|
||||
});
|
||||
@@ -1,6 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { AntAnchor } from './Anchor';
|
||||
|
||||
const AnchorContext = React.createContext<AntAnchor>(null as any);
|
||||
|
||||
export default AnchorContext;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user