mirror of
https://github.com/ant-design/ant-design.git
synced 2025-07-26 00:26:53 +08:00
Merge pull request #23377 from ant-design/master
chore: Merge master into feature
This commit is contained in:
commit
fabec4831c
@ -6,6 +6,14 @@ const defaultVars = require('./scripts/default-vars');
|
|||||||
const darkVars = require('./scripts/dark-vars');
|
const darkVars = require('./scripts/dark-vars');
|
||||||
const compactVars = require('./scripts/compact-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
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
|
||||||
// We need compile additional content for antd user
|
// We need compile additional content for antd user
|
||||||
function finalizeCompile() {
|
function finalizeCompile() {
|
||||||
if (fs.existsSync(path.join(__dirname, './lib'))) {
|
if (fs.existsSync(path.join(__dirname, './lib'))) {
|
||||||
@ -61,11 +69,27 @@ function buildThemeFile(theme, vars) {
|
|||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(`Built a entry less file to dist/antd.${theme}.less`);
|
console.log(`Built a entry less file to dist/antd.${theme}.less`);
|
||||||
|
|
||||||
|
if (theme === 'default') {
|
||||||
|
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
|
// 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(
|
fs.writeFileSync(
|
||||||
path.join(process.cwd(), 'dist', `${theme}-theme.js`),
|
path.join(process.cwd(), 'dist', `${theme}-theme.js`),
|
||||||
`module.exports = ${JSON.stringify(vars, null, 2)};`,
|
generateThemeFileContent(theme),
|
||||||
);
|
);
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
@ -80,10 +104,47 @@ function finalizeDist() {
|
|||||||
'@import "../lib/style/index.less";\n@import "../lib/style/components.less";',
|
'@import "../lib/style/index.less";\n@import "../lib/style/components.less";',
|
||||||
);
|
);
|
||||||
// eslint-disable-next-line no-console
|
// 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');
|
console.log('Built a entry less file to dist/antd.less');
|
||||||
buildThemeFile('default', defaultVars);
|
buildThemeFile('default', defaultVars);
|
||||||
buildThemeFile('dark', darkVars);
|
buildThemeFile('dark', darkVars);
|
||||||
buildThemeFile('compact', compactVars);
|
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',
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,4 +155,6 @@ module.exports = {
|
|||||||
dist: {
|
dist: {
|
||||||
finalize: finalizeDist,
|
finalize: finalizeDist,
|
||||||
},
|
},
|
||||||
|
generateThemeFileContent,
|
||||||
};
|
};
|
||||||
|
finalizeDist();
|
||||||
|
11
.jest.js
11
.jest.js
@ -1,5 +1,3 @@
|
|||||||
const libDir = process.env.LIB_DIR;
|
|
||||||
|
|
||||||
const transformIgnorePatterns = [
|
const transformIgnorePatterns = [
|
||||||
'/dist/',
|
'/dist/',
|
||||||
// Ignore modules without es dir.
|
// Ignore modules without es dir.
|
||||||
@ -7,6 +5,13 @@ const transformIgnorePatterns = [
|
|||||||
'node_modules/(?!.*@babel)[^/]+?/(?!(es|node_modules)/)',
|
'node_modules/(?!.*@babel)[^/]+?/(?!(es|node_modules)/)',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function getTestRegex(libDir) {
|
||||||
|
if (libDir === 'dist') {
|
||||||
|
return 'demo\\.test\\.js$';
|
||||||
|
}
|
||||||
|
return '.*\\.test\\.(j|t)sx?$';
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
verbose: true,
|
verbose: true,
|
||||||
setupFiles: ['./tests/setup.js'],
|
setupFiles: ['./tests/setup.js'],
|
||||||
@ -28,7 +33,7 @@ module.exports = {
|
|||||||
'\\.md$': './node_modules/@ant-design/tools/lib/jest/demoPreprocessor',
|
'\\.md$': './node_modules/@ant-design/tools/lib/jest/demoPreprocessor',
|
||||||
'\\.(jpg|png|gif|svg)$': './node_modules/@ant-design/tools/lib/jest/imagePreprocessor',
|
'\\.(jpg|png|gif|svg)$': './node_modules/@ant-design/tools/lib/jest/imagePreprocessor',
|
||||||
},
|
},
|
||||||
testRegex: `${libDir === 'dist' ? 'demo' : '.*'}\\.test\\.js$`,
|
testRegex: getTestRegex(process.env.LIB_DIR),
|
||||||
collectCoverageFrom: [
|
collectCoverageFrom: [
|
||||||
'components/**/*.{ts,tsx}',
|
'components/**/*.{ts,tsx}',
|
||||||
'!components/*/style/index.tsx',
|
'!components/*/style/index.tsx',
|
||||||
|
@ -12,7 +12,7 @@ module.exports = {
|
|||||||
'\\.md$': './node_modules/@ant-design/tools/lib/jest/demoPreprocessor',
|
'\\.md$': './node_modules/@ant-design/tools/lib/jest/demoPreprocessor',
|
||||||
'\\.(jpg|png|gif|svg)$': './node_modules/@ant-design/tools/lib/jest/imagePreprocessor',
|
'\\.(jpg|png|gif|svg)$': './node_modules/@ant-design/tools/lib/jest/imagePreprocessor',
|
||||||
},
|
},
|
||||||
testRegex: 'demo\\.test\\.js$',
|
testRegex: 'demo\\.test\\.(j|t)s$',
|
||||||
testEnvironment: 'node',
|
testEnvironment: 'node',
|
||||||
transformIgnorePatterns,
|
transformIgnorePatterns,
|
||||||
snapshotSerializers: ['enzyme-to-json/serializer'],
|
snapshotSerializers: ['enzyme-to-json/serializer'],
|
||||||
|
@ -15,8 +15,58 @@ timeline: true
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 4.1.4
|
||||||
|
|
||||||
|
`2020-04-18`
|
||||||
|
|
||||||
|
- 🐞 Fix dark theme and compact theme not working. [#23243](https://github.com/ant-design/ant-design/pull/23243)
|
||||||
|
- 🐞 Fix Modal.info executed only once when has argument. [#23360](https://github.com/ant-design/ant-design/pull/23360)
|
||||||
|
- 🐞 Fix Dropdown submenu background missing. [#23296](https://github.com/ant-design/ant-design/pull/23296)
|
||||||
|
- 💄 Optimize PageHeader responsive behavior. [#23277](https://github.com/ant-design/ant-design/pull/23277)
|
||||||
|
- 🐞 Fix TreeSelect render blank in compact mode. [#23231](https://github.com/ant-design/ant-design/pull/23231)
|
||||||
|
- 🛎 Fix Checkbox and Switch console warning typo (validate -> a valid). [#23240](https://github.com/ant-design/ant-design/pull/23240) [@evancharlton](https://github.com/evancharlton)
|
||||||
|
- 🐞 Fix Table `rowSelection` params issue when `childrenColumnName` configured. [#23205](https://github.com/ant-design/ant-design/pull/23205)
|
||||||
|
- Input
|
||||||
|
- 🐞 Fix Input `type="color"` height issue. [#23351](https://github.com/ant-design/ant-design/pull/23351)
|
||||||
|
- 🐞 Fix Input width shaking when trigger clear icon. [#23259](https://github.com/ant-design/ant-design/pull/23259)
|
||||||
|
- 🐞 Fix Input.Search `size` not affected by ConfigProvider `componentSize`. [#23331](https://github.com/ant-design/ant-design/pull/23331)
|
||||||
|
- Select
|
||||||
|
- 🐞 Fix multiple Select show remove icon when `disabled`. [#23295](https://github.com/ant-design/ant-design/pull/23295)
|
||||||
|
- 🐞 Fix Select custom `suffixIcon` cannot be access. [#23274](https://github.com/ant-design/ant-design/pull/23274)
|
||||||
|
- 🐞 Fix Select search input caret missing in Collapse. [#23250](https://github.com/ant-design/ant-design/pull/23250)
|
||||||
|
- Globalization
|
||||||
|
- 🇨🇳 Form validation messages support internalization and add zh_CN locale. [#23165](https://github.com/ant-design/ant-design/pull/23165) [@hengkx](https://github.com/hengkx)
|
||||||
|
- 🌐 Add missing translations in he_IL. [#23302](https://github.com/ant-design/ant-design/pull/23302) [@MishaKav](https://github.com/MishaKav)
|
||||||
|
- 🌐 Add missing translations in ru_RU. [#23303](https://github.com/ant-design/ant-design/pull/23303) [@MishaKav](https://github.com/MishaKav)
|
||||||
|
- TypeScript
|
||||||
|
- 🔷 Form.Item type upgrade. [#22962](https://github.com/ant-design/ant-design/pull/22962) [@fa93hws](https://github.com/fa93hws)
|
||||||
|
- 🔷 Tree type upgrade. [#23348](https://github.com/ant-design/ant-design/pull/23348) [@yoyo837](https://github.com/yoyo837)
|
||||||
|
- 🐞 Pass `popupClassName` prop to `rc-picker`. [#23214](https://github.com/ant-design/ant-design/pull/23214) [@tanmoyopenroot](https://github.com/tanmoyopenroot)
|
||||||
|
- RTL
|
||||||
|
- 💄 Fix Select RTL style. [#23235](https://github.com/ant-design/ant-design/pull/23235)
|
||||||
|
- 💄 Fix Menu RTL style. [#23319](https://github.com/ant-design/ant-design/pull/23319)
|
||||||
|
|
||||||
|
## 4.1.3
|
||||||
|
|
||||||
|
`2020-04-13`
|
||||||
|
|
||||||
|
- 💄 Adjust Form.Item `label` height style in vertical layout. [#23192](https://github.com/ant-design/ant-design/pull/23192)
|
||||||
|
- 🐞 Fix `Variable is undefined` when importing dark or compact theme and provide a `getThemeVariables` methold for getting theme variables easily. [#23171](https://github.com/ant-design/ant-design/pull/23171)
|
||||||
|
- 🐞 Fix PageHeader style breaks when `title` is too long and improve it's responsive design. [#23133](https://github.com/ant-design/ant-design/pull/23133)
|
||||||
|
- Tabs
|
||||||
|
- 🐞 Fix Tabs `@tabs-card-height` less variable not working. [#23168](https://github.com/ant-design/ant-design/pull/23168)
|
||||||
|
- 🐞 Fix Tabs cannot be displayed in Safari 13. [#23151](https://github.com/ant-design/ant-design/pull/23151) [@imhxc](https://github.com/imhxc)
|
||||||
|
- Table
|
||||||
|
- 🐞 Fix Table fixed columns cannot pin in Safari 12. [#23161](https://github.com/ant-design/ant-design/pull/23161)
|
||||||
|
- 🐞 Fix Table `summary` padding in small size. [#23140](https://github.com/ant-design/ant-design/pull/23140) [@someyoungideas](https://github.com/someyoungideas)
|
||||||
|
- 🐞 Fix Select align style with different size. [#23160](https://github.com/ant-design/ant-design/pull/23160)
|
||||||
|
- 🐞 Fix RangePicker under Input.Group style issue. [#23149](https://github.com/ant-design/ant-design/pull/23149)
|
||||||
|
- 🐞 Fix Pagination missing TypeScript definition of `showTitle`. [#23144](https://github.com/ant-design/ant-design/pull/23144) [@DongchengWang](https://github.com/DongchengWang)
|
||||||
|
|
||||||
## 4.1.2
|
## 4.1.2
|
||||||
|
|
||||||
|
`2020-04-10`
|
||||||
|
|
||||||
- Menu
|
- Menu
|
||||||
- 🐞 Fix Menu SubMenu background in dark mode. [#22981](https://github.com/ant-design/ant-design/pull/22981) [@AshoneA](https://github.com/AshoneA)
|
- 🐞 Fix Menu SubMenu background in dark mode. [#22981](https://github.com/ant-design/ant-design/pull/22981) [@AshoneA](https://github.com/AshoneA)
|
||||||
- 🐞 Fix long SubMenu title being overlayed by arrow icon. [#23028](https://github.com/ant-design/ant-design/pull/23028) [@wwyx778](https://github.com/wwyx778)
|
- 🐞 Fix long SubMenu title being overlayed by arrow icon. [#23028](https://github.com/ant-design/ant-design/pull/23028) [@wwyx778](https://github.com/wwyx778)
|
||||||
@ -52,6 +102,8 @@ timeline: true
|
|||||||
|
|
||||||
## 4.1.1
|
## 4.1.1
|
||||||
|
|
||||||
|
`2020-04-05`
|
||||||
|
|
||||||
- 🐞 Fix Tabs panel focus outline style. [#22752](https://github.com/ant-design/ant-design/pull/22752) [@MrHeer](https://github.com/MrHeer)
|
- 🐞 Fix Tabs panel focus outline style. [#22752](https://github.com/ant-design/ant-design/pull/22752) [@MrHeer](https://github.com/MrHeer)
|
||||||
- 🐞 Fix Input affix with popup element can not get click focus. [#22887](https://github.com/ant-design/ant-design/pull/22887)
|
- 🐞 Fix Input affix with popup element can not get click focus. [#22887](https://github.com/ant-design/ant-design/pull/22887)
|
||||||
- Table
|
- Table
|
||||||
|
@ -15,8 +15,58 @@ timeline: true
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 4.1.4
|
||||||
|
|
||||||
|
`2020-04-18`
|
||||||
|
|
||||||
|
- 🐞 修复暗黑主题和紧凑主题不生效的问题。[#23243](https://github.com/ant-design/ant-design/pull/23243)
|
||||||
|
- 🐞 修复 Modal.info 等方法的 `onOk` 函数有参数时只触发一次的问题。[#23360](https://github.com/ant-design/ant-design/pull/23360)
|
||||||
|
- 🐞 修复 Dropdown 弹出菜单背景样式问题。[#23296](https://github.com/ant-design/ant-design/pull/23296)
|
||||||
|
- 💄 优化 PageHeader 的响应式表现。[#23277](https://github.com/ant-design/ant-design/pull/23277)
|
||||||
|
- 🐞 修复紧凑模式下树选择出现空白。[#23231](https://github.com/ant-design/ant-design/pull/23231)
|
||||||
|
- 🛎 修改 Checkbox 和 Switch 中控制台输出的错别字 (validate -> a valid)。[#23240](https://github.com/ant-design/ant-design/pull/23240) [@evancharlton](https://github.com/evancharlton)
|
||||||
|
- 🐞 修复 Table `rowSelection` 在设置 `childrenColumnName` 时事件参数不正确的问题。[#23205](https://github.com/ant-design/ant-design/pull/23205)
|
||||||
|
- Input
|
||||||
|
- 🐞 修复 Input `type="color"` 的高度问题。[#23351](https://github.com/ant-design/ant-design/pull/23351)
|
||||||
|
- 🐞 修复 Input 设置 `allowClear` 内联展示时,触发清除按钮样式抖动的问题。[#23259](https://github.com/ant-design/ant-design/pull/23259)
|
||||||
|
- 🐞 修复 Input.Search 全局设置 `size` 不生效问题。[#23331](https://github.com/ant-design/ant-design/pull/23331)
|
||||||
|
- Select
|
||||||
|
- 🐞 修复 Select 多选时设置 `disabled` 选项仍然会展示移除按钮的问题。[#23295](https://github.com/ant-design/ant-design/pull/23295)
|
||||||
|
- 🐞 修复 Select 自定义 `suffixIcon` 无法交互的问题。[#23274](https://github.com/ant-design/ant-design/pull/23274)
|
||||||
|
- 🐞 修复 Select 输入光标在 Collapse 内不显示的问题。[#23250](https://github.com/ant-design/ant-design/pull/23250)
|
||||||
|
- 国际化
|
||||||
|
- 🌐 Form 校验信息支持国际化并增加中文文案。[#23165](https://github.com/ant-design/ant-design/pull/23165) [@hengkx](https://github.com/hengkx)
|
||||||
|
- 🌐 完善希伯来语(以色列) 国际化。[#23302](https://github.com/ant-design/ant-design/pull/23302) [@MishaKav](https://github.com/MishaKav)
|
||||||
|
- 🌐 完善俄语国际化。[#23303](https://github.com/ant-design/ant-design/pull/23303) [@MishaKav](https://github.com/MishaKav)
|
||||||
|
- TypeScript
|
||||||
|
- 🔷 更新 Tree 的类型定义。[#23348](https://github.com/ant-design/ant-design/pull/23348) [@yoyo837](https://github.com/yoyo837)
|
||||||
|
- 🔷 更新 Form Item 的类型定义。[#22962](https://github.com/ant-design/ant-design/pull/22962) [@fa93hws](https://github.com/fa93hws)
|
||||||
|
- 🐞 修复 Slider 组件 `value` 和 `defaultValue` 文档与 TypeScript 定义不一致的问题。[#23252](https://github.com/ant-design/ant-design/pull/23252) [@DongchengWang](https://github.com/DongchengWang)
|
||||||
|
- RTL
|
||||||
|
- 🐞 修复 Menu RTL 样式。[#23319](https://github.com/ant-design/ant-design/pull/23319)
|
||||||
|
- 💄 修复 Select 的 RTL 样式。[#23235](https://github.com/ant-design/ant-design/pull/23235)
|
||||||
|
|
||||||
|
## 4.1.3
|
||||||
|
|
||||||
|
`2020-04-13`
|
||||||
|
|
||||||
|
- 💄 调整 Form.Item `label` 在垂直布局下的高度样式。[#23192](https://github.com/ant-design/ant-design/pull/23192)
|
||||||
|
- 🐞 修复引用暗黑或紧凑主题时提示 `Variable is undefined` 的问题,并提供 `getThemeVariables` 方便获取对应主题变量。[#23171](https://github.com/ant-design/ant-design/pull/23171)
|
||||||
|
- 🐞 修复 PageHeader `title` 超长时布局被破坏的问题并优化响应式表现。[#23133](https://github.com/ant-design/ant-design/pull/23133)
|
||||||
|
- Tabs
|
||||||
|
- 🐞 修复 Tabs `@tabs-card-height` less 变量无效的问题。[#23168](https://github.com/ant-design/ant-design/pull/23168)
|
||||||
|
- 🐞 修复 Tabs 在 Safair 浏览器下无法显示的问题。[#23151](https://github.com/ant-design/ant-design/pull/23151) [@imhxc](https://github.com/imhxc)
|
||||||
|
- Table
|
||||||
|
- 🐞 修复 Table 固定列在 Safari 12 中不能固定的问题。[#23161](https://github.com/ant-design/ant-design/pull/23161)
|
||||||
|
- 🐞 修复 Table `summary` 在小尺寸下的内边距样式。[#23140](https://github.com/ant-design/ant-design/pull/23140) [@someyoungideas](https://github.com/someyoungideas)
|
||||||
|
- 🐞 修复 Select 不同尺寸下的对齐样式问题。[#23160](https://github.com/ant-design/ant-design/pull/23160)
|
||||||
|
- 🐞 修复 RangePicker 在 Input.Group 内的样式问题。[#23149](https://github.com/ant-design/ant-design/pull/23149)
|
||||||
|
- 🐞 修复 Pagination 缺少 `showTitle` TypeScript 定义的问题。[#23144](https://github.com/ant-design/ant-design/pull/23144) [@DongchengWang](https://github.com/DongchengWang)
|
||||||
|
|
||||||
## 4.1.2
|
## 4.1.2
|
||||||
|
|
||||||
|
`2020-04-10`
|
||||||
|
|
||||||
- Menu
|
- Menu
|
||||||
- 🐞 修复暗色 Menu 弹出菜单背景色为白色的问题。[#22981](https://github.com/ant-design/ant-design/pull/22981) [@AshoneA](https://github.com/AshoneA)
|
- 🐞 修复暗色 Menu 弹出菜单背景色为白色的问题。[#22981](https://github.com/ant-design/ant-design/pull/22981) [@AshoneA](https://github.com/AshoneA)
|
||||||
- 🐞 修复 SubMenu 标题过长而导致被箭头图标部分覆盖的问题。[#23028](https://github.com/ant-design/ant-design/pull/23028) [@wwyx778](https://github.com/wwyx778)
|
- 🐞 修复 SubMenu 标题过长而导致被箭头图标部分覆盖的问题。[#23028](https://github.com/ant-design/ant-design/pull/23028) [@wwyx778](https://github.com/wwyx778)
|
||||||
@ -52,6 +102,8 @@ timeline: true
|
|||||||
|
|
||||||
## 4.1.1
|
## 4.1.1
|
||||||
|
|
||||||
|
`2020-04-05`
|
||||||
|
|
||||||
- 🐞 移除 Tabs 的内容区域的 focus 蓝色轮廓线。[#22752](https://github.com/ant-design/ant-design/pull/22752) [@MrHeer](https://github.com/MrHeer)
|
- 🐞 移除 Tabs 的内容区域的 focus 蓝色轮廓线。[#22752](https://github.com/ant-design/ant-design/pull/22752) [@MrHeer](https://github.com/MrHeer)
|
||||||
- 🐞 修复 Input 前后缀添加弹出元素不能点击获得焦点的问题。[#22887](https://github.com/ant-design/ant-design/pull/22887)
|
- 🐞 修复 Input 前后缀添加弹出元素不能点击获得焦点的问题。[#22887](https://github.com/ant-design/ant-design/pull/22887)
|
||||||
- Table
|
- Table
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
const __NULL__ = { notExist: true };
|
const __NULL__ = { notExist: true };
|
||||||
|
|
||||||
export function spyElementPrototypes(Element, properties) {
|
type ElementType<P> = {
|
||||||
|
prototype: P;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function spyElementPrototypes<P extends {}>(Element: ElementType<P>, properties: P) {
|
||||||
const propNames = Object.keys(properties);
|
const propNames = Object.keys(properties);
|
||||||
const originDescriptors = {};
|
const originDescriptors = {};
|
||||||
|
|
||||||
@ -51,7 +55,16 @@ export function spyElementPrototypes(Element, properties) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function spyElementPrototype(Element, propName, property) {
|
type FunctionPropertyNames<T> = {
|
||||||
|
[K in keyof T]: T[K] extends (...args: any[]) => any ? K : never;
|
||||||
|
}[keyof T] &
|
||||||
|
string;
|
||||||
|
|
||||||
|
export function spyElementPrototype<P extends {}, K extends FunctionPropertyNames<P>>(
|
||||||
|
Element: ElementType<P>,
|
||||||
|
propName: K,
|
||||||
|
property: P[K],
|
||||||
|
) {
|
||||||
return spyElementPrototypes(Element, {
|
return spyElementPrototypes(Element, {
|
||||||
[propName]: property,
|
[propName]: property,
|
||||||
});
|
});
|
@ -7,9 +7,17 @@ import { spyElementPrototype } from '../../__tests__/util/domHook';
|
|||||||
import rtlTest from '../../../tests/shared/rtlTest';
|
import rtlTest from '../../../tests/shared/rtlTest';
|
||||||
import { sleep } from '../../../tests/utils';
|
import { sleep } from '../../../tests/utils';
|
||||||
|
|
||||||
const events = {};
|
const events: any = {};
|
||||||
|
|
||||||
|
class AffixMounter extends React.Component<{
|
||||||
|
offsetBottom?: number;
|
||||||
|
offsetTop?: number;
|
||||||
|
onTestUpdatePosition?(): void;
|
||||||
|
}> {
|
||||||
|
private container: HTMLDivElement;
|
||||||
|
|
||||||
|
private affix: Affix;
|
||||||
|
|
||||||
class AffixMounter extends React.Component {
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.container.addEventListener = jest.fn().mockImplementation((event, cb) => {
|
this.container.addEventListener = jest.fn().mockImplementation((event, cb) => {
|
||||||
events[event] = cb;
|
events[event] = cb;
|
||||||
@ -47,7 +55,7 @@ describe('Affix Render', () => {
|
|||||||
let wrapper;
|
let wrapper;
|
||||||
let domMock;
|
let domMock;
|
||||||
|
|
||||||
const classRect = {
|
const classRect: any = {
|
||||||
container: {
|
container: {
|
||||||
top: 0,
|
top: 0,
|
||||||
bottom: 100,
|
bottom: 100,
|
||||||
@ -135,9 +143,9 @@ describe('Affix Render', () => {
|
|||||||
describe('updatePosition when target changed', () => {
|
describe('updatePosition when target changed', () => {
|
||||||
it('function change', () => {
|
it('function change', () => {
|
||||||
document.body.innerHTML = '<div id="mounter" />';
|
document.body.innerHTML = '<div id="mounter" />';
|
||||||
const container = document.querySelector('#id');
|
const container = document.querySelector('#id') as HTMLDivElement;
|
||||||
const getTarget = () => container;
|
const getTarget = () => container;
|
||||||
wrapper = mount(<Affix target={getTarget} />);
|
wrapper = mount(<Affix target={getTarget}>{null}</Affix>);
|
||||||
wrapper.setProps({ target: null });
|
wrapper.setProps({ target: null });
|
||||||
expect(wrapper.instance().state.status).toBe(0);
|
expect(wrapper.instance().state.status).toBe(0);
|
||||||
expect(wrapper.instance().state.affixStyle).toBe(undefined);
|
expect(wrapper.instance().state.affixStyle).toBe(undefined);
|
||||||
@ -153,7 +161,7 @@ describe('Affix Render', () => {
|
|||||||
|
|
||||||
const originLength = getObserverLength();
|
const originLength = getObserverLength();
|
||||||
const getTarget = () => target;
|
const getTarget = () => target;
|
||||||
wrapper = mount(<Affix target={getTarget} />);
|
wrapper = mount(<Affix target={getTarget}>{null}</Affix>);
|
||||||
await sleep(50);
|
await sleep(50);
|
||||||
|
|
||||||
expect(getObserverLength()).toBe(originLength + 1);
|
expect(getObserverLength()).toBe(originLength + 1);
|
@ -32,7 +32,7 @@ export interface AffixProps {
|
|||||||
target?: () => Window | HTMLElement | null;
|
target?: () => Window | HTMLElement | null;
|
||||||
prefixCls?: string;
|
prefixCls?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
children: React.ReactElement;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AffixStatus {
|
enum AffixStatus {
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { mount, render } from 'enzyme';
|
import { mount, render } from 'enzyme';
|
||||||
import renderer from 'react-test-renderer';
|
|
||||||
import { SearchOutlined } from '@ant-design/icons';
|
import { SearchOutlined } from '@ant-design/icons';
|
||||||
import Button from '..';
|
import Button from '..';
|
||||||
import ConfigProvider from '../../config-provider';
|
import ConfigProvider from '../../config-provider';
|
||||||
import mountTest from '../../../tests/shared/mountTest';
|
import mountTest from '../../../tests/shared/mountTest';
|
||||||
import rtlTest from '../../../tests/shared/rtlTest';
|
import rtlTest from '../../../tests/shared/rtlTest';
|
||||||
import { sleep } from '../../../tests/utils';
|
import { sleep } from '../../../tests/utils';
|
||||||
|
import { SizeType } from '../../config-provider/SizeContext';
|
||||||
|
|
||||||
describe('Button', () => {
|
describe('Button', () => {
|
||||||
mountTest(Button);
|
mountTest(Button);
|
||||||
@ -30,13 +30,14 @@ describe('Button', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('mount correctly', () => {
|
it('mount correctly', () => {
|
||||||
expect(() => renderer.create(<Button>Follow</Button>)).not.toThrow();
|
expect(() => mount(<Button>Follow</Button>)).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('warns if size is wrong', () => {
|
it('warns if size is wrong', () => {
|
||||||
const mockWarn = jest.fn();
|
const mockWarn = jest.fn();
|
||||||
jest.spyOn(console, 'warn').mockImplementation(mockWarn);
|
jest.spyOn(console, 'warn').mockImplementation(mockWarn);
|
||||||
render(<Button.Group size="who am I" />);
|
const size = ('who am I' as any) as SizeType;
|
||||||
|
render(<Button.Group size={size} />);
|
||||||
expect(mockWarn).toHaveBeenCalledTimes(1);
|
expect(mockWarn).toHaveBeenCalledTimes(1);
|
||||||
expect(mockWarn.mock.calls[0][0]).toMatchObject({
|
expect(mockWarn.mock.calls[0][0]).toMatchObject({
|
||||||
message: 'unreachable case: "who am I"',
|
message: 'unreachable case: "who am I"',
|
||||||
@ -74,7 +75,7 @@ describe('Button', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('renders Chinese characters correctly in HOC', () => {
|
it('renders Chinese characters correctly in HOC', () => {
|
||||||
const Text = ({ children }) => <span>{children}</span>;
|
const Text = ({ children }: { children: React.ReactNode }) => <span>{children}</span>;
|
||||||
const wrapper = mount(
|
const wrapper = mount(
|
||||||
<Button>
|
<Button>
|
||||||
<Text>按钮</Text>
|
<Text>按钮</Text>
|
||||||
@ -110,7 +111,7 @@ describe('Button', () => {
|
|||||||
|
|
||||||
it('have static property for type detecting', () => {
|
it('have static property for type detecting', () => {
|
||||||
const wrapper = mount(<Button>Button Text</Button>);
|
const wrapper = mount(<Button>Button Text</Button>);
|
||||||
expect(wrapper.type().__ANT_BUTTON).toBe(true);
|
expect((wrapper.type() as any).__ANT_BUTTON).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should change loading state instantly by default', () => {
|
it('should change loading state instantly by default', () => {
|
||||||
@ -189,7 +190,7 @@ describe('Button', () => {
|
|||||||
|
|
||||||
it('should has click wave effect', async () => {
|
it('should has click wave effect', async () => {
|
||||||
const wrapper = mount(<Button type="primary">button</Button>);
|
const wrapper = mount(<Button type="primary">button</Button>);
|
||||||
wrapper.find('.ant-btn').getDOMNode().click();
|
wrapper.find('.ant-btn').getDOMNode<HTMLButtonElement>().click();
|
||||||
await new Promise(resolve => setTimeout(resolve, 0));
|
await new Promise(resolve => setTimeout(resolve, 0));
|
||||||
expect(wrapper.render()).toMatchSnapshot();
|
expect(wrapper.render()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
@ -262,6 +263,7 @@ describe('Button', () => {
|
|||||||
throw new Error('Should not called!!!');
|
throw new Error('Should not called!!!');
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
wrapper.find('Button').instance().forceUpdate();
|
|
||||||
|
expect(wrapper.find('Button').instance()).toBe(null);
|
||||||
});
|
});
|
||||||
});
|
});
|
@ -4,7 +4,7 @@ import classNames from 'classnames';
|
|||||||
import omit from 'omit.js';
|
import omit from 'omit.js';
|
||||||
|
|
||||||
import Group from './button-group';
|
import Group from './button-group';
|
||||||
import { ConfigContext, ConfigConsumerProps } from '../config-provider';
|
import { ConfigContext } from '../config-provider';
|
||||||
import Wave from '../_util/wave';
|
import Wave from '../_util/wave';
|
||||||
import { Omit, tuple } from '../_util/type';
|
import { Omit, tuple } from '../_util/type';
|
||||||
import warning from '../_util/warning';
|
import warning from '../_util/warning';
|
||||||
@ -104,72 +104,57 @@ export type NativeButtonProps = {
|
|||||||
|
|
||||||
export type ButtonProps = Partial<AnchorButtonProps & NativeButtonProps>;
|
export type ButtonProps = Partial<AnchorButtonProps & NativeButtonProps>;
|
||||||
|
|
||||||
interface ButtonState {
|
interface ButtonTypeProps extends React.FC<ButtonProps> {
|
||||||
loading?: boolean | { delay?: number };
|
Group: typeof Group;
|
||||||
hasTwoCNChar: boolean;
|
__ANT_BUTTON: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Button extends React.Component<ButtonProps, ButtonState> {
|
const Button: ButtonTypeProps = ({ ...props }) => {
|
||||||
static Group: typeof Group;
|
const [loading, setLoading] = React.useState(props.loading);
|
||||||
|
const [hasTwoCNChar, setHasTwoCNChar] = React.useState(false);
|
||||||
|
const { getPrefixCls, autoInsertSpaceInButton, direction } = React.useContext(ConfigContext);
|
||||||
|
const buttonRef = React.createRef<HTMLButtonElement>();
|
||||||
|
let delayTimeout: number;
|
||||||
|
|
||||||
static __ANT_BUTTON = true;
|
const isNeedInserted = () => {
|
||||||
|
const { icon, children, type } = props;
|
||||||
static contextType = ConfigContext;
|
return React.Children.count(children) === 1 && !icon && type !== 'link';
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
loading: false,
|
|
||||||
ghost: false,
|
|
||||||
block: false,
|
|
||||||
htmlType: 'button' as ButtonProps['htmlType'],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private delayTimeout: number;
|
const fixTwoCNChar = () => {
|
||||||
|
// Fix for HOC usage like <FormatMessage />
|
||||||
private buttonNode: HTMLElement | null;
|
if (!buttonRef || !buttonRef.current || autoInsertSpaceInButton === false) {
|
||||||
|
return;
|
||||||
constructor(props: ButtonProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
loading: props.loading,
|
|
||||||
hasTwoCNChar: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.fixTwoCNChar();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps: ButtonProps) {
|
|
||||||
this.fixTwoCNChar();
|
|
||||||
|
|
||||||
if (prevProps.loading && typeof prevProps.loading !== 'boolean') {
|
|
||||||
clearTimeout(this.delayTimeout);
|
|
||||||
}
|
}
|
||||||
|
const buttonText = buttonRef.current.textContent;
|
||||||
const { loading } = this.props;
|
if (isNeedInserted() && isTwoCNChar(buttonText)) {
|
||||||
if (loading && typeof loading !== 'boolean' && loading.delay) {
|
if (!hasTwoCNChar) {
|
||||||
this.delayTimeout = window.setTimeout(() => {
|
setHasTwoCNChar(true);
|
||||||
this.setState({ loading });
|
}
|
||||||
}, loading.delay);
|
} else if (hasTwoCNChar) {
|
||||||
} else if (prevProps.loading !== loading) {
|
setHasTwoCNChar(false);
|
||||||
// eslint-disable-next-line react/no-did-update-set-state
|
|
||||||
this.setState({ loading });
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
if (this.delayTimeout) {
|
|
||||||
clearTimeout(this.delayTimeout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
saveButtonRef = (node: HTMLElement | null) => {
|
|
||||||
this.buttonNode = node;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handleClick: React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement> = e => {
|
React.useEffect(() => {
|
||||||
const { loading } = this.state;
|
if (props.loading && typeof props.loading !== 'boolean') {
|
||||||
const { onClick } = this.props;
|
clearTimeout(delayTimeout);
|
||||||
|
}
|
||||||
|
if (props.loading && typeof props.loading !== 'boolean' && props.loading.delay) {
|
||||||
|
delayTimeout = window.setTimeout(() => {
|
||||||
|
setLoading(props.loading);
|
||||||
|
}, props.loading.delay);
|
||||||
|
} else if (props.loading !== loading) {
|
||||||
|
setLoading(props.loading);
|
||||||
|
}
|
||||||
|
}, [props.loading]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fixTwoCNChar();
|
||||||
|
}, [buttonRef]);
|
||||||
|
|
||||||
|
const handleClick = (e: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement, MouseEvent>) => {
|
||||||
|
const { onClick } = props;
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -178,143 +163,117 @@ class Button extends React.Component<ButtonProps, ButtonState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fixTwoCNChar() {
|
return (
|
||||||
const { autoInsertSpaceInButton }: ConfigConsumerProps = this.context;
|
<SizeContext.Consumer>
|
||||||
|
{size => {
|
||||||
|
const {
|
||||||
|
prefixCls: customizePrefixCls,
|
||||||
|
type,
|
||||||
|
danger,
|
||||||
|
shape,
|
||||||
|
size: customizeSize,
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
icon,
|
||||||
|
ghost,
|
||||||
|
block,
|
||||||
|
...rest
|
||||||
|
} = props;
|
||||||
|
|
||||||
// Fix for HOC usage like <FormatMessage />
|
warning(
|
||||||
if (!this.buttonNode || autoInsertSpaceInButton === false) {
|
!(typeof icon === 'string' && icon.length > 2),
|
||||||
return;
|
'Button',
|
||||||
}
|
`\`icon\` is using ReactNode instead of string naming in v4. Please check \`${icon}\` at https://ant.design/components/icon`,
|
||||||
const buttonText = this.buttonNode.textContent;
|
);
|
||||||
if (this.isNeedInserted() && isTwoCNChar(buttonText)) {
|
|
||||||
if (!this.state.hasTwoCNChar) {
|
const prefixCls = getPrefixCls('btn', customizePrefixCls);
|
||||||
this.setState({
|
const autoInsertSpace = autoInsertSpaceInButton !== false;
|
||||||
hasTwoCNChar: true,
|
|
||||||
|
// large => lg
|
||||||
|
// small => sm
|
||||||
|
let sizeCls = '';
|
||||||
|
switch (customizeSize || size) {
|
||||||
|
case 'large':
|
||||||
|
sizeCls = 'lg';
|
||||||
|
break;
|
||||||
|
case 'small':
|
||||||
|
sizeCls = 'sm';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const iconType = loading ? 'loading' : icon;
|
||||||
|
|
||||||
|
const classes = classNames(prefixCls, className, {
|
||||||
|
[`${prefixCls}-${type}`]: type,
|
||||||
|
[`${prefixCls}-${shape}`]: shape,
|
||||||
|
[`${prefixCls}-${sizeCls}`]: sizeCls,
|
||||||
|
[`${prefixCls}-icon-only`]: !children && children !== 0 && iconType,
|
||||||
|
[`${prefixCls}-background-ghost`]: ghost,
|
||||||
|
[`${prefixCls}-loading`]: loading,
|
||||||
|
[`${prefixCls}-two-chinese-chars`]: hasTwoCNChar && autoInsertSpace,
|
||||||
|
[`${prefixCls}-block`]: block,
|
||||||
|
[`${prefixCls}-dangerous`]: !!danger,
|
||||||
|
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||||
});
|
});
|
||||||
}
|
|
||||||
} else if (this.state.hasTwoCNChar) {
|
|
||||||
this.setState({
|
|
||||||
hasTwoCNChar: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isNeedInserted() {
|
const iconNode =
|
||||||
const { icon, children, type } = this.props;
|
icon && !loading ? (
|
||||||
return React.Children.count(children) === 1 && !icon && type !== 'link';
|
icon
|
||||||
}
|
) : (
|
||||||
|
<LoadingIcon existIcon={!!icon} prefixCls={prefixCls} loading={loading} />
|
||||||
render() {
|
|
||||||
const { getPrefixCls, autoInsertSpaceInButton, direction }: ConfigConsumerProps = this.context;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SizeContext.Consumer>
|
|
||||||
{size => {
|
|
||||||
const {
|
|
||||||
prefixCls: customizePrefixCls,
|
|
||||||
type,
|
|
||||||
danger,
|
|
||||||
shape,
|
|
||||||
size: customizeSize,
|
|
||||||
className,
|
|
||||||
children,
|
|
||||||
icon,
|
|
||||||
ghost,
|
|
||||||
block,
|
|
||||||
...rest
|
|
||||||
} = this.props;
|
|
||||||
const { loading, hasTwoCNChar } = this.state;
|
|
||||||
|
|
||||||
warning(
|
|
||||||
!(typeof icon === 'string' && icon.length > 2),
|
|
||||||
'Button',
|
|
||||||
`\`icon\` is using ReactNode instead of string naming in v4. Please check \`${icon}\` at https://ant.design/components/icon`,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const prefixCls = getPrefixCls('btn', customizePrefixCls);
|
const kids =
|
||||||
const autoInsertSpace = autoInsertSpaceInButton !== false;
|
children || children === 0
|
||||||
|
? spaceChildren(children, isNeedInserted() && autoInsertSpace)
|
||||||
|
: null;
|
||||||
|
|
||||||
// large => lg
|
const linkButtonRestProps = omit(rest as AnchorButtonProps, ['htmlType', 'loading']);
|
||||||
// small => sm
|
if (linkButtonRestProps.href !== undefined) {
|
||||||
let sizeCls = '';
|
return (
|
||||||
switch (customizeSize || size) {
|
<a {...linkButtonRestProps} className={classes} onClick={handleClick} ref={buttonRef}>
|
||||||
case 'large':
|
|
||||||
sizeCls = 'lg';
|
|
||||||
break;
|
|
||||||
case 'small':
|
|
||||||
sizeCls = 'sm';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const iconType = loading ? 'loading' : icon;
|
|
||||||
|
|
||||||
const classes = classNames(prefixCls, className, {
|
|
||||||
[`${prefixCls}-${type}`]: type,
|
|
||||||
[`${prefixCls}-${shape}`]: shape,
|
|
||||||
[`${prefixCls}-${sizeCls}`]: sizeCls,
|
|
||||||
[`${prefixCls}-icon-only`]: !children && children !== 0 && iconType,
|
|
||||||
[`${prefixCls}-background-ghost`]: ghost,
|
|
||||||
[`${prefixCls}-loading`]: loading,
|
|
||||||
[`${prefixCls}-two-chinese-chars`]: hasTwoCNChar && autoInsertSpace,
|
|
||||||
[`${prefixCls}-block`]: block,
|
|
||||||
[`${prefixCls}-dangerous`]: !!danger,
|
|
||||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
|
||||||
});
|
|
||||||
|
|
||||||
const iconNode =
|
|
||||||
icon && !loading ? (
|
|
||||||
icon
|
|
||||||
) : (
|
|
||||||
<LoadingIcon existIcon={!!icon} prefixCls={prefixCls} loading={loading} />
|
|
||||||
);
|
|
||||||
|
|
||||||
const kids =
|
|
||||||
children || children === 0
|
|
||||||
? spaceChildren(children, this.isNeedInserted() && autoInsertSpace)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const linkButtonRestProps = omit(rest as AnchorButtonProps, ['htmlType', 'loading']);
|
|
||||||
if (linkButtonRestProps.href !== undefined) {
|
|
||||||
return (
|
|
||||||
<a
|
|
||||||
{...linkButtonRestProps}
|
|
||||||
className={classes}
|
|
||||||
onClick={this.handleClick}
|
|
||||||
ref={this.saveButtonRef}
|
|
||||||
>
|
|
||||||
{iconNode}
|
|
||||||
{kids}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// React does not recognize the `htmlType` prop on a DOM element. Here we pick it out of `rest`.
|
|
||||||
const { htmlType, ...otherProps } = rest as NativeButtonProps;
|
|
||||||
|
|
||||||
const buttonNode = (
|
|
||||||
<button
|
|
||||||
{...(omit(otherProps, ['loading']) as NativeButtonProps)}
|
|
||||||
type={htmlType}
|
|
||||||
className={classes}
|
|
||||||
onClick={this.handleClick}
|
|
||||||
ref={this.saveButtonRef}
|
|
||||||
>
|
|
||||||
{iconNode}
|
{iconNode}
|
||||||
{kids}
|
{kids}
|
||||||
</button>
|
</a>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (type === 'link') {
|
// React does not recognize the `htmlType` prop on a DOM element. Here we pick it out of `rest`.
|
||||||
return buttonNode;
|
const { htmlType, ...otherProps } = rest as NativeButtonProps;
|
||||||
}
|
|
||||||
|
|
||||||
return <Wave>{buttonNode}</Wave>;
|
const buttonNode = (
|
||||||
}}
|
<button
|
||||||
</SizeContext.Consumer>
|
{...(omit(otherProps, ['loading']) as NativeButtonProps)}
|
||||||
);
|
type={htmlType}
|
||||||
}
|
className={classes}
|
||||||
}
|
onClick={handleClick}
|
||||||
|
ref={buttonRef}
|
||||||
|
>
|
||||||
|
{iconNode}
|
||||||
|
{kids}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (type === 'link') {
|
||||||
|
return buttonNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Wave>{buttonNode}</Wave>;
|
||||||
|
}}
|
||||||
|
</SizeContext.Consumer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Button.defaultProps = {
|
||||||
|
loading: false,
|
||||||
|
ghost: false,
|
||||||
|
block: false,
|
||||||
|
htmlType: 'button' as ButtonProps['htmlType'],
|
||||||
|
};
|
||||||
|
|
||||||
|
Button.Group = Group;
|
||||||
|
Button.__ANT_BUTTON = true;
|
||||||
|
|
||||||
export default Button;
|
export default Button;
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import Button from './button';
|
import Button from './button';
|
||||||
import ButtonGroup from './button-group';
|
|
||||||
|
|
||||||
export { ButtonProps, ButtonShape, ButtonType } from './button';
|
export { ButtonProps, ButtonShape, ButtonType } from './button';
|
||||||
export { ButtonGroupProps } from './button-group';
|
export { ButtonGroupProps } from './button-group';
|
||||||
export { SizeType as ButtonSize } from '../config-provider/SizeContext';
|
export { SizeType as ButtonSize } from '../config-provider/SizeContext';
|
||||||
|
|
||||||
Button.Group = ButtonGroup;
|
|
||||||
export default Button;
|
export default Button;
|
||||||
|
@ -37,7 +37,7 @@ A card can be used to display content related to a single subject. The content c
|
|||||||
| title | Card title | string\|ReactNode | - | |
|
| title | Card title | string\|ReactNode | - | |
|
||||||
| type | Card style type, can be set to `inner` or not set | string | - | |
|
| type | Card style type, can be set to `inner` or not set | string | - | |
|
||||||
| onTabChange | Callback when tab is switched | (key) => void | - | |
|
| onTabChange | Callback when tab is switched | (key) => void | - | |
|
||||||
| tabProps | [Tabs](https://ant.design/components/tabs/#Tabs) | - | - | |
|
| tabProps | [Tabs](/components/tabs/#Tabs) | - | - | |
|
||||||
|
|
||||||
### Card.Grid
|
### Card.Grid
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ cols: 1
|
|||||||
| title | 卡片标题 | string\|ReactNode | - | |
|
| title | 卡片标题 | string\|ReactNode | - | |
|
||||||
| type | 卡片类型,可设置为 `inner` 或 不设置 | string | - | |
|
| type | 卡片类型,可设置为 `inner` 或 不设置 | string | - | |
|
||||||
| onTabChange | 页签切换的回调 | (key) => void | - | |
|
| onTabChange | 页签切换的回调 | (key) => void | - | |
|
||||||
| tabProps | [Tabs](https://ant.design/components/tabs-cn/#Tabs) | - | - | |
|
| tabProps | [Tabs](/components/tabs/#Tabs) | - | - | |
|
||||||
|
|
||||||
### Card.Grid
|
### Card.Grid
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ class Checkbox extends React.PureComponent<CheckboxProps, {}> {
|
|||||||
warning(
|
warning(
|
||||||
'checked' in this.props || this.context || !('value' in this.props),
|
'checked' in this.props || this.context || !('value' in this.props),
|
||||||
'Checkbox',
|
'Checkbox',
|
||||||
'`value` is not validate prop, do you mean `checked`?',
|
'`value` is not a valid prop, do you mean `checked`?',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ describe('Checkbox', () => {
|
|||||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||||
mount(<Checkbox value />);
|
mount(<Checkbox value />);
|
||||||
expect(errorSpy).toHaveBeenCalledWith(
|
expect(errorSpy).toHaveBeenCalledWith(
|
||||||
'Warning: [antd: Checkbox] `value` is not validate prop, do you mean `checked`?',
|
'Warning: [antd: Checkbox] `value` is not a valid prop, do you mean `checked`?',
|
||||||
);
|
);
|
||||||
errorSpy.mockRestore();
|
errorSpy.mockRestore();
|
||||||
});
|
});
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -82,6 +82,26 @@ describe('ConfigProvider', () => {
|
|||||||
),
|
),
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('configProvider componentSize large', () => {
|
||||||
|
expect(
|
||||||
|
render(
|
||||||
|
<ConfigProvider componentSize="large" prefixCls="config">
|
||||||
|
{renderComponent({})}
|
||||||
|
</ConfigProvider>,
|
||||||
|
),
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('configProvider componentSize middle', () => {
|
||||||
|
expect(
|
||||||
|
render(
|
||||||
|
<ConfigProvider componentSize="middle" prefixCls="config">
|
||||||
|
{renderComponent({})}
|
||||||
|
</ConfigProvider>,
|
||||||
|
),
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,12 @@ import zhCN from '../../locale/zh_CN';
|
|||||||
import enUS from '../../locale/en_US';
|
import enUS from '../../locale/en_US';
|
||||||
import TimePicker from '../../time-picker';
|
import TimePicker from '../../time-picker';
|
||||||
import Modal from '../../modal';
|
import Modal from '../../modal';
|
||||||
|
import Form from '../../form';
|
||||||
|
|
||||||
|
const delay = (timeout = 0) =>
|
||||||
|
new Promise(resolve => {
|
||||||
|
setTimeout(resolve, timeout);
|
||||||
|
});
|
||||||
|
|
||||||
describe('ConfigProvider.Locale', () => {
|
describe('ConfigProvider.Locale', () => {
|
||||||
function $$(className) {
|
function $$(className) {
|
||||||
@ -104,4 +110,41 @@ describe('ConfigProvider.Locale', () => {
|
|||||||
testLocale(wrapper);
|
testLocale(wrapper);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('form validateMessages', () => {
|
||||||
|
const wrapperComponent = ({ validateMessages }) =>
|
||||||
|
mount(
|
||||||
|
<ConfigProvider locale={zhCN} form={{ validateMessages }}>
|
||||||
|
<Form initialValues={{ age: 18 }}>
|
||||||
|
<Form.Item name="test" label="姓名" rules={[{ required: true }]}>
|
||||||
|
<input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="age" label="年龄" rules={[{ type: 'number', len: 17 }]}>
|
||||||
|
<input />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</ConfigProvider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
it('set locale zhCN', async () => {
|
||||||
|
const wrapper = wrapperComponent({});
|
||||||
|
|
||||||
|
wrapper.find('form').simulate('submit');
|
||||||
|
await delay(50);
|
||||||
|
wrapper.update();
|
||||||
|
|
||||||
|
expect(wrapper.find('.ant-form-item-explain').first().text()).toEqual('请输入姓名');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('set locale zhCN and set form validateMessages one item, other use default message', async () => {
|
||||||
|
const wrapper = wrapperComponent({ validateMessages: { required: '必须' } });
|
||||||
|
|
||||||
|
wrapper.find('form').simulate('submit');
|
||||||
|
await delay(50);
|
||||||
|
wrapper.update();
|
||||||
|
|
||||||
|
expect(wrapper.find('.ant-form-item-explain').first().text()).toEqual('必须');
|
||||||
|
expect(wrapper.find('.ant-form-item-explain').last().text()).toEqual('年龄必须等于17');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -46,6 +46,9 @@ const FormSizeDemo = () => {
|
|||||||
<div className="example">
|
<div className="example">
|
||||||
<Input />
|
<Input />
|
||||||
</div>
|
</div>
|
||||||
|
<div className="example">
|
||||||
|
<Input.Search />
|
||||||
|
</div>
|
||||||
<div className="example">
|
<div className="example">
|
||||||
<Select defaultValue="demo" options={[{ value: 'demo' }]} />
|
<Select defaultValue="demo" options={[{ value: 'demo' }]} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -93,10 +93,17 @@ class ConfigProvider extends React.Component<ConfigProviderProps> {
|
|||||||
let childNode = children;
|
let childNode = children;
|
||||||
|
|
||||||
// Additional Form provider
|
// Additional Form provider
|
||||||
|
let validateMessages: ValidateMessages = {};
|
||||||
|
|
||||||
|
if (locale && locale.Form && locale.Form.defaultValidateMessages) {
|
||||||
|
validateMessages = locale.Form.defaultValidateMessages;
|
||||||
|
}
|
||||||
if (form && form.validateMessages) {
|
if (form && form.validateMessages) {
|
||||||
childNode = (
|
validateMessages = { ...validateMessages, ...form.validateMessages };
|
||||||
<RcFormProvider validateMessages={form.validateMessages}>{children}</RcFormProvider>
|
}
|
||||||
);
|
|
||||||
|
if (Object.keys(validateMessages).length > 0) {
|
||||||
|
childNode = <RcFormProvider validateMessages={validateMessages}>{children}</RcFormProvider>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -68,6 +68,8 @@
|
|||||||
&-submenu-popup {
|
&-submenu-popup {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: @zindex-dropdown;
|
z-index: @zindex-dropdown;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
> .@{dropdown-prefix-cls}-menu {
|
> .@{dropdown-prefix-cls}-menu {
|
||||||
transform-origin: 0 0;
|
transform-origin: 0 0;
|
||||||
|
@ -24,7 +24,7 @@ Empty state placeholder.
|
|||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| description | Customize description | string \| ReactNode | - | |
|
| description | Customize description | string \| ReactNode | - | |
|
||||||
| imageStyle | style of image | CSSProperties | - | |
|
| imageStyle | style of image | CSSProperties | - | |
|
||||||
| image | Customize image. Will tread as image url when string provided. | string \| ReactNode | `Empty.PRESENTED_IMAGE_DEFAULT` | |
|
| image | Customize image. Will treat as image url when string provided. | string \| ReactNode | `Empty.PRESENTED_IMAGE_DEFAULT` | |
|
||||||
|
|
||||||
## Built-in images
|
## Built-in images
|
||||||
|
|
||||||
|
@ -24,22 +24,17 @@ type ChildrenType = RenderChildren | React.ReactNode;
|
|||||||
interface MemoInputProps {
|
interface MemoInputProps {
|
||||||
value: any;
|
value: any;
|
||||||
update: number;
|
update: number;
|
||||||
children: any;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MemoInput = React.memo<MemoInputProps>(
|
const MemoInput = React.memo(
|
||||||
({ children }) => {
|
({ children }: MemoInputProps) => children as JSX.Element,
|
||||||
return children;
|
|
||||||
},
|
|
||||||
(prev, next) => {
|
(prev, next) => {
|
||||||
return prev.value === next.value && prev.update === next.update;
|
return prev.value === next.value && prev.update === next.update;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface FormItemProps
|
export interface FormItemProps extends FormItemLabelProps, FormItemInputProps, RcFieldProps {
|
||||||
extends FormItemLabelProps,
|
|
||||||
FormItemInputProps,
|
|
||||||
Omit<RcFieldProps, 'children'> {
|
|
||||||
prefixCls?: string;
|
prefixCls?: string;
|
||||||
noStyle?: boolean;
|
noStyle?: boolean;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
@ -129,7 +124,7 @@ function FormItem(props: FormItemProps): React.ReactElement {
|
|||||||
fieldId?: string,
|
fieldId?: string,
|
||||||
meta?: Meta,
|
meta?: Meta,
|
||||||
isRequired?: boolean,
|
isRequired?: boolean,
|
||||||
): any {
|
): React.ReactNode {
|
||||||
if (noStyle) {
|
if (noStyle) {
|
||||||
return baseChildren;
|
return baseChildren;
|
||||||
}
|
}
|
||||||
@ -226,7 +221,7 @@ function FormItem(props: FormItemProps): React.ReactElement {
|
|||||||
updateRef.current += 1;
|
updateRef.current += 1;
|
||||||
|
|
||||||
if (!hasName && !isRenderProps && !dependencies) {
|
if (!hasName && !isRenderProps && !dependencies) {
|
||||||
return renderLayout(children);
|
return renderLayout(children) as JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
const variables: Record<string, string> = {};
|
const variables: Record<string, string> = {};
|
||||||
@ -304,7 +299,7 @@ function FormItem(props: FormItemProps): React.ReactElement {
|
|||||||
);
|
);
|
||||||
} else if (React.isValidElement(children)) {
|
} else if (React.isValidElement(children)) {
|
||||||
warning(
|
warning(
|
||||||
(children.props as any).defaultValue === undefined,
|
children.props.defaultValue === undefined,
|
||||||
'Form.Item',
|
'Form.Item',
|
||||||
'`defaultValue` will not work on controlled Field. You should use `initialValues` of Form instead.',
|
'`defaultValue` will not work on controlled Field. You should use `initialValues` of Form instead.',
|
||||||
);
|
);
|
||||||
@ -337,7 +332,7 @@ function FormItem(props: FormItemProps): React.ReactElement {
|
|||||||
'Form.Item',
|
'Form.Item',
|
||||||
'`name` is only used for validate React element. If you are using Form.Item as layout display, please remove `name` instead.',
|
'`name` is only used for validate React element. If you are using Form.Item as layout display, please remove `name` instead.',
|
||||||
);
|
);
|
||||||
childNode = children as any;
|
childNode = children;
|
||||||
}
|
}
|
||||||
|
|
||||||
return renderLayout(childNode, fieldId, meta, isRequired);
|
return renderLayout(childNode, fieldId, meta, isRequired);
|
||||||
|
@ -6206,7 +6206,29 @@ exports[`renders ./components/form/demo/validate-static.md correctly 1`] = `
|
|||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
class="ant-input-suffix"
|
class="ant-input-suffix"
|
||||||
/>
|
>
|
||||||
|
<span
|
||||||
|
aria-label="close-circle"
|
||||||
|
class="anticon anticon-close-circle ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="close-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 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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
@ -6360,6 +6382,27 @@ exports[`renders ./components/form/demo/validate-static.md correctly 1`] = `
|
|||||||
<span
|
<span
|
||||||
class="ant-input-suffix"
|
class="ant-input-suffix"
|
||||||
>
|
>
|
||||||
|
<span
|
||||||
|
aria-label="close-circle"
|
||||||
|
class="anticon anticon-close-circle ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="close-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 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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
<span
|
<span
|
||||||
aria-label="eye-invisible"
|
aria-label="eye-invisible"
|
||||||
class="anticon anticon-eye-invisible ant-input-password-icon"
|
class="anticon anticon-eye-invisible ant-input-password-icon"
|
||||||
|
@ -585,9 +585,9 @@ describe('Form', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
wrapper.find('form').simulate('submit');
|
wrapper.find('form').simulate('submit');
|
||||||
await delay(50);
|
await delay(100);
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
|
await delay(100);
|
||||||
expect(wrapper.find('.ant-form-item-explain').first().text()).toEqual('Bamboo is good!');
|
expect(wrapper.find('.ant-form-item-explain').first().text()).toEqual('Bamboo is good!');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* eslint-disable no-unused-expressions */
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import Form from '..';
|
import Form from '..';
|
||||||
import Input from '../../input';
|
import Input from '../../input';
|
||||||
@ -17,5 +15,3 @@ describe('Form.typescript', () => {
|
|||||||
expect(form).toBeTruthy();
|
expect(form).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/* eslint-enable */
|
|
||||||
|
@ -25,13 +25,13 @@ High performance Form component with data scope management. Including data colle
|
|||||||
| hideRequiredMark | Hide required mark for all form items | boolean | false |
|
| hideRequiredMark | Hide required mark for all form items | boolean | false |
|
||||||
| initialValues | Set value by Form initialization or reset | object | - |
|
| initialValues | Set value by Form initialization or reset | object | - |
|
||||||
| labelAlign | text align of label of all items | `left` \| `right` | `right` |
|
| labelAlign | text align of label of all items | `left` \| `right` | `right` |
|
||||||
| labelCol | label layout, like `<Col>` component. Set `span` `offset` value like `{span: 3, offset: 12}` or `sm: {span: 3, offset: 12}` | [object](https://ant.design/components/grid/#Col) | - |
|
| labelCol | label layout, like `<Col>` component. Set `span` `offset` value like `{span: 3, offset: 12}` or `sm: {span: 3, offset: 12}` | [object](/components/grid/#Col) | - |
|
||||||
| layout | Form layout | `horizontal` \| `vertical` \| `inline` | `horizontal` |
|
| layout | Form layout | `horizontal` \| `vertical` \| `inline` | `horizontal` |
|
||||||
| name | Form name. Will be the prefix of Field `id` | string | - |
|
| name | Form name. Will be the prefix of Field `id` | string | - |
|
||||||
| scrollToFirstError | Auto scroll to first failed field when submit | false | - |
|
| scrollToFirstError | Auto scroll to first failed field when submit | false | - |
|
||||||
| size | Set field component size (antd components only) | `small` \| `middle` \| `large` | - |
|
| size | Set field component size (antd components only) | `small` \| `middle` \| `large` | - |
|
||||||
| validateMessages | Validation prompt template, description [see below](#validateMessages) | [ValidateMessages](https://github.com/react-component/field-form/blob/master/src/utils/messages.ts) | - |
|
| validateMessages | Validation prompt template, description [see below](#validateMessages) | [ValidateMessages](https://github.com/react-component/field-form/blob/master/src/utils/messages.ts) | - |
|
||||||
| wrapperCol | The layout for input controls, same as `labelCol` | [object](https://ant.design/components/grid/#Col) | - |
|
| wrapperCol | The layout for input controls, same as `labelCol` | [object](/components/grid/#Col) | - |
|
||||||
| onFinish | Trigger after submitting the form and verifying data successfully | Function(values) | - |
|
| onFinish | Trigger after submitting the form and verifying data successfully | Function(values) | - |
|
||||||
| onFinishFailed | Trigger after submitting the form and verifying data failed | Function({ values, errorFields, outOfDate }) | - |
|
| onFinishFailed | Trigger after submitting the form and verifying data failed | Function({ values, errorFields, outOfDate }) | - |
|
||||||
| onFieldsChange | Trigger when field updated | Function(changedFields, allFields) | - |
|
| onFieldsChange | Trigger when field updated | Function(changedFields, allFields) | - |
|
||||||
@ -84,7 +84,7 @@ Form field component for data bidirectional binding, validation, layout, and so
|
|||||||
| labelCol | The layout of label. You can set `span` `offset` to something like `{span: 3, offset: 12}` or `sm: {span: 3, offset: 12}` same as with `<Col>`. You can set `labelCol` on Form. If both exists, use Item first | [object](/components/grid/#Col) | - | |
|
| labelCol | The layout of label. You can set `span` `offset` to something like `{span: 3, offset: 12}` or `sm: {span: 3, offset: 12}` same as with `<Col>`. You can set `labelCol` on Form. If both exists, use Item first | [object](/components/grid/#Col) | - | |
|
||||||
| name | Field name, support array | [NamePath](#NamePath) | - | |
|
| name | Field name, support array | [NamePath](#NamePath) | - | |
|
||||||
| normalize | Normalize value from component value before passing to Form instance | (value, prevValue, prevValues) => any | - | |
|
| normalize | Normalize value from component value before passing to Form instance | (value, prevValue, prevValues) => any | - | |
|
||||||
| required | Whether provided or not, it will be generated by the validation rule | boolean | false | |
|
| required | Display required style. It will be generated by the validation rule | boolean | false | |
|
||||||
| rules | Rules for field validation. Click [here](#components-form-demo-basic) to see an example | [Rule](#Rule)[] | - | |
|
| rules | Rules for field validation. Click [here](#components-form-demo-basic) to see an example | [Rule](#Rule)[] | - | |
|
||||||
| shouldUpdate | Custom field update logic. See [below](#shouldUpdate) | boolean \| (prevValue, curValue) => boolean | false | |
|
| shouldUpdate | Custom field update logic. See [below](#shouldUpdate) | boolean \| (prevValue, curValue) => boolean | false | |
|
||||||
| trigger | When to collect the value of children node | string | onChange | |
|
| trigger | When to collect the value of children node | string | onChange | |
|
||||||
|
@ -26,13 +26,13 @@ title: Form
|
|||||||
| hideRequiredMark | 隐藏所有表单项的必选标记 | boolean | false |
|
| hideRequiredMark | 隐藏所有表单项的必选标记 | boolean | false |
|
||||||
| initialValues | 表单默认值,只有初始化以及重置时生效 | object | - |
|
| initialValues | 表单默认值,只有初始化以及重置时生效 | object | - |
|
||||||
| labelAlign | label 标签的文本对齐方式 | `left` \| `right` | `right` |
|
| labelAlign | label 标签的文本对齐方式 | `left` \| `right` | `right` |
|
||||||
| labelCol | label 标签布局,同 `<Col>` 组件,设置 `span` `offset` 值,如 `{span: 3, offset: 12}` 或 `sm: {span: 3, offset: 12}` | [object](https://ant.design/components/grid/#Col) | - |
|
| labelCol | label 标签布局,同 `<Col>` 组件,设置 `span` `offset` 值,如 `{span: 3, offset: 12}` 或 `sm: {span: 3, offset: 12}` | [object](/components/grid/#Col) | - |
|
||||||
| layout | 表单布局 | `horizontal` \| `vertical` \| `inline` | `horizontal` |
|
| layout | 表单布局 | `horizontal` \| `vertical` \| `inline` | `horizontal` |
|
||||||
| name | 表单名称,会作为表单字段 `id` 前缀使用 | string | - |
|
| name | 表单名称,会作为表单字段 `id` 前缀使用 | string | - |
|
||||||
| scrollToFirstError | 提交失败自动滚动到第一个错误字段 | false | - |
|
| scrollToFirstError | 提交失败自动滚动到第一个错误字段 | false | - |
|
||||||
| size | 设置字段组件的尺寸(仅限 antd 组件) | `small` \| `middle` \| `large` | - |
|
| size | 设置字段组件的尺寸(仅限 antd 组件) | `small` \| `middle` \| `large` | - |
|
||||||
| validateMessages | 验证提示模板,说明[见下](#validateMessages) | [ValidateMessages](https://github.com/react-component/field-form/blob/master/src/utils/messages.ts) | - |
|
| validateMessages | 验证提示模板,说明[见下](#validateMessages) | [ValidateMessages](https://github.com/react-component/field-form/blob/master/src/utils/messages.ts) | - |
|
||||||
| wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol | [object](https://ant.design/components/grid/#Col) | - |
|
| wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol | [object](/components/grid/#Col) | - |
|
||||||
| onFinish | 提交表单且数据验证成功后回调事件 | Function(values) | - |
|
| onFinish | 提交表单且数据验证成功后回调事件 | Function(values) | - |
|
||||||
| onFinishFailed | 提交表单且数据验证失败后回调事件 | Function({ values, errorFields, outOfDate }) | - |
|
| onFinishFailed | 提交表单且数据验证失败后回调事件 | Function({ values, errorFields, outOfDate }) | - |
|
||||||
| onFieldsChange | 字段更新时触发回调事件 | Function(changedFields, allFields) | - |
|
| onFieldsChange | 字段更新时触发回调事件 | Function(changedFields, allFields) | - |
|
||||||
@ -85,7 +85,7 @@ const validateMessages = {
|
|||||||
| labelCol | `label` 标签布局,同 `<Col>` 组件,设置 `span` `offset` 值,如 `{span: 3, offset: 12}` 或 `sm: {span: 3, offset: 12}`。你可以通过 Form 的 `labelCol` 进行统一设置。当和 Form 同时设置时,以 Item 为准 | [object](/components/grid/#Col) | - | |
|
| labelCol | `label` 标签布局,同 `<Col>` 组件,设置 `span` `offset` 值,如 `{span: 3, offset: 12}` 或 `sm: {span: 3, offset: 12}`。你可以通过 Form 的 `labelCol` 进行统一设置。当和 Form 同时设置时,以 Item 为准 | [object](/components/grid/#Col) | - | |
|
||||||
| name | 字段名,支持数组 | [NamePath](#NamePath) | - | |
|
| name | 字段名,支持数组 | [NamePath](#NamePath) | - | |
|
||||||
| normalize | 组件获取值后进行转换,再放入 Form 中 | (value, prevValue, prevValues) => any | - | |
|
| normalize | 组件获取值后进行转换,再放入 Form 中 | (value, prevValue, prevValues) => any | - | |
|
||||||
| required | 是否必填,如不设置,则会根据校验规则自动生成 | boolean | false | |
|
| required | 必填样式设置。如不设置,则会根据校验规则自动生成 | boolean | false | |
|
||||||
| rules | 校验规则,设置字段的校验逻辑。点击[此处](#components-form-demo-basic)查看示例 | [Rule](#Rule)[] | - | |
|
| rules | 校验规则,设置字段的校验逻辑。点击[此处](#components-form-demo-basic)查看示例 | [Rule](#Rule)[] | - | |
|
||||||
| shouldUpdate | 自定义字段更新逻辑,说明[见下](#shouldUpdate) | boolean \| (prevValue, curValue) => boolean | false | |
|
| shouldUpdate | 自定义字段更新逻辑,说明[见下](#shouldUpdate) | boolean \| (prevValue, curValue) => boolean | false | |
|
||||||
| trigger | 设置收集字段值变更的时机 | string | onChange | |
|
| trigger | 设置收集字段值变更的时机 | string | onChange | |
|
||||||
|
@ -36,6 +36,10 @@
|
|||||||
.@{form-prefix-cls}-vertical {
|
.@{form-prefix-cls}-vertical {
|
||||||
.@{form-item-prefix-cls} {
|
.@{form-item-prefix-cls} {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
&-label > label {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,21 +54,25 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
|
|||||||
|
|
||||||
renderClearIcon(prefixCls: string) {
|
renderClearIcon(prefixCls: string) {
|
||||||
const { allowClear, value, disabled, readOnly, inputType, handleReset } = this.props;
|
const { allowClear, value, disabled, readOnly, inputType, handleReset } = this.props;
|
||||||
if (
|
|
||||||
!allowClear ||
|
if (!allowClear) {
|
||||||
disabled ||
|
|
||||||
readOnly ||
|
|
||||||
value === undefined ||
|
|
||||||
value === null ||
|
|
||||||
value === ''
|
|
||||||
) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const needClear = !disabled && !readOnly && value;
|
||||||
const className =
|
const className =
|
||||||
inputType === ClearableInputType[0]
|
inputType === ClearableInputType[0]
|
||||||
? `${prefixCls}-textarea-clear-icon`
|
? `${prefixCls}-textarea-clear-icon`
|
||||||
: `${prefixCls}-clear-icon`;
|
: `${prefixCls}-clear-icon`;
|
||||||
return <CloseCircleFilled onClick={handleReset} className={className} role="button" />;
|
return (
|
||||||
|
<CloseCircleFilled
|
||||||
|
onClick={handleReset}
|
||||||
|
className={classNames(className, {
|
||||||
|
[`${className}-hidden`]: !needClear,
|
||||||
|
})}
|
||||||
|
role="button"
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSuffix(prefixCls: string) {
|
renderSuffix(prefixCls: string) {
|
||||||
|
@ -4,6 +4,7 @@ import SearchOutlined from '@ant-design/icons/SearchOutlined';
|
|||||||
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
|
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
|
||||||
import Input, { InputProps } from './Input';
|
import Input, { InputProps } from './Input';
|
||||||
import Button from '../button';
|
import Button from '../button';
|
||||||
|
import SizeContext, { SizeType } from '../config-provider/SizeContext';
|
||||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||||
|
|
||||||
export interface SearchProps extends InputProps {
|
export interface SearchProps extends InputProps {
|
||||||
@ -65,13 +66,22 @@ export default class Search extends React.Component<SearchProps, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderLoading = (prefixCls: string) => {
|
renderLoading = (prefixCls: string) => {
|
||||||
const { enterButton, size } = this.props;
|
const { enterButton, size: customizeSize } = this.props;
|
||||||
|
|
||||||
if (enterButton) {
|
if (enterButton) {
|
||||||
return (
|
return (
|
||||||
<Button className={`${prefixCls}-button`} type="primary" size={size} key="enterButton">
|
<SizeContext.Consumer>
|
||||||
<LoadingOutlined />
|
{size => (
|
||||||
</Button>
|
<Button
|
||||||
|
className={`${prefixCls}-button`}
|
||||||
|
type="primary"
|
||||||
|
size={customizeSize || size}
|
||||||
|
key="enterButton"
|
||||||
|
>
|
||||||
|
<LoadingOutlined />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</SizeContext.Consumer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return <LoadingOutlined className={`${prefixCls}-icon`} key="loadingIcon" />;
|
return <LoadingOutlined className={`${prefixCls}-icon`} key="loadingIcon" />;
|
||||||
@ -104,8 +114,8 @@ export default class Search extends React.Component<SearchProps, any> {
|
|||||||
return icon;
|
return icon;
|
||||||
};
|
};
|
||||||
|
|
||||||
renderAddonAfter = (prefixCls: string) => {
|
renderAddonAfter = (prefixCls: string, size: SizeType) => {
|
||||||
const { enterButton, size, disabled, addonAfter, loading } = this.props;
|
const { enterButton, disabled, addonAfter, loading } = this.props;
|
||||||
const btnClassName = `${prefixCls}-button`;
|
const btnClassName = `${prefixCls}-button`;
|
||||||
|
|
||||||
if (loading && enterButton) {
|
if (loading && enterButton) {
|
||||||
@ -165,9 +175,9 @@ export default class Search extends React.Component<SearchProps, any> {
|
|||||||
const {
|
const {
|
||||||
prefixCls: customizePrefixCls,
|
prefixCls: customizePrefixCls,
|
||||||
inputPrefixCls: customizeInputPrefixCls,
|
inputPrefixCls: customizeInputPrefixCls,
|
||||||
size,
|
|
||||||
enterButton,
|
enterButton,
|
||||||
className,
|
className,
|
||||||
|
size: customizeSize,
|
||||||
...restProps
|
...restProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@ -177,32 +187,38 @@ export default class Search extends React.Component<SearchProps, any> {
|
|||||||
const prefixCls = getPrefixCls('input-search', customizePrefixCls);
|
const prefixCls = getPrefixCls('input-search', customizePrefixCls);
|
||||||
const inputPrefixCls = getPrefixCls('input', customizeInputPrefixCls);
|
const inputPrefixCls = getPrefixCls('input', customizeInputPrefixCls);
|
||||||
|
|
||||||
let inputClassName;
|
const getClassName = (size: SizeType) => {
|
||||||
|
let inputClassName;
|
||||||
if (enterButton) {
|
if (enterButton) {
|
||||||
inputClassName = classNames(prefixCls, className, {
|
inputClassName = classNames(prefixCls, className, {
|
||||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||||
[`${prefixCls}-enter-button`]: !!enterButton,
|
[`${prefixCls}-enter-button`]: !!enterButton,
|
||||||
[`${prefixCls}-${size}`]: !!size,
|
[`${prefixCls}-${size}`]: !!size,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
inputClassName = classNames(prefixCls, className, {
|
inputClassName = classNames(prefixCls, className, {
|
||||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return inputClassName;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Input
|
<SizeContext.Consumer>
|
||||||
onPressEnter={this.onSearch}
|
{size => (
|
||||||
{...restProps}
|
<Input
|
||||||
size={size}
|
onPressEnter={this.onSearch}
|
||||||
prefixCls={inputPrefixCls}
|
{...restProps}
|
||||||
addonAfter={this.renderAddonAfter(prefixCls)}
|
size={customizeSize || size}
|
||||||
suffix={this.renderSuffix(prefixCls)}
|
prefixCls={inputPrefixCls}
|
||||||
onChange={this.onChange}
|
addonAfter={this.renderAddonAfter(prefixCls, customizeSize || size)}
|
||||||
ref={this.saveInput}
|
suffix={this.renderSuffix(prefixCls)}
|
||||||
className={inputClassName}
|
onChange={this.onChange}
|
||||||
/>
|
ref={this.saveInput}
|
||||||
|
className={getClassName(customizeSize || size)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</SizeContext.Consumer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -960,7 +960,29 @@ exports[`renders ./components/input/demo/allowClear.md correctly 1`] = `
|
|||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
class="ant-input-suffix"
|
class="ant-input-suffix"
|
||||||
/>
|
>
|
||||||
|
<span
|
||||||
|
aria-label="close-circle"
|
||||||
|
class="anticon anticon-close-circle ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="close-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 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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
@ -971,6 +993,27 @@ exports[`renders ./components/input/demo/allowClear.md correctly 1`] = `
|
|||||||
class="ant-input"
|
class="ant-input"
|
||||||
placeholder="textarea with clear icon"
|
placeholder="textarea with clear icon"
|
||||||
/>
|
/>
|
||||||
|
<span
|
||||||
|
aria-label="close-circle"
|
||||||
|
class="anticon anticon-close-circle ant-input-textarea-clear-icon ant-input-textarea-clear-icon-hidden"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="close-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 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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
@ -48,7 +48,29 @@ exports[`Input allowClear should change type when click 2`] = `
|
|||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
class="ant-input-suffix"
|
class="ant-input-suffix"
|
||||||
/>
|
>
|
||||||
|
<span
|
||||||
|
aria-label="close-circle"
|
||||||
|
class="anticon anticon-close-circle ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="close-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 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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -63,7 +85,29 @@ exports[`Input allowClear should not show icon if defaultValue is undefined, nul
|
|||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
class="ant-input-suffix"
|
class="ant-input-suffix"
|
||||||
/>
|
>
|
||||||
|
<span
|
||||||
|
aria-label="close-circle"
|
||||||
|
class="anticon anticon-close-circle ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="close-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 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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -78,7 +122,29 @@ exports[`Input allowClear should not show icon if defaultValue is undefined, nul
|
|||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
class="ant-input-suffix"
|
class="ant-input-suffix"
|
||||||
/>
|
>
|
||||||
|
<span
|
||||||
|
aria-label="close-circle"
|
||||||
|
class="anticon anticon-close-circle ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="close-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 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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -93,7 +159,29 @@ exports[`Input allowClear should not show icon if defaultValue is undefined, nul
|
|||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
class="ant-input-suffix"
|
class="ant-input-suffix"
|
||||||
/>
|
>
|
||||||
|
<span
|
||||||
|
aria-label="close-circle"
|
||||||
|
class="anticon anticon-close-circle ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="close-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 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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -108,7 +196,29 @@ exports[`Input allowClear should not show icon if value is undefined, null or em
|
|||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
class="ant-input-suffix"
|
class="ant-input-suffix"
|
||||||
/>
|
>
|
||||||
|
<span
|
||||||
|
aria-label="close-circle"
|
||||||
|
class="anticon anticon-close-circle ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="close-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 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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -123,7 +233,29 @@ exports[`Input allowClear should not show icon if value is undefined, null or em
|
|||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
class="ant-input-suffix"
|
class="ant-input-suffix"
|
||||||
/>
|
>
|
||||||
|
<span
|
||||||
|
aria-label="close-circle"
|
||||||
|
class="anticon anticon-close-circle ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="close-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 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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -138,7 +270,29 @@ exports[`Input allowClear should not show icon if value is undefined, null or em
|
|||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
class="ant-input-suffix"
|
class="ant-input-suffix"
|
||||||
/>
|
>
|
||||||
|
<span
|
||||||
|
aria-label="close-circle"
|
||||||
|
class="anticon anticon-close-circle ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="close-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 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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -40,6 +40,27 @@ exports[`TextArea allowClear should change type when click 2`] = `
|
|||||||
<textarea
|
<textarea
|
||||||
class="ant-input"
|
class="ant-input"
|
||||||
/>
|
/>
|
||||||
|
<span
|
||||||
|
aria-label="close-circle"
|
||||||
|
class="anticon anticon-close-circle ant-input-textarea-clear-icon ant-input-textarea-clear-icon-hidden"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="close-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 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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -50,6 +71,27 @@ exports[`TextArea allowClear should not show icon if defaultValue is undefined,
|
|||||||
<textarea
|
<textarea
|
||||||
class="ant-input"
|
class="ant-input"
|
||||||
/>
|
/>
|
||||||
|
<span
|
||||||
|
aria-label="close-circle"
|
||||||
|
class="anticon anticon-close-circle ant-input-textarea-clear-icon ant-input-textarea-clear-icon-hidden"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="close-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 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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -60,6 +102,27 @@ exports[`TextArea allowClear should not show icon if defaultValue is undefined,
|
|||||||
<textarea
|
<textarea
|
||||||
class="ant-input"
|
class="ant-input"
|
||||||
/>
|
/>
|
||||||
|
<span
|
||||||
|
aria-label="close-circle"
|
||||||
|
class="anticon anticon-close-circle ant-input-textarea-clear-icon ant-input-textarea-clear-icon-hidden"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="close-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 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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -70,6 +133,27 @@ exports[`TextArea allowClear should not show icon if defaultValue is undefined,
|
|||||||
<textarea
|
<textarea
|
||||||
class="ant-input"
|
class="ant-input"
|
||||||
/>
|
/>
|
||||||
|
<span
|
||||||
|
aria-label="close-circle"
|
||||||
|
class="anticon anticon-close-circle ant-input-textarea-clear-icon ant-input-textarea-clear-icon-hidden"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="close-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 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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -80,6 +164,27 @@ exports[`TextArea allowClear should not show icon if value is undefined, null or
|
|||||||
<textarea
|
<textarea
|
||||||
class="ant-input"
|
class="ant-input"
|
||||||
/>
|
/>
|
||||||
|
<span
|
||||||
|
aria-label="close-circle"
|
||||||
|
class="anticon anticon-close-circle ant-input-textarea-clear-icon ant-input-textarea-clear-icon-hidden"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="close-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 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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -90,6 +195,27 @@ exports[`TextArea allowClear should not show icon if value is undefined, null or
|
|||||||
<textarea
|
<textarea
|
||||||
class="ant-input"
|
class="ant-input"
|
||||||
/>
|
/>
|
||||||
|
<span
|
||||||
|
aria-label="close-circle"
|
||||||
|
class="anticon anticon-close-circle ant-input-textarea-clear-icon ant-input-textarea-clear-icon-hidden"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="close-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 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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -100,6 +226,27 @@ exports[`TextArea allowClear should not show icon if value is undefined, null or
|
|||||||
<textarea
|
<textarea
|
||||||
class="ant-input"
|
class="ant-input"
|
||||||
/>
|
/>
|
||||||
|
<span
|
||||||
|
aria-label="close-circle"
|
||||||
|
class="anticon anticon-close-circle ant-input-textarea-clear-icon ant-input-textarea-clear-icon-hidden"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="close-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 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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -56,10 +56,7 @@ describe('Input', () => {
|
|||||||
describe('focus trigger warning', () => {
|
describe('focus trigger warning', () => {
|
||||||
it('not trigger', () => {
|
it('not trigger', () => {
|
||||||
const wrapper = mount(<Input suffix="bamboo" />);
|
const wrapper = mount(<Input suffix="bamboo" />);
|
||||||
wrapper
|
wrapper.find('input').instance().focus();
|
||||||
.find('input')
|
|
||||||
.instance()
|
|
||||||
.focus();
|
|
||||||
wrapper.setProps({
|
wrapper.setProps({
|
||||||
suffix: 'light',
|
suffix: 'light',
|
||||||
});
|
});
|
||||||
@ -67,10 +64,7 @@ describe('Input', () => {
|
|||||||
});
|
});
|
||||||
it('trigger warning', () => {
|
it('trigger warning', () => {
|
||||||
const wrapper = mount(<Input />, { attachTo: document.body });
|
const wrapper = mount(<Input />, { attachTo: document.body });
|
||||||
wrapper
|
wrapper.find('input').instance().focus();
|
||||||
.find('input')
|
|
||||||
.instance()
|
|
||||||
.focus();
|
|
||||||
wrapper.setProps({
|
wrapper.setProps({
|
||||||
suffix: 'light',
|
suffix: 'light',
|
||||||
});
|
});
|
||||||
@ -129,10 +123,7 @@ describe('Input allowClear', () => {
|
|||||||
wrapper.find('input').simulate('change', { target: { value: '111' } });
|
wrapper.find('input').simulate('change', { target: { value: '111' } });
|
||||||
expect(wrapper.find('input').getDOMNode().value).toEqual('111');
|
expect(wrapper.find('input').getDOMNode().value).toEqual('111');
|
||||||
expect(wrapper.render()).toMatchSnapshot();
|
expect(wrapper.render()).toMatchSnapshot();
|
||||||
wrapper
|
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
|
||||||
.find('.ant-input-clear-icon')
|
|
||||||
.at(0)
|
|
||||||
.simulate('click');
|
|
||||||
expect(wrapper.render()).toMatchSnapshot();
|
expect(wrapper.render()).toMatchSnapshot();
|
||||||
expect(wrapper.find('input').getDOMNode().value).toEqual('');
|
expect(wrapper.find('input').getDOMNode().value).toEqual('');
|
||||||
});
|
});
|
||||||
@ -141,7 +132,7 @@ describe('Input allowClear', () => {
|
|||||||
const wrappers = [null, undefined, ''].map(val => mount(<Input allowClear value={val} />));
|
const wrappers = [null, undefined, ''].map(val => mount(<Input allowClear value={val} />));
|
||||||
wrappers.forEach(wrapper => {
|
wrappers.forEach(wrapper => {
|
||||||
expect(wrapper.find('input').getDOMNode().value).toEqual('');
|
expect(wrapper.find('input').getDOMNode().value).toEqual('');
|
||||||
expect(wrapper.find('.ant-input-clear-icon').exists()).toEqual(false);
|
expect(wrapper.find('.ant-input-clear-icon-hidden').exists()).toBeTruthy();
|
||||||
expect(wrapper.render()).toMatchSnapshot();
|
expect(wrapper.render()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -152,7 +143,7 @@ describe('Input allowClear', () => {
|
|||||||
);
|
);
|
||||||
wrappers.forEach(wrapper => {
|
wrappers.forEach(wrapper => {
|
||||||
expect(wrapper.find('input').getDOMNode().value).toEqual('');
|
expect(wrapper.find('input').getDOMNode().value).toEqual('');
|
||||||
expect(wrapper.find('.ant-input-clear-icon').exists()).toEqual(false);
|
expect(wrapper.find('.ant-input-clear-icon-hidden').exists()).toBeTruthy();
|
||||||
expect(wrapper.render()).toMatchSnapshot();
|
expect(wrapper.render()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -165,18 +156,10 @@ describe('Input allowClear', () => {
|
|||||||
argumentEventObjectValue = e.target.value;
|
argumentEventObjectValue = e.target.value;
|
||||||
};
|
};
|
||||||
const wrapper = mount(<Input allowClear defaultValue="111" onChange={onChange} />);
|
const wrapper = mount(<Input allowClear defaultValue="111" onChange={onChange} />);
|
||||||
wrapper
|
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
|
||||||
.find('.ant-input-clear-icon')
|
|
||||||
.at(0)
|
|
||||||
.simulate('click');
|
|
||||||
expect(argumentEventObject.type).toBe('click');
|
expect(argumentEventObject.type).toBe('click');
|
||||||
expect(argumentEventObjectValue).toBe('');
|
expect(argumentEventObjectValue).toBe('');
|
||||||
expect(
|
expect(wrapper.find('input').at(0).getDOMNode().value).toBe('');
|
||||||
wrapper
|
|
||||||
.find('input')
|
|
||||||
.at(0)
|
|
||||||
.getDOMNode().value,
|
|
||||||
).toBe('');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should trigger event correctly on controlled mode', () => {
|
it('should trigger event correctly on controlled mode', () => {
|
||||||
@ -187,39 +170,23 @@ describe('Input allowClear', () => {
|
|||||||
argumentEventObjectValue = e.target.value;
|
argumentEventObjectValue = e.target.value;
|
||||||
};
|
};
|
||||||
const wrapper = mount(<Input allowClear value="111" onChange={onChange} />);
|
const wrapper = mount(<Input allowClear value="111" onChange={onChange} />);
|
||||||
wrapper
|
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
|
||||||
.find('.ant-input-clear-icon')
|
|
||||||
.at(0)
|
|
||||||
.simulate('click');
|
|
||||||
expect(argumentEventObject.type).toBe('click');
|
expect(argumentEventObject.type).toBe('click');
|
||||||
expect(argumentEventObjectValue).toBe('');
|
expect(argumentEventObjectValue).toBe('');
|
||||||
expect(
|
expect(wrapper.find('input').at(0).getDOMNode().value).toBe('111');
|
||||||
wrapper
|
|
||||||
.find('input')
|
|
||||||
.at(0)
|
|
||||||
.getDOMNode().value,
|
|
||||||
).toBe('111');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should focus input after clear', () => {
|
it('should focus input after clear', () => {
|
||||||
const wrapper = mount(<Input allowClear defaultValue="111" />, { attachTo: document.body });
|
const wrapper = mount(<Input allowClear defaultValue="111" />, { attachTo: document.body });
|
||||||
wrapper
|
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
|
||||||
.find('.ant-input-clear-icon')
|
expect(document.activeElement).toBe(wrapper.find('input').at(0).getDOMNode());
|
||||||
.at(0)
|
|
||||||
.simulate('click');
|
|
||||||
expect(document.activeElement).toBe(
|
|
||||||
wrapper
|
|
||||||
.find('input')
|
|
||||||
.at(0)
|
|
||||||
.getDOMNode(),
|
|
||||||
);
|
|
||||||
wrapper.unmount();
|
wrapper.unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
['disabled', 'readOnly'].forEach(prop => {
|
['disabled', 'readOnly'].forEach(prop => {
|
||||||
it(`should not support allowClear when it is ${prop}`, () => {
|
it(`should not support allowClear when it is ${prop}`, () => {
|
||||||
const wrapper = mount(<Input allowClear defaultValue="111" {...{ [prop]: true }} />);
|
const wrapper = mount(<Input allowClear defaultValue="111" {...{ [prop]: true }} />);
|
||||||
expect(wrapper.find('.ant-input-clear-icon').length).toBe(0);
|
expect(wrapper.find('.ant-input-clear-icon-hidden').exists()).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -183,7 +183,7 @@ describe('TextArea allowClear', () => {
|
|||||||
const wrappers = [null, undefined, ''].map(val => mount(<TextArea allowClear value={val} />));
|
const wrappers = [null, undefined, ''].map(val => mount(<TextArea allowClear value={val} />));
|
||||||
wrappers.forEach(wrapper => {
|
wrappers.forEach(wrapper => {
|
||||||
expect(wrapper.find('textarea').getDOMNode().value).toEqual('');
|
expect(wrapper.find('textarea').getDOMNode().value).toEqual('');
|
||||||
expect(wrapper.find('.ant-input-textarea-clear-icon').exists()).toEqual(false);
|
expect(wrapper.find('.ant-input-textarea-clear-icon-hidden').exists()).toBeTruthy();
|
||||||
expect(wrapper.render()).toMatchSnapshot();
|
expect(wrapper.render()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -236,7 +236,7 @@ describe('TextArea allowClear', () => {
|
|||||||
|
|
||||||
it('should not support allowClear when it is disabled', () => {
|
it('should not support allowClear when it is disabled', () => {
|
||||||
const wrapper = mount(<TextArea allowClear defaultValue="111" disabled />);
|
const wrapper = mount(<TextArea allowClear defaultValue="111" disabled />);
|
||||||
expect(wrapper.find('.ant-input-textarea-clear-icon').length).toBe(0);
|
expect(wrapper.find('.ant-input-textarea-clear-icon-hidden').exists()).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('not block input when `value` is undefined', () => {
|
it('not block input when `value` is undefined', () => {
|
||||||
|
@ -19,6 +19,10 @@
|
|||||||
+ i {
|
+ i {
|
||||||
margin-left: 6px;
|
margin-left: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-hidden {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================= Input =========================
|
// ========================= Input =========================
|
||||||
|
@ -8,27 +8,40 @@
|
|||||||
.@{ant-prefix}-input {
|
.@{ant-prefix}-input {
|
||||||
.reset-component;
|
.reset-component;
|
||||||
.input;
|
.input;
|
||||||
}
|
|
||||||
|
|
||||||
//== Style for input-group: input with label, with button or dropdown...
|
//== Style for input-group: input with label, with button or dropdown...
|
||||||
.@{ant-prefix}-input-group {
|
&-group {
|
||||||
.reset-component;
|
.reset-component;
|
||||||
.input-group(~'@{ant-prefix}-input');
|
.input-group(~'@{ant-prefix}-input');
|
||||||
&-wrapper {
|
&-wrapper {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: start;
|
text-align: start;
|
||||||
vertical-align: top; // https://github.com/ant-design/ant-design/issues/6403
|
vertical-align: top; // https://github.com/ant-design/ant-design/issues/6403
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.@{ant-prefix}-input-password-icon {
|
&-password-icon {
|
||||||
color: @text-color-secondary;
|
color: @text-color-secondary;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: @input-icon-hover-color;
|
color: @input-icon-hover-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[type='color'] {
|
||||||
|
height: @input-height-base;
|
||||||
|
|
||||||
|
&.@{ant-prefix}-input-lg {
|
||||||
|
height: @input-height-lg;
|
||||||
|
}
|
||||||
|
&.@{ant-prefix}-input-sm {
|
||||||
|
height: @input-height-sm;
|
||||||
|
padding-top: 3px;
|
||||||
|
padding-bottom: 3px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,10 +23,10 @@ A list can be used to display content related to a single subject. The content c
|
|||||||
| header | List header renderer | string\|ReactNode | - | |
|
| header | List header renderer | string\|ReactNode | - | |
|
||||||
| itemLayout | The layout of list, default is `horizontal`, If a vertical list is desired, set the itemLayout property to `vertical` | string | - | |
|
| itemLayout | The layout of list, default is `horizontal`, If a vertical list is desired, set the itemLayout property to `vertical` | string | - | |
|
||||||
| rowKey | Item's unique key, could be a string or function that returns a string | string\|Function(record):string | `key` | |
|
| rowKey | Item's unique key, could be a string or function that returns a string | string\|Function(record):string | `key` | |
|
||||||
| loading | Shows a loading indicator while the contents of the list are being fetched | boolean\|[SpinProps](https://ant.design/components/spin/#API) ([more](https://github.com/ant-design/ant-design/issues/8659)) | false | |
|
| loading | Shows a loading indicator while the contents of the list are being fetched | boolean\|[SpinProps](/components/spin/#API) ([more](https://github.com/ant-design/ant-design/issues/8659)) | false | |
|
||||||
| loadMore | Shows a load more content | string\|ReactNode | - | |
|
| loadMore | Shows a load more content | string\|ReactNode | - | |
|
||||||
| locale | i18n text including empty text | object | emptyText: 'No Data' <br> | |
|
| locale | i18n text including empty text | object | emptyText: 'No Data' <br> | |
|
||||||
| pagination | Pagination [config](https://ant.design/components/pagination/), hide it by setting it to false | boolean \| object | false | |
|
| pagination | Pagination [config](/components/pagination/), hide it by setting it to false | boolean \| object | false | |
|
||||||
| size | Size of list | `default` \| `large` \| `small` | `default` | |
|
| size | Size of list | `default` \| `large` \| `small` | `default` | |
|
||||||
| split | Toggles rendering of the split under the list item | boolean | true | |
|
| split | Toggles rendering of the split under the list item | boolean | true | |
|
||||||
| dataSource | dataSource array for list | any[] | - | |
|
| dataSource | dataSource array for list | any[] | - | |
|
||||||
|
@ -23,7 +23,7 @@ cols: 1
|
|||||||
| grid | 列表栅格配置 | [object](#List-grid-props) | - | |
|
| grid | 列表栅格配置 | [object](#List-grid-props) | - | |
|
||||||
| header | 列表头部 | string\|ReactNode | - | |
|
| header | 列表头部 | string\|ReactNode | - | |
|
||||||
| itemLayout | 设置 `List.Item` 布局, 设置成 `vertical` 则竖直样式显示, 默认横排 | string | - | |
|
| itemLayout | 设置 `List.Item` 布局, 设置成 `vertical` 则竖直样式显示, 默认横排 | string | - | |
|
||||||
| loading | 当卡片内容还在加载中时,可以用 `loading` 展示一个占位 | boolean\|[object](https://ant.design/components/spin-cn/#API) ([更多](https://github.com/ant-design/ant-design/issues/8659)) | false | |
|
| loading | 当卡片内容还在加载中时,可以用 `loading` 展示一个占位 | boolean\|[object](/components/spin/#API) ([更多](https://github.com/ant-design/ant-design/issues/8659)) | false | |
|
||||||
| loadMore | 加载更多 | string\|ReactNode | - | |
|
| loadMore | 加载更多 | string\|ReactNode | - | |
|
||||||
| locale | 默认文案设置,目前包括空数据文案 | object | emptyText: '暂无数据' | |
|
| locale | 默认文案设置,目前包括空数据文案 | object | emptyText: '暂无数据' | |
|
||||||
| pagination | 对应的 `pagination` 配置, 设置 `false` 不显示 | boolean\|object | false | |
|
| pagination | 对应的 `pagination` 配置, 设置 `false` 不显示 | boolean\|object | false | |
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { ValidateMessages } from 'rc-field-form/lib/interface';
|
||||||
import warning from '../_util/warning';
|
import warning from '../_util/warning';
|
||||||
|
|
||||||
import { ModalLocale, changeConfirmLocale } from '../modal/locale';
|
import { ModalLocale, changeConfirmLocale } from '../modal/locale';
|
||||||
@ -30,6 +31,9 @@ export interface Locale {
|
|||||||
PageHeader?: Object;
|
PageHeader?: Object;
|
||||||
Icon?: Object;
|
Icon?: Object;
|
||||||
Text?: Object;
|
Text?: Object;
|
||||||
|
Form?: {
|
||||||
|
defaultValidateMessages: ValidateMessages;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LocaleProviderProps {
|
export interface LocaleProviderProps {
|
||||||
|
@ -10,12 +10,22 @@ const localeValues: Locale = {
|
|||||||
DatePicker,
|
DatePicker,
|
||||||
TimePicker,
|
TimePicker,
|
||||||
Calendar,
|
Calendar,
|
||||||
|
global: {
|
||||||
|
placeholder: 'אנא בחר',
|
||||||
|
},
|
||||||
Table: {
|
Table: {
|
||||||
filterTitle: 'תפריט סינון',
|
filterTitle: 'תפריט סינון',
|
||||||
filterConfirm: 'אישור',
|
filterConfirm: 'אישור',
|
||||||
filterReset: 'איפוס',
|
filterReset: 'איפוס',
|
||||||
selectAll: 'בחר הכל',
|
selectAll: 'בחר הכל',
|
||||||
selectInvert: 'הפוך בחירה',
|
selectInvert: 'הפוך בחירה',
|
||||||
|
selectionAll: 'בחר את כל הנתונים',
|
||||||
|
sortTitle: 'מיון',
|
||||||
|
expand: 'הרחב שורה',
|
||||||
|
collapse: 'צמצם שורהw',
|
||||||
|
triggerDesc: 'לחץ על מיון לפי סדר יורד',
|
||||||
|
triggerAsc: 'לחץ על מיון לפי סדר עולה',
|
||||||
|
cancelSort: 'לחץ כדי לבטל את המיון',
|
||||||
},
|
},
|
||||||
Modal: {
|
Modal: {
|
||||||
okText: 'אישור',
|
okText: 'אישור',
|
||||||
@ -41,6 +51,18 @@ const localeValues: Locale = {
|
|||||||
Empty: {
|
Empty: {
|
||||||
description: 'אין מידע',
|
description: 'אין מידע',
|
||||||
},
|
},
|
||||||
|
Icon: {
|
||||||
|
icon: 'סמל',
|
||||||
|
},
|
||||||
|
Text: {
|
||||||
|
edit: 'ערוך',
|
||||||
|
copy: 'העתק',
|
||||||
|
copied: 'הועתק',
|
||||||
|
expand: 'הרחב',
|
||||||
|
},
|
||||||
|
PageHeader: {
|
||||||
|
back: 'חזרה',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default localeValues;
|
export default localeValues;
|
||||||
|
@ -10,6 +10,9 @@ const localeValues: Locale = {
|
|||||||
DatePicker,
|
DatePicker,
|
||||||
TimePicker,
|
TimePicker,
|
||||||
Calendar,
|
Calendar,
|
||||||
|
global: {
|
||||||
|
placeholder: 'Пожалуйста выберите',
|
||||||
|
},
|
||||||
Table: {
|
Table: {
|
||||||
filterTitle: 'Фильтр',
|
filterTitle: 'Фильтр',
|
||||||
filterConfirm: 'OK',
|
filterConfirm: 'OK',
|
||||||
@ -17,6 +20,11 @@ const localeValues: Locale = {
|
|||||||
selectAll: 'Выбрать всё',
|
selectAll: 'Выбрать всё',
|
||||||
selectInvert: 'Инвертировать выбор',
|
selectInvert: 'Инвертировать выбор',
|
||||||
sortTitle: 'Сортировка',
|
sortTitle: 'Сортировка',
|
||||||
|
expand: 'Развернуть строку',
|
||||||
|
collapse: 'Свернуть строку',
|
||||||
|
triggerDesc: 'Нажмите для сортировки по убыванию',
|
||||||
|
triggerAsc: 'Нажмите для сортировки по возрастанию',
|
||||||
|
cancelSort: 'Нажмите, чтобы отменить сортировку',
|
||||||
},
|
},
|
||||||
Modal: {
|
Modal: {
|
||||||
okText: 'OK',
|
okText: 'OK',
|
||||||
@ -42,6 +50,9 @@ const localeValues: Locale = {
|
|||||||
Empty: {
|
Empty: {
|
||||||
description: 'Нет данных',
|
description: 'Нет данных',
|
||||||
},
|
},
|
||||||
|
Icon: {
|
||||||
|
icon: 'иконка',
|
||||||
|
},
|
||||||
Text: {
|
Text: {
|
||||||
edit: 'редактировать',
|
edit: 'редактировать',
|
||||||
copy: 'копировать',
|
copy: 'копировать',
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
|
/* eslint-disable no-template-curly-in-string */
|
||||||
import Pagination from 'rc-pagination/lib/locale/zh_CN';
|
import Pagination from 'rc-pagination/lib/locale/zh_CN';
|
||||||
import DatePicker from '../date-picker/locale/zh_CN';
|
import DatePicker from '../date-picker/locale/zh_CN';
|
||||||
import TimePicker from '../time-picker/locale/zh_CN';
|
import TimePicker from '../time-picker/locale/zh_CN';
|
||||||
import Calendar from '../calendar/locale/zh_CN';
|
import Calendar from '../calendar/locale/zh_CN';
|
||||||
import { Locale } from '../locale-provider';
|
import { Locale } from '../locale-provider';
|
||||||
|
|
||||||
|
const typeTemplate = '${label}不是一个有效的${type}';
|
||||||
|
|
||||||
const localeValues: Locale = {
|
const localeValues: Locale = {
|
||||||
locale: 'zh-cn',
|
locale: 'zh-cn',
|
||||||
Pagination,
|
Pagination,
|
||||||
@ -64,6 +67,55 @@ const localeValues: Locale = {
|
|||||||
PageHeader: {
|
PageHeader: {
|
||||||
back: '返回',
|
back: '返回',
|
||||||
},
|
},
|
||||||
|
Form: {
|
||||||
|
defaultValidateMessages: {
|
||||||
|
default: '字段验证错误${label}',
|
||||||
|
required: '请输入${label}',
|
||||||
|
enum: '${label}必须是其中一个[${enum}]',
|
||||||
|
whitespace: '${label}不能为空字符',
|
||||||
|
date: {
|
||||||
|
format: '${label}日期格式无效',
|
||||||
|
parse: '${label}不能转换为日期',
|
||||||
|
invalid: '${label}是一个无效日期',
|
||||||
|
},
|
||||||
|
types: {
|
||||||
|
string: typeTemplate,
|
||||||
|
method: typeTemplate,
|
||||||
|
array: typeTemplate,
|
||||||
|
object: typeTemplate,
|
||||||
|
number: typeTemplate,
|
||||||
|
date: typeTemplate,
|
||||||
|
boolean: typeTemplate,
|
||||||
|
integer: typeTemplate,
|
||||||
|
float: typeTemplate,
|
||||||
|
regexp: typeTemplate,
|
||||||
|
email: typeTemplate,
|
||||||
|
url: typeTemplate,
|
||||||
|
hex: typeTemplate,
|
||||||
|
},
|
||||||
|
string: {
|
||||||
|
len: '${label}须为${len}个字符',
|
||||||
|
min: '${label}最少${min}个字符',
|
||||||
|
max: '${label}最多${max}个字符',
|
||||||
|
range: '${label}须在${min}-${max}字符之间',
|
||||||
|
},
|
||||||
|
number: {
|
||||||
|
len: '${label}必须等于${len}',
|
||||||
|
min: '${label}最小值为${min}',
|
||||||
|
max: '${label}最大值为${max}',
|
||||||
|
range: '${label}须在${min}-${max}之间',
|
||||||
|
},
|
||||||
|
array: {
|
||||||
|
len: '须为${len}个${label}',
|
||||||
|
min: '最少${min}个${label}',
|
||||||
|
max: '最多${max}个${label}',
|
||||||
|
range: '${label}数量须在${min}-${max}之间',
|
||||||
|
},
|
||||||
|
pattern: {
|
||||||
|
mismatch: '${label}与模式不匹配${pattern}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default localeValues;
|
export default localeValues;
|
||||||
|
@ -126,30 +126,26 @@ exports[`renders ./components/menu/demo/horizontal.md correctly 1`] = `
|
|||||||
role="button"
|
role="button"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="submenu-title-wrapper"
|
aria-label="setting"
|
||||||
|
class="anticon anticon-setting"
|
||||||
|
role="img"
|
||||||
>
|
>
|
||||||
<span
|
<svg
|
||||||
aria-label="setting"
|
aria-hidden="true"
|
||||||
class="anticon anticon-setting"
|
class=""
|
||||||
role="img"
|
data-icon="setting"
|
||||||
|
fill="currentColor"
|
||||||
|
focusable="false"
|
||||||
|
height="1em"
|
||||||
|
viewBox="64 64 896 896"
|
||||||
|
width="1em"
|
||||||
>
|
>
|
||||||
<svg
|
<path
|
||||||
aria-hidden="true"
|
d="M924.8 625.7l-65.5-56c3.1-19 4.7-38.4 4.7-57.8s-1.6-38.8-4.7-57.8l65.5-56a32.03 32.03 0 009.3-35.2l-.9-2.6a443.74 443.74 0 00-79.7-137.9l-1.8-2.1a32.12 32.12 0 00-35.1-9.5l-81.3 28.9c-30-24.6-63.5-44-99.7-57.6l-15.7-85a32.05 32.05 0 00-25.8-25.7l-2.7-.5c-52.1-9.4-106.9-9.4-159 0l-2.7.5a32.05 32.05 0 00-25.8 25.7l-15.8 85.4a351.86 351.86 0 00-99 57.4l-81.9-29.1a32 32 0 00-35.1 9.5l-1.8 2.1a446.02 446.02 0 00-79.7 137.9l-.9 2.6c-4.5 12.5-.8 26.5 9.3 35.2l66.3 56.6c-3.1 18.8-4.6 38-4.6 57.1 0 19.2 1.5 38.4 4.6 57.1L99 625.5a32.03 32.03 0 00-9.3 35.2l.9 2.6c18.1 50.4 44.9 96.9 79.7 137.9l1.8 2.1a32.12 32.12 0 0035.1 9.5l81.9-29.1c29.8 24.5 63.1 43.9 99 57.4l15.8 85.4a32.05 32.05 0 0025.8 25.7l2.7.5a449.4 449.4 0 00159 0l2.7-.5a32.05 32.05 0 0025.8-25.7l15.7-85a350 350 0 0099.7-57.6l81.3 28.9a32 32 0 0035.1-9.5l1.8-2.1c34.8-41.1 61.6-87.5 79.7-137.9l.9-2.6c4.5-12.3.8-26.3-9.3-35zM788.3 465.9c2.5 15.1 3.8 30.6 3.8 46.1s-1.3 31-3.8 46.1l-6.6 40.1 74.7 63.9a370.03 370.03 0 01-42.6 73.6L721 702.8l-31.4 25.8c-23.9 19.6-50.5 35-79.3 45.8l-38.1 14.3-17.9 97a377.5 377.5 0 01-85 0l-17.9-97.2-37.8-14.5c-28.5-10.8-55-26.2-78.7-45.7l-31.4-25.9-93.4 33.2c-17-22.9-31.2-47.6-42.6-73.6l75.5-64.5-6.5-40c-2.4-14.9-3.7-30.3-3.7-45.5 0-15.3 1.2-30.6 3.7-45.5l6.5-40-75.5-64.5c11.3-26.1 25.6-50.7 42.6-73.6l93.4 33.2 31.4-25.9c23.7-19.5 50.2-34.9 78.7-45.7l37.9-14.3 17.9-97.2c28.1-3.2 56.8-3.2 85 0l17.9 97 38.1 14.3c28.7 10.8 55.4 26.2 79.3 45.8l31.4 25.8 92.8-32.9c17 22.9 31.2 47.6 42.6 73.6L781.8 426l6.5 39.9zM512 326c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm79.2 255.2A111.6 111.6 0 01512 614c-29.9 0-58-11.7-79.2-32.8A111.6 111.6 0 01400 502c0-29.9 11.7-58 32.8-79.2C454 401.6 482.1 390 512 390c29.9 0 58 11.6 79.2 32.8A111.6 111.6 0 01624 502c0 29.9-11.7 58-32.8 79.2z"
|
||||||
class=""
|
/>
|
||||||
data-icon="setting"
|
</svg>
|
||||||
fill="currentColor"
|
|
||||||
focusable="false"
|
|
||||||
height="1em"
|
|
||||||
viewBox="64 64 896 896"
|
|
||||||
width="1em"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M924.8 625.7l-65.5-56c3.1-19 4.7-38.4 4.7-57.8s-1.6-38.8-4.7-57.8l65.5-56a32.03 32.03 0 009.3-35.2l-.9-2.6a443.74 443.74 0 00-79.7-137.9l-1.8-2.1a32.12 32.12 0 00-35.1-9.5l-81.3 28.9c-30-24.6-63.5-44-99.7-57.6l-15.7-85a32.05 32.05 0 00-25.8-25.7l-2.7-.5c-52.1-9.4-106.9-9.4-159 0l-2.7.5a32.05 32.05 0 00-25.8 25.7l-15.8 85.4a351.86 351.86 0 00-99 57.4l-81.9-29.1a32 32 0 00-35.1 9.5l-1.8 2.1a446.02 446.02 0 00-79.7 137.9l-.9 2.6c-4.5 12.5-.8 26.5 9.3 35.2l66.3 56.6c-3.1 18.8-4.6 38-4.6 57.1 0 19.2 1.5 38.4 4.6 57.1L99 625.5a32.03 32.03 0 00-9.3 35.2l.9 2.6c18.1 50.4 44.9 96.9 79.7 137.9l1.8 2.1a32.12 32.12 0 0035.1 9.5l81.9-29.1c29.8 24.5 63.1 43.9 99 57.4l15.8 85.4a32.05 32.05 0 0025.8 25.7l2.7.5a449.4 449.4 0 00159 0l2.7-.5a32.05 32.05 0 0025.8-25.7l15.7-85a350 350 0 0099.7-57.6l81.3 28.9a32 32 0 0035.1-9.5l1.8-2.1c34.8-41.1 61.6-87.5 79.7-137.9l.9-2.6c4.5-12.3.8-26.3-9.3-35zM788.3 465.9c2.5 15.1 3.8 30.6 3.8 46.1s-1.3 31-3.8 46.1l-6.6 40.1 74.7 63.9a370.03 370.03 0 01-42.6 73.6L721 702.8l-31.4 25.8c-23.9 19.6-50.5 35-79.3 45.8l-38.1 14.3-17.9 97a377.5 377.5 0 01-85 0l-17.9-97.2-37.8-14.5c-28.5-10.8-55-26.2-78.7-45.7l-31.4-25.9-93.4 33.2c-17-22.9-31.2-47.6-42.6-73.6l75.5-64.5-6.5-40c-2.4-14.9-3.7-30.3-3.7-45.5 0-15.3 1.2-30.6 3.7-45.5l6.5-40-75.5-64.5c11.3-26.1 25.6-50.7 42.6-73.6l93.4 33.2 31.4-25.9c23.7-19.5 50.2-34.9 78.7-45.7l37.9-14.3 17.9-97.2c28.1-3.2 56.8-3.2 85 0l17.9 97 38.1 14.3c28.7 10.8 55.4 26.2 79.3 45.8l31.4 25.8 92.8-32.9c17 22.9 31.2 47.6 42.6 73.6L781.8 426l6.5 39.9zM512 326c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm79.2 255.2A111.6 111.6 0 01512 614c-29.9 0-58-11.7-79.2-32.8A111.6 111.6 0 01400 502c0-29.9 11.7-58 32.8-79.2C454 401.6 482.1 390 512 390c29.9 0 58 11.6 79.2 32.8A111.6 111.6 0 01624 502c0 29.9-11.7 58-32.8 79.2z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
Navigation Three - Submenu
|
|
||||||
</span>
|
</span>
|
||||||
|
Navigation Three - Submenu
|
||||||
<i
|
<i
|
||||||
class="ant-menu-submenu-arrow"
|
class="ant-menu-submenu-arrow"
|
||||||
/>
|
/>
|
||||||
|
@ -15,11 +15,7 @@ Horizontal top navigation menu.
|
|||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { Menu } from 'antd';
|
import { Menu } from 'antd';
|
||||||
import {
|
import { MailOutlined, AppstoreOutlined, SettingOutlined } from '@ant-design/icons';
|
||||||
MailOutlined,
|
|
||||||
AppstoreOutlined,
|
|
||||||
SettingOutlined,
|
|
||||||
} from '@ant-design/icons';
|
|
||||||
|
|
||||||
const { SubMenu } = Menu;
|
const { SubMenu } = Menu;
|
||||||
|
|
||||||
@ -48,10 +44,10 @@ class App extends React.Component {
|
|||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<SubMenu
|
<SubMenu
|
||||||
title={
|
title={
|
||||||
<span className="submenu-title-wrapper">
|
<>
|
||||||
<SettingOutlined />
|
<SettingOutlined />
|
||||||
Navigation Three - Submenu
|
Navigation Three - Submenu
|
||||||
</span>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Menu.ItemGroup title="Item 1">
|
<Menu.ItemGroup title="Item 1">
|
||||||
|
@ -210,10 +210,6 @@
|
|||||||
z-index: @zindex-dropdown;
|
z-index: @zindex-dropdown;
|
||||||
border-radius: @border-radius-base;
|
border-radius: @border-radius-base;
|
||||||
|
|
||||||
.submenu-title-wrapper {
|
|
||||||
padding-right: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -7px;
|
top: -7px;
|
||||||
|
@ -22,6 +22,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-dark {
|
||||||
|
.@{menu-prefix-cls}-inline,
|
||||||
|
.@{menu-prefix-cls}-vertical {
|
||||||
|
.@{menu-prefix-cls}-rtl& {
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-vertical&-sub,
|
&-vertical&-sub,
|
||||||
&-vertical-left&-sub,
|
&-vertical-left&-sub,
|
||||||
&-vertical-right&-sub {
|
&-vertical-right&-sub {
|
||||||
@ -56,15 +65,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-submenu {
|
&-submenu {
|
||||||
&-popup {
|
|
||||||
.submenu-title-wrapper {
|
|
||||||
.@{menu-prefix-cls}-submenu-rtl& {
|
|
||||||
padding-right: 0;
|
|
||||||
padding-left: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-vertical,
|
&-vertical,
|
||||||
&-vertical-left,
|
&-vertical-left,
|
||||||
&-vertical-right,
|
&-vertical-right,
|
||||||
@ -134,6 +134,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-inline-collapsed&-vertical {
|
||||||
|
.@{menu-prefix-cls}-submenu-title {
|
||||||
|
.@{menu-prefix-cls}-rtl& {
|
||||||
|
padding: 0 (@menu-collapsed-width - @menu-icon-size-lg) / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-item-group-list {
|
&-item-group-list {
|
||||||
.@{menu-prefix-cls}-item,
|
.@{menu-prefix-cls}-item,
|
||||||
.@{menu-prefix-cls}-submenu-title {
|
.@{menu-prefix-cls}-submenu-title {
|
||||||
|
@ -35,55 +35,59 @@ export default class ActionButton extends React.Component<ActionButtonProps, Act
|
|||||||
clearTimeout(this.timeoutId);
|
clearTimeout(this.timeoutId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handlePromiseOnOk(returnValueOfOnOk?: PromiseLike<any>) {
|
||||||
|
const { closeModal } = this.props;
|
||||||
|
if (!returnValueOfOnOk || !returnValueOfOnOk.then) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setState({ loading: true });
|
||||||
|
returnValueOfOnOk.then(
|
||||||
|
(...args: any[]) => {
|
||||||
|
// It's unnecessary to set loading=false, for the Modal will be unmounted after close.
|
||||||
|
// this.setState({ loading: false });
|
||||||
|
closeModal(...args);
|
||||||
|
},
|
||||||
|
(e: Error) => {
|
||||||
|
// Emit error when catch promise reject
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(e);
|
||||||
|
// See: https://github.com/ant-design/ant-design/issues/6183
|
||||||
|
this.setState({ loading: false });
|
||||||
|
this.clicked = false;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
onClick = () => {
|
onClick = () => {
|
||||||
const { actionFn, closeModal } = this.props;
|
const { actionFn, closeModal } = this.props;
|
||||||
if (this.clicked) {
|
if (this.clicked) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.clicked = true;
|
this.clicked = true;
|
||||||
if (actionFn) {
|
if (!actionFn) {
|
||||||
let ret;
|
|
||||||
if (actionFn.length) {
|
|
||||||
ret = actionFn(closeModal);
|
|
||||||
} else {
|
|
||||||
ret = actionFn();
|
|
||||||
if (!ret) {
|
|
||||||
closeModal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ret && ret.then) {
|
|
||||||
this.setState({ loading: true });
|
|
||||||
ret.then(
|
|
||||||
(...args: any[]) => {
|
|
||||||
// It's unnecessary to set loading=false, for the Modal will be unmounted after close.
|
|
||||||
// this.setState({ loading: false });
|
|
||||||
closeModal(...args);
|
|
||||||
},
|
|
||||||
(e: Error) => {
|
|
||||||
// Emit error when catch promise reject
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error(e);
|
|
||||||
// See: https://github.com/ant-design/ant-design/issues/6183
|
|
||||||
this.setState({ loading: false });
|
|
||||||
this.clicked = false;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
closeModal();
|
closeModal();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
let returnValueOfOnOk;
|
||||||
|
if (actionFn.length) {
|
||||||
|
returnValueOfOnOk = actionFn(closeModal);
|
||||||
|
// https://github.com/ant-design/ant-design/issues/23358
|
||||||
|
this.clicked = false;
|
||||||
|
} else {
|
||||||
|
returnValueOfOnOk = actionFn();
|
||||||
|
if (!returnValueOfOnOk) {
|
||||||
|
closeModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.handlePromiseOnOk(returnValueOfOnOk);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { type, children, buttonProps } = this.props;
|
const { type, children, buttonProps } = this.props;
|
||||||
const { loading } = this.state;
|
const { loading } = this.state;
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button type={type} onClick={this.onClick} loading={loading} {...buttonProps}>
|
||||||
type={type}
|
|
||||||
onClick={this.onClick}
|
|
||||||
loading={loading}
|
|
||||||
{...buttonProps}
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
@ -9,6 +9,7 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
|||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
errorSpy.mockReset();
|
errorSpy.mockReset();
|
||||||
document.body.innerHTML = '';
|
document.body.innerHTML = '';
|
||||||
|
Modal.destroyAll();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
@ -74,12 +75,19 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
|||||||
expect(errorSpy).not.toHaveBeenCalled();
|
expect(errorSpy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not hide confirm when onOk return Promise.resolve', () => {
|
||||||
|
open({
|
||||||
|
onOk: () => Promise.resolve(''),
|
||||||
|
});
|
||||||
|
$$('.ant-btn-primary')[0].click();
|
||||||
|
expect($$('.ant-modal-confirm')).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
it('should emit error when onOk return Promise.reject', () => {
|
it('should emit error when onOk return Promise.reject', () => {
|
||||||
const error = new Error('something wrong');
|
const error = new Error('something wrong');
|
||||||
open({
|
open({
|
||||||
onOk: () => Promise.reject(error),
|
onOk: () => Promise.reject(error),
|
||||||
});
|
});
|
||||||
// Fifth Modal
|
|
||||||
$$('.ant-btn-primary')[0].click();
|
$$('.ant-btn-primary')[0].click();
|
||||||
// wait promise
|
// wait promise
|
||||||
return Promise.resolve().then(() => {
|
return Promise.resolve().then(() => {
|
||||||
@ -125,6 +133,22 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
|||||||
jest.useRealTimers();
|
jest.useRealTimers();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not close modals when click confirm button when onOk has argument', () => {
|
||||||
|
jest.useFakeTimers();
|
||||||
|
['info', 'success', 'warning', 'error'].forEach(type => {
|
||||||
|
Modal[type]({
|
||||||
|
title: 'title',
|
||||||
|
content: 'content',
|
||||||
|
onOk: close => null, // eslint-disable-line no-unused-vars
|
||||||
|
});
|
||||||
|
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(1);
|
||||||
|
$$('.ant-btn')[0].click();
|
||||||
|
jest.runAllTimers();
|
||||||
|
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(1);
|
||||||
|
});
|
||||||
|
jest.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
it('could be update', () => {
|
it('could be update', () => {
|
||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
['info', 'success', 'warning', 'error'].forEach(type => {
|
['info', 'success', 'warning', 'error'].forEach(type => {
|
||||||
@ -235,9 +259,23 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
|||||||
it('ok button should trigger onOk once when click it many times quickly', () => {
|
it('ok button should trigger onOk once when click it many times quickly', () => {
|
||||||
const onOk = jest.fn();
|
const onOk = jest.fn();
|
||||||
open({ onOk });
|
open({ onOk });
|
||||||
// Fifth Modal
|
|
||||||
$$('.ant-btn-primary')[0].click();
|
$$('.ant-btn-primary')[0].click();
|
||||||
$$('.ant-btn-primary')[0].click();
|
$$('.ant-btn-primary')[0].click();
|
||||||
expect(onOk).toHaveBeenCalledTimes(1);
|
expect(onOk).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// https://github.com/ant-design/ant-design/issues/23358
|
||||||
|
it('ok button should trigger onOk multiple times when onOk has close argument', () => {
|
||||||
|
const onOk = jest.fn();
|
||||||
|
open({
|
||||||
|
onOk: close => {
|
||||||
|
onOk();
|
||||||
|
(() => {})(close); // do nothing
|
||||||
|
},
|
||||||
|
});
|
||||||
|
$$('.ant-btn-primary')[0].click();
|
||||||
|
$$('.ant-btn-primary')[0].click();
|
||||||
|
$$('.ant-btn-primary')[0].click();
|
||||||
|
expect(onOk).toHaveBeenCalledTimes(3);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -75,8 +75,8 @@ The items listed above are all functions, expecting a settings object as paramet
|
|||||||
| title | Title | string\|ReactNode | - |
|
| title | Title | string\|ReactNode | - |
|
||||||
| width | Width of the modal dialog | string\|number | 416 |
|
| width | Width of the modal dialog | string\|number | 416 |
|
||||||
| zIndex | The `z-index` of the Modal | Number | 1000 |
|
| zIndex | The `z-index` of the Modal | Number | 1000 |
|
||||||
| onCancel | Specify a function that will be called when the user clicks the Cancel button. The parameter of this function is a function whose execution should include closing the dialog. You can also just return a promise and when the promise is resolved, the modal dialog will also be closed | function | - |
|
| onCancel | Specify a function that will be called when the user clicks the Cancel button. The parameter of this function is a function whose execution should include closing the dialog. You can also just return a promise and when the promise is resolved, the modal dialog will also be closed | function(close) | - |
|
||||||
| onOk | Specify a function that will be called when the user clicks the OK button. The parameter of this function is a function whose execution should include closing the dialog. You can also just return a promise and when the promise is resolved, the modal dialog will also be closed | function | - |
|
| onOk | Specify a function that will be called when the user clicks the OK button. The parameter of this function is a function whose execution should include closing the dialog. You can also just return a promise and when the promise is resolved, the modal dialog will also be closed | function(close) | - |
|
||||||
|
|
||||||
All the `Modal.method`s will return a reference, and then we can update and close the modal dialog by the reference.
|
All the `Modal.method`s will return a reference, and then we can update and close the modal dialog by the reference.
|
||||||
|
|
||||||
|
@ -77,8 +77,8 @@ title: Modal
|
|||||||
| title | 标题 | string\|ReactNode | - |
|
| title | 标题 | string\|ReactNode | - |
|
||||||
| width | 宽度 | string\|number | 416 |
|
| width | 宽度 | string\|number | 416 |
|
||||||
| zIndex | 设置 Modal 的 `z-index` | Number | 1000 |
|
| zIndex | 设置 Modal 的 `z-index` | Number | 1000 |
|
||||||
| onCancel | 取消回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | function | - |
|
| onCancel | 取消回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | function(close) | - |
|
||||||
| onOk | 点击确定回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | function | - |
|
| onOk | 点击确定回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | function(close) | - |
|
||||||
|
|
||||||
以上函数调用后,会返回一个引用,可以通过该引用更新和关闭弹窗。
|
以上函数调用后,会返回一个引用,可以通过该引用更新和关闭弹窗。
|
||||||
|
|
||||||
|
@ -4,11 +4,26 @@ import PageHeader from '..';
|
|||||||
import ConfigProvider from '../../config-provider';
|
import ConfigProvider from '../../config-provider';
|
||||||
import mountTest from '../../../tests/shared/mountTest';
|
import mountTest from '../../../tests/shared/mountTest';
|
||||||
import rtlTest from '../../../tests/shared/rtlTest';
|
import rtlTest from '../../../tests/shared/rtlTest';
|
||||||
|
import { spyElementPrototypes } from '../../__tests__/util/domHook';
|
||||||
|
|
||||||
describe('PageHeader', () => {
|
describe('PageHeader', () => {
|
||||||
mountTest(PageHeader);
|
mountTest(PageHeader);
|
||||||
rtlTest(PageHeader);
|
rtlTest(PageHeader);
|
||||||
|
|
||||||
|
let spy;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
spy = spyElementPrototypes(HTMLElement, {
|
||||||
|
getBoundingClientRect: () => ({
|
||||||
|
width: 100,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
spy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
it('pageHeader should not contain back it back', () => {
|
it('pageHeader should not contain back it back', () => {
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
@ -101,4 +116,11 @@ describe('PageHeader', () => {
|
|||||||
|
|
||||||
expect(render(wrapper)).toMatchSnapshot();
|
expect(render(wrapper)).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('change container width', () => {
|
||||||
|
const wrapper = mount(<PageHeader title="Page Title" extra="extra" />);
|
||||||
|
wrapper.triggerResize();
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('.ant-page-header').hasClass('ant-page-header-compact')).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -21,9 +21,9 @@ PageHeader can be used to highlight the page topic, display important informatio
|
|||||||
| ghost | PageHeader type, will change background color | boolean | true | |
|
| ghost | PageHeader type, will change background color | boolean | true | |
|
||||||
| avatar | Avatar next to the title bar | [avatar props](/components/avatar/) | - | |
|
| avatar | Avatar next to the title bar | [avatar props](/components/avatar/) | - | |
|
||||||
| backIcon | Custom back icon, if false the back icon will not be displayed | ReactNode \| boolean | `<ArrowLeft />` | |
|
| backIcon | Custom back icon, if false the back icon will not be displayed | ReactNode \| boolean | `<ArrowLeft />` | |
|
||||||
| tags | Tag list next to title | [Tag](https://ant.design/components/tag-cn/)[] \| [Tag](https://ant.design/components/tag-cn/) | - | |
|
| tags | Tag list next to title | [Tag](/components/tag/)[] \| [Tag](/components/tag/) | - | |
|
||||||
| extra | Operating area, at the end of the line of the title line | ReactNode | - | |
|
| extra | Operating area, at the end of the line of the title line | ReactNode | - | |
|
||||||
| breadcrumb | Breadcrumb configuration | [breadcrumb](https://ant.design/components/breadcrumb-cn/) | - | |
|
| breadcrumb | Breadcrumb configuration | [breadcrumb](/components/breadcrumb/) | - | |
|
||||||
| footer | PageHeader's footer, generally used to render TabBar | ReactNode | - | |
|
| footer | PageHeader's footer, generally used to render TabBar | ReactNode | - | |
|
||||||
| onBack | Back icon click event | `()=>void` | `()=>history.back()` | |
|
| onBack | Back icon click event | `()=>void` | `()=>history.back()` | |
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ const renderChildren = (prefixCls: string, children: React.ReactNode) => {
|
|||||||
const PageHeader: React.FC<PageHeaderProps> = props => {
|
const PageHeader: React.FC<PageHeaderProps> = props => {
|
||||||
const [compact, updateCompact] = React.useState(false);
|
const [compact, updateCompact] = React.useState(false);
|
||||||
const onResize = ({ width }: { width: number }) => {
|
const onResize = ({ width }: { width: number }) => {
|
||||||
updateCompact(width < 540);
|
updateCompact(width < 768);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<ConfigConsumer>
|
<ConfigConsumer>
|
||||||
|
@ -21,9 +21,9 @@ subtitle: 页头
|
|||||||
| ghost | pageHeader 的类型,将会改变背景颜色 | boolean | true | |
|
| ghost | pageHeader 的类型,将会改变背景颜色 | boolean | true | |
|
||||||
| avatar | 标题栏旁的头像 | [avatar props](/components/avatar/) | - | |
|
| avatar | 标题栏旁的头像 | [avatar props](/components/avatar/) | - | |
|
||||||
| backIcon | 自定义 back icon ,如果为 false 不渲染 back icon | ReactNode \| boolean | `<ArrowLeft />` | |
|
| backIcon | 自定义 back icon ,如果为 false 不渲染 back icon | ReactNode \| boolean | `<ArrowLeft />` | |
|
||||||
| tags | title 旁的 tag 列表 | [Tag](https://ant.design/components/tag-cn/)[] \| [Tag](https://ant.design/components/tag-cn/) | - | |
|
| tags | title 旁的 tag 列表 | [Tag](/components/tag/)[] \| [Tag](/components/tag/) | - | |
|
||||||
| extra | 操作区,位于 title 行的行尾 | ReactNode | - | |
|
| extra | 操作区,位于 title 行的行尾 | ReactNode | - | |
|
||||||
| breadcrumb | 面包屑的配置 | [breadcrumb](https://ant.design/components/breadcrumb-cn/) | - | |
|
| breadcrumb | 面包屑的配置 | [breadcrumb](/components/breadcrumb/) | - | |
|
||||||
| footer | PageHeader 的页脚,一般用于渲染 TabBar | ReactNode | - | |
|
| footer | PageHeader 的页脚,一般用于渲染 TabBar | ReactNode | - | |
|
||||||
| onBack | 返回按钮的点击事件 | `()=>void` | `()=>history.back()` | |
|
| onBack | 返回按钮的点击事件 | `()=>void` | `()=>history.back()` | |
|
||||||
|
|
||||||
|
@ -56,6 +56,7 @@
|
|||||||
&-left {
|
&-left {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin: (@margin-xs / 2) 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +83,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-extra {
|
&-extra {
|
||||||
|
margin: (@margin-xs / 2) 0;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
margin-left: @margin-sm;
|
margin-left: @margin-sm;
|
||||||
white-space: unset;
|
white-space: unset;
|
||||||
@ -112,11 +115,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-compact &-heading {
|
&-compact &-heading {
|
||||||
flex-direction: column;
|
flex-wrap: wrap;
|
||||||
|
|
||||||
&-extra {
|
|
||||||
margin-top: @margin-xs;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ The difference with the `confirm` modal dialog is that it's more lightweight tha
|
|||||||
| icon | customize icon of confirmation | ReactNode | `<ExclamationCircle />` |
|
| icon | customize icon of confirmation | ReactNode | `<ExclamationCircle />` |
|
||||||
| disabled | is show popconfirm when click its childrenNode | boolean | false |
|
| disabled | is show popconfirm when click its childrenNode | boolean | false |
|
||||||
|
|
||||||
Consult [Tooltip's documentation](https://ant.design/components/tooltip/#API) to find more APIs.
|
Consult [Tooltip's documentation](/components/tooltip/#API) to find more APIs.
|
||||||
|
|
||||||
## Note
|
## Note
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ Comparing with `Tooltip`, besides information `Popover` card can also provide ac
|
|||||||
| content | Content of the card | string\|ReactNode\|() => ReactNode | - | |
|
| content | Content of the card | string\|ReactNode\|() => ReactNode | - | |
|
||||||
| title | Title of the card | string\|ReactNode\|() => ReactNode | - | |
|
| title | Title of the card | string\|ReactNode\|() => ReactNode | - | |
|
||||||
|
|
||||||
Consult [Tooltip's documentation](https://ant.design/components/tooltip/#API) to find more APIs.
|
Consult [Tooltip's documentation](/components/tooltip/#API) to find more APIs.
|
||||||
|
|
||||||
## Note
|
## Note
|
||||||
|
|
||||||
|
@ -34,9 +34,9 @@ Select component to select value from options.
|
|||||||
| dropdownRender | Customize dropdown content | (menuNode: ReactNode, props) => ReactNode | - | |
|
| dropdownRender | Customize dropdown content | (menuNode: ReactNode, props) => ReactNode | - | |
|
||||||
| dropdownStyle | style of dropdown menu | object | - | |
|
| dropdownStyle | style of dropdown menu | object | - | |
|
||||||
| filterOption | If true, filter options by input, if function, filter options against it. The function will receive two arguments, `inputValue` and `option`, if the function returns `true`, the option will be included in the filtered set; Otherwise, it will be excluded. | boolean or function(inputValue, option) | true | |
|
| filterOption | If true, filter options by input, if function, filter options against it. The function will receive two arguments, `inputValue` and `option`, if the function returns `true`, the option will be included in the filtered set; Otherwise, it will be excluded. | boolean or function(inputValue, option) | true | |
|
||||||
| firstActiveValue | Value of action option by default | string\|string\[] | - | |
|
|
||||||
| getPopupContainer | Parent Node which the selector should be rendered to. Default to `body`. When position issues happen, try to modify it into scrollable content and position it relative. [Example](https://codesandbox.io/s/4j168r7jw0) | function(triggerNode) | () => document.body | |
|
| getPopupContainer | Parent Node which the selector should be rendered to. Default to `body`. When position issues happen, try to modify it into scrollable content and position it relative. [Example](https://codesandbox.io/s/4j168r7jw0) | function(triggerNode) | () => document.body | |
|
||||||
| labelInValue | whether to embed label in value, turn the format of value from `string` to `{key: string, label: ReactNode}` | boolean | false | |
|
| labelInValue | whether to embed label in value, turn the format of value from `string` to `{key: string, label: ReactNode}` | boolean | false | |
|
||||||
|
| listHeight | Config popup height | number | 256 | |
|
||||||
| maxTagCount | Max tag count to show | number | - | |
|
| maxTagCount | Max tag count to show | number | - | |
|
||||||
| maxTagTextLength | Max tag text length to show | number | - | |
|
| maxTagTextLength | Max tag text length to show | number | - | |
|
||||||
| maxTagPlaceholder | Placeholder for not showing tags | ReactNode/function(omittedValues) | - | |
|
| maxTagPlaceholder | Placeholder for not showing tags | ReactNode/function(omittedValues) | - | |
|
||||||
|
@ -35,9 +35,9 @@ title: Select
|
|||||||
| dropdownRender | 自定义下拉框内容 | (menuNode: ReactNode, props) => ReactNode | - | |
|
| dropdownRender | 自定义下拉框内容 | (menuNode: ReactNode, props) => ReactNode | - | |
|
||||||
| dropdownStyle | 下拉菜单的 style 属性 | object | - | |
|
| dropdownStyle | 下拉菜单的 style 属性 | object | - | |
|
||||||
| filterOption | 是否根据输入项进行筛选。当其为一个函数时,会接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 `true`,反之则返回 `false`。 | boolean or function(inputValue, option) | true | |
|
| filterOption | 是否根据输入项进行筛选。当其为一个函数时,会接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 `true`,反之则返回 `false`。 | boolean or function(inputValue, option) | true | |
|
||||||
| firstActiveValue | 默认高亮的选项 | string\|string\[] | - | |
|
|
||||||
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。[示例](https://codesandbox.io/s/4j168r7jw0) | Function(triggerNode) | () => document.body | |
|
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。[示例](https://codesandbox.io/s/4j168r7jw0) | Function(triggerNode) | () => document.body | |
|
||||||
| labelInValue | 是否把每个选项的 label 包装到 value 中,会把 Select 的 value 类型从 `string` 变为 `{key: string, label: ReactNode}` 的格式 | boolean | false | |
|
| labelInValue | 是否把每个选项的 label 包装到 value 中,会把 Select 的 value 类型从 `string` 变为 `{key: string, label: ReactNode}` 的格式 | boolean | false | |
|
||||||
|
| listHeight | 设置弹窗滚动高度 | number | 256 | |
|
||||||
| maxTagCount | 最多显示多少个 tag | number | - | |
|
| maxTagCount | 最多显示多少个 tag | number | - | |
|
||||||
| maxTagTextLength | 最大显示的 tag 文本长度 | number | - | |
|
| maxTagTextLength | 最大显示的 tag 文本长度 | number | - | |
|
||||||
| maxTagPlaceholder | 隐藏 tag 时显示的内容 | ReactNode/function(omittedValues) | - | |
|
| maxTagPlaceholder | 隐藏 tag 时显示的内容 | ReactNode/function(omittedValues) | - | |
|
||||||
|
@ -98,7 +98,6 @@
|
|||||||
// ========================== Arrow ==========================
|
// ========================== Arrow ==========================
|
||||||
&-arrow {
|
&-arrow {
|
||||||
.iconfont-mixin();
|
.iconfont-mixin();
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 53%;
|
top: 53%;
|
||||||
right: @control-padding-horizontal - 1px;
|
right: @control-padding-horizontal - 1px;
|
||||||
@ -109,8 +108,6 @@
|
|||||||
font-size: @font-size-sm;
|
font-size: @font-size-sm;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
// transform-origin: 50% 50%;
|
|
||||||
pointer-events: none;
|
|
||||||
|
|
||||||
.anticon {
|
.anticon {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
@ -124,6 +121,10 @@
|
|||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.anticon-down {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================== Clear ==========================
|
// ========================== Clear ==========================
|
||||||
|
@ -13,170 +13,178 @@
|
|||||||
* since chrome may update to redesign with its align logic.
|
* since chrome may update to redesign with its align logic.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.@{select-prefix-cls}-multiple {
|
.@{select-prefix-cls} {
|
||||||
// ========================= Selector =========================
|
&-multiple {
|
||||||
.@{select-prefix-cls}-selector {
|
// ========================= Selector =========================
|
||||||
.select-selector();
|
.@{select-prefix-cls}-selector {
|
||||||
.select-search-input-without-border();
|
.select-selector();
|
||||||
|
.select-search-input-without-border();
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
// Multiple is little different that horizontal is follow the vertical
|
// Multiple is little different that horizontal is follow the vertical
|
||||||
padding: @select-multiple-padding @input-padding-vertical-base;
|
padding: @select-multiple-padding @input-padding-vertical-base;
|
||||||
|
|
||||||
.@{select-prefix-cls}-show-search& {
|
.@{select-prefix-cls}-show-search& {
|
||||||
cursor: text;
|
cursor: text;
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
display: inline-block;
|
|
||||||
width: 0;
|
|
||||||
margin: @select-multiple-item-spacing-half 0;
|
|
||||||
line-height: @select-multiple-item-height;
|
|
||||||
content: '\a0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.@{select-prefix-cls}-allow-clear .@{select-prefix-cls}-selector {
|
|
||||||
padding-right: @font-size-sm + @control-padding-horizontal;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ======================== Selections ========================
|
|
||||||
.@{select-prefix-cls}-selection-item {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex: none;
|
|
||||||
box-sizing: border-box;
|
|
||||||
max-width: 100%;
|
|
||||||
|
|
||||||
height: @select-multiple-item-height;
|
|
||||||
margin-top: @select-multiple-item-spacing-half;
|
|
||||||
margin-right: @input-padding-vertical-base;
|
|
||||||
margin-bottom: @select-multiple-item-spacing-half;
|
|
||||||
padding: 0 (@padding-xs / 2) 0 @padding-xs;
|
|
||||||
line-height: @select-multiple-item-height - @select-multiple-item-border-width * 2;
|
|
||||||
background: @select-selection-item-bg;
|
|
||||||
border: 1px solid @select-selection-item-border-color;
|
|
||||||
border-radius: @border-radius-base;
|
|
||||||
cursor: default;
|
|
||||||
transition: font-size 0.3s, line-height 0.3s, height 0.3s;
|
|
||||||
user-select: none;
|
|
||||||
|
|
||||||
// It's ok not to do this, but 24px makes bottom narrow in view should adjust
|
|
||||||
&-content {
|
|
||||||
display: inline-block;
|
|
||||||
margin-right: @padding-xs / 2;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-remove {
|
|
||||||
.iconfont-mixin();
|
|
||||||
|
|
||||||
display: inline-block;
|
|
||||||
color: @text-color-secondary;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: @font-size-sm;
|
|
||||||
line-height: inherit;
|
|
||||||
cursor: pointer;
|
|
||||||
.iconfont-size-under-12px(10px);
|
|
||||||
&:hover {
|
|
||||||
color: @icon-color-hover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========================== Input ==========================
|
|
||||||
.@{select-prefix-cls}-selection-search {
|
|
||||||
position: relative;
|
|
||||||
margin-left: @select-multiple-padding / 2;
|
|
||||||
|
|
||||||
&-input,
|
|
||||||
&-mirror {
|
|
||||||
font-family: @font-family;
|
|
||||||
line-height: @line-height-base;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-input {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-mirror {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: 999;
|
|
||||||
white-space: nowrap;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/ant-design/ant-design/issues/22906
|
|
||||||
&:first-child .@{select-prefix-cls}-selection-search-input {
|
|
||||||
margin-left: 6.5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ======================= Placeholder =======================
|
|
||||||
.@{select-prefix-cls}-selection-placeholder {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
right: @input-padding-horizontal;
|
|
||||||
left: @input-padding-horizontal;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// == Size ==
|
|
||||||
// ============================================================
|
|
||||||
.select-size(@suffix, @input-height) {
|
|
||||||
@merged-cls: ~'@{select-prefix-cls}-@{suffix}';
|
|
||||||
&.@{merged-cls} {
|
|
||||||
@select-selection-height: @input-height - @input-padding-vertical-base * 2;
|
|
||||||
@select-height-without-border: @input-height - @border-width-base * 2;
|
|
||||||
|
|
||||||
.@{select-prefix-cls}-selector::after {
|
|
||||||
line-height: @select-selection-height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.@{select-prefix-cls}-selection-item {
|
&::after {
|
||||||
height: @select-selection-height;
|
display: inline-block;
|
||||||
line-height: @select-selection-height - @border-width-base * 2;
|
width: 0;
|
||||||
|
margin: @select-multiple-item-spacing-half 0;
|
||||||
|
line-height: @select-multiple-item-height;
|
||||||
|
content: '\a0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.@{select-prefix-cls}-allow-clear .@{select-prefix-cls}-selector {
|
||||||
|
padding-right: @font-size-sm + @control-padding-horizontal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== Selections ========================
|
||||||
|
.@{select-prefix-cls}-selection-item {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
|
height: @select-multiple-item-height;
|
||||||
|
margin-top: @select-multiple-item-spacing-half;
|
||||||
|
margin-right: @input-padding-vertical-base;
|
||||||
|
margin-bottom: @select-multiple-item-spacing-half;
|
||||||
|
padding: 0 (@padding-xs / 2) 0 @padding-xs;
|
||||||
|
line-height: @select-multiple-item-height - @select-multiple-item-border-width * 2;
|
||||||
|
background: @select-selection-item-bg;
|
||||||
|
border: 1px solid @select-selection-item-border-color;
|
||||||
|
border-radius: @border-radius-base;
|
||||||
|
cursor: default;
|
||||||
|
transition: font-size 0.3s, line-height 0.3s, height 0.3s;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
// It's ok not to do this, but 24px makes bottom narrow in view should adjust
|
||||||
|
&-content {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: @padding-xs / 2;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.@{select-prefix-cls}-selection-search {
|
&-remove {
|
||||||
height: @select-selection-height + @select-multiple-padding;
|
.iconfont-mixin();
|
||||||
line-height: @select-selection-height + @select-multiple-padding;
|
|
||||||
|
|
||||||
&-input,
|
display: inline-block;
|
||||||
&-mirror {
|
color: @text-color-secondary;
|
||||||
height: @select-selection-height;
|
font-weight: bold;
|
||||||
line-height: @select-selection-height - @border-width-base * 2;
|
font-size: @font-size-sm;
|
||||||
|
line-height: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
.iconfont-size-under-12px(10px);
|
||||||
|
&:hover {
|
||||||
|
color: @icon-color-hover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.select-size('lg', @input-height-lg);
|
// ========================== Input ==========================
|
||||||
.select-size('sm', @input-height-sm);
|
.@{select-prefix-cls}-selection-search {
|
||||||
|
position: relative;
|
||||||
|
margin-left: @select-multiple-padding / 2;
|
||||||
|
|
||||||
// Size small need additional set padding
|
&-input,
|
||||||
&.@{select-prefix-cls}-sm {
|
&-mirror {
|
||||||
|
font-family: @font-family;
|
||||||
|
line-height: @line-height-base;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-input {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-mirror {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 999;
|
||||||
|
white-space: nowrap;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/ant-design/ant-design/issues/22906
|
||||||
|
&:first-child .@{select-prefix-cls}-selection-search-input {
|
||||||
|
margin-left: 6.5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================= Placeholder =======================
|
||||||
.@{select-prefix-cls}-selection-placeholder {
|
.@{select-prefix-cls}-selection-placeholder {
|
||||||
left: @input-padding-horizontal-sm;
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: @input-padding-horizontal;
|
||||||
|
left: @input-padding-horizontal;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
transition: all 0.3s;
|
||||||
}
|
}
|
||||||
// https://github.com/ant-design/ant-design/issues/22906
|
|
||||||
.@{select-prefix-cls}-selection-search:first-child .@{select-prefix-cls}-selection-search-input {
|
// ============================================================
|
||||||
margin-left: 3px;
|
// == Size ==
|
||||||
|
// ============================================================
|
||||||
|
.select-size(@suffix, @input-height) {
|
||||||
|
@merged-cls: ~'@{select-prefix-cls}-@{suffix}';
|
||||||
|
&.@{merged-cls} {
|
||||||
|
@select-selection-height: @input-height - @input-padding-vertical-base * 2;
|
||||||
|
@select-height-without-border: @input-height - @border-width-base * 2;
|
||||||
|
|
||||||
|
.@{select-prefix-cls}-selector::after {
|
||||||
|
line-height: @select-selection-height;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{select-prefix-cls}-selection-item {
|
||||||
|
height: @select-selection-height;
|
||||||
|
line-height: @select-selection-height - @border-width-base * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{select-prefix-cls}-selection-search {
|
||||||
|
height: @select-selection-height + @select-multiple-padding;
|
||||||
|
line-height: @select-selection-height + @select-multiple-padding;
|
||||||
|
|
||||||
|
&-input,
|
||||||
|
&-mirror {
|
||||||
|
height: @select-selection-height;
|
||||||
|
line-height: @select-selection-height - @border-width-base * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-size('lg', @input-height-lg);
|
||||||
|
.select-size('sm', @input-height-sm);
|
||||||
|
|
||||||
|
// Size small need additional set padding
|
||||||
|
&.@{select-prefix-cls}-sm {
|
||||||
|
.@{select-prefix-cls}-selection-placeholder {
|
||||||
|
left: @input-padding-horizontal-sm;
|
||||||
|
}
|
||||||
|
// https://github.com/ant-design/ant-design/issues/22906
|
||||||
|
.@{select-prefix-cls}-selection-search:first-child
|
||||||
|
.@{select-prefix-cls}-selection-search-input {
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.@{select-prefix-cls}-lg {
|
||||||
|
.@{select-prefix-cls}-selection-item {
|
||||||
|
height: @select-multiple-item-height-lg;
|
||||||
|
line-height: @select-multiple-item-height-lg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.@{select-prefix-cls}-lg {
|
|
||||||
.@{select-prefix-cls}-selection-item {
|
&-disabled .@{select-prefix-cls}-selection-item-remove {
|
||||||
height: @select-multiple-item-height-lg;
|
display: none;
|
||||||
line-height: @select-multiple-item-height-lg;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,8 +28,7 @@
|
|||||||
// ========================== Popup ==========================
|
// ========================== Popup ==========================
|
||||||
&-dropdown {
|
&-dropdown {
|
||||||
&-rtl {
|
&-rtl {
|
||||||
direction: ltr;
|
direction: rtl;
|
||||||
text-align: right;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@
|
|||||||
|
|
||||||
.@{select-prefix-cls}-selection-item,
|
.@{select-prefix-cls}-selection-item,
|
||||||
.@{select-prefix-cls}-selection-placeholder {
|
.@{select-prefix-cls}-selection-placeholder {
|
||||||
line-height: @input-height;
|
line-height: @input-height - 2 * @border-width-base;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,17 +8,11 @@ export interface AvatarProps extends Omit<SkeletonElementProps, 'shape'> {
|
|||||||
shape?: 'circle' | 'square';
|
shape?: 'circle' | 'square';
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react/prefer-stateless-function
|
const SkeletonAvatar = (props: AvatarProps) => {
|
||||||
class SkeletonAvatar extends React.Component<AvatarProps, any> {
|
const renderSkeletonAvatar = ({ getPrefixCls }: ConfigConsumerProps) => {
|
||||||
static defaultProps: Partial<AvatarProps> = {
|
const { prefixCls: customizePrefixCls, className, active } = props;
|
||||||
size: 'default',
|
|
||||||
shape: 'circle',
|
|
||||||
};
|
|
||||||
|
|
||||||
renderSkeletonAvatar = ({ getPrefixCls }: ConfigConsumerProps) => {
|
|
||||||
const { prefixCls: customizePrefixCls, className, active } = this.props;
|
|
||||||
const prefixCls = getPrefixCls('skeleton', customizePrefixCls);
|
const prefixCls = getPrefixCls('skeleton', customizePrefixCls);
|
||||||
const otherProps = omit(this.props, ['prefixCls']);
|
const otherProps = omit(props, ['prefixCls']);
|
||||||
const cls = classNames(prefixCls, className, `${prefixCls}-element`, {
|
const cls = classNames(prefixCls, className, `${prefixCls}-element`, {
|
||||||
[`${prefixCls}-active`]: active,
|
[`${prefixCls}-active`]: active,
|
||||||
});
|
});
|
||||||
@ -28,10 +22,12 @@ class SkeletonAvatar extends React.Component<AvatarProps, any> {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
return <ConfigConsumer>{renderSkeletonAvatar}</ConfigConsumer>;
|
||||||
render() {
|
|
||||||
return <ConfigConsumer>{this.renderSkeletonAvatar}</ConfigConsumer>;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SkeletonAvatar.defaultProps = {
|
||||||
|
size: 'default',
|
||||||
|
shape: 'circle',
|
||||||
|
};
|
||||||
|
|
||||||
export default SkeletonAvatar;
|
export default SkeletonAvatar;
|
||||||
|
@ -4,20 +4,15 @@ import classNames from 'classnames';
|
|||||||
import Element, { SkeletonElementProps } from './Element';
|
import Element, { SkeletonElementProps } from './Element';
|
||||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||||
|
|
||||||
interface SkeletonButtonProps extends Omit<SkeletonElementProps, 'size'> {
|
export interface SkeletonButtonProps extends Omit<SkeletonElementProps, 'size'> {
|
||||||
size?: 'large' | 'small' | 'default';
|
size?: 'large' | 'small' | 'default';
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react/prefer-stateless-function
|
const SkeletonButton = (props: SkeletonButtonProps) => {
|
||||||
class SkeletonButton extends React.Component<SkeletonButtonProps, any> {
|
const renderSkeletonButton = ({ getPrefixCls }: ConfigConsumerProps) => {
|
||||||
static defaultProps: Partial<SkeletonButtonProps> = {
|
const { prefixCls: customizePrefixCls, className, active } = props;
|
||||||
size: 'default',
|
|
||||||
};
|
|
||||||
|
|
||||||
renderSkeletonButton = ({ getPrefixCls }: ConfigConsumerProps) => {
|
|
||||||
const { prefixCls: customizePrefixCls, className, active } = this.props;
|
|
||||||
const prefixCls = getPrefixCls('skeleton', customizePrefixCls);
|
const prefixCls = getPrefixCls('skeleton', customizePrefixCls);
|
||||||
const otherProps = omit(this.props, ['prefixCls']);
|
const otherProps = omit(props, ['prefixCls']);
|
||||||
const cls = classNames(prefixCls, className, `${prefixCls}-element`, {
|
const cls = classNames(prefixCls, className, `${prefixCls}-element`, {
|
||||||
[`${prefixCls}-active`]: active,
|
[`${prefixCls}-active`]: active,
|
||||||
});
|
});
|
||||||
@ -27,10 +22,11 @@ class SkeletonButton extends React.Component<SkeletonButtonProps, any> {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
return <ConfigConsumer>{renderSkeletonButton}</ConfigConsumer>;
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
SkeletonButton.defaultProps = {
|
||||||
return <ConfigConsumer>{this.renderSkeletonButton}</ConfigConsumer>;
|
size: 'default',
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export default SkeletonButton;
|
export default SkeletonButton;
|
||||||
|
@ -10,37 +10,35 @@ export interface SkeletonElementProps {
|
|||||||
active?: boolean;
|
active?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react/prefer-stateless-function
|
const Element = (props: SkeletonElementProps) => {
|
||||||
class Element extends React.Component<SkeletonElementProps, any> {
|
const { prefixCls, className, style, size, shape } = props;
|
||||||
render() {
|
|
||||||
const { prefixCls, className, style, size, shape } = this.props;
|
|
||||||
|
|
||||||
const sizeCls = classNames({
|
const sizeCls = classNames({
|
||||||
[`${prefixCls}-lg`]: size === 'large',
|
[`${prefixCls}-lg`]: size === 'large',
|
||||||
[`${prefixCls}-sm`]: size === 'small',
|
[`${prefixCls}-sm`]: size === 'small',
|
||||||
});
|
});
|
||||||
|
|
||||||
const shapeCls = classNames({
|
const shapeCls = classNames({
|
||||||
[`${prefixCls}-circle`]: shape === 'circle',
|
[`${prefixCls}-circle`]: shape === 'circle',
|
||||||
[`${prefixCls}-square`]: shape === 'square',
|
[`${prefixCls}-square`]: shape === 'square',
|
||||||
[`${prefixCls}-round`]: shape === 'round',
|
[`${prefixCls}-round`]: shape === 'round',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const sizeStyle: React.CSSProperties =
|
||||||
|
typeof size === 'number'
|
||||||
|
? {
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
lineHeight: `${size}px`,
|
||||||
|
}
|
||||||
|
: {};
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={classNames(prefixCls, className, sizeCls, shapeCls)}
|
||||||
|
style={{ ...sizeStyle, ...style }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const sizeStyle: React.CSSProperties =
|
|
||||||
typeof size === 'number'
|
|
||||||
? {
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
lineHeight: `${size}px`,
|
|
||||||
}
|
|
||||||
: {};
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
className={classNames(prefixCls, className, sizeCls, shapeCls)}
|
|
||||||
style={{ ...sizeStyle, ...style }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Element;
|
export default Element;
|
||||||
|
@ -4,20 +4,15 @@ import classNames from 'classnames';
|
|||||||
import Element, { SkeletonElementProps } from './Element';
|
import Element, { SkeletonElementProps } from './Element';
|
||||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||||
|
|
||||||
interface SkeletonInputProps extends Omit<SkeletonElementProps, 'size' | 'shape'> {
|
export interface SkeletonInputProps extends Omit<SkeletonElementProps, 'size' | 'shape'> {
|
||||||
size?: 'large' | 'small' | 'default';
|
size?: 'large' | 'small' | 'default';
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react/prefer-stateless-function
|
const SkeletonInput = (props: SkeletonInputProps) => {
|
||||||
class SkeletonInput extends React.Component<SkeletonInputProps, any> {
|
const renderSkeletonInput = ({ getPrefixCls }: ConfigConsumerProps) => {
|
||||||
static defaultProps: Partial<SkeletonInputProps> = {
|
const { prefixCls: customizePrefixCls, className, active } = props;
|
||||||
size: 'default',
|
|
||||||
};
|
|
||||||
|
|
||||||
renderSkeletonInput = ({ getPrefixCls }: ConfigConsumerProps) => {
|
|
||||||
const { prefixCls: customizePrefixCls, className, active } = this.props;
|
|
||||||
const prefixCls = getPrefixCls('skeleton', customizePrefixCls);
|
const prefixCls = getPrefixCls('skeleton', customizePrefixCls);
|
||||||
const otherProps = omit(this.props, ['prefixCls']);
|
const otherProps = omit(props, ['prefixCls']);
|
||||||
const cls = classNames(prefixCls, className, `${prefixCls}-element`, {
|
const cls = classNames(prefixCls, className, `${prefixCls}-element`, {
|
||||||
[`${prefixCls}-active`]: active,
|
[`${prefixCls}-active`]: active,
|
||||||
});
|
});
|
||||||
@ -27,10 +22,11 @@ class SkeletonInput extends React.Component<SkeletonInputProps, any> {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
return <ConfigConsumer>{renderSkeletonInput}</ConfigConsumer>;
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
SkeletonInput.defaultProps = {
|
||||||
return <ConfigConsumer>{this.renderSkeletonInput}</ConfigConsumer>;
|
size: 'default',
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export default SkeletonInput;
|
export default SkeletonInput;
|
||||||
|
@ -11,9 +11,9 @@ export interface SkeletonParagraphProps {
|
|||||||
rows?: number;
|
rows?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Paragraph extends React.Component<SkeletonParagraphProps, {}> {
|
const Paragraph = (props: SkeletonParagraphProps) => {
|
||||||
getWidth(index: number) {
|
const getWidth = (index: number) => {
|
||||||
const { width, rows = 2 } = this.props;
|
const { width, rows = 2 } = props;
|
||||||
if (Array.isArray(width)) {
|
if (Array.isArray(width)) {
|
||||||
return width[index];
|
return width[index];
|
||||||
}
|
}
|
||||||
@ -22,20 +22,17 @@ class Paragraph extends React.Component<SkeletonParagraphProps, {}> {
|
|||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
};
|
||||||
|
const { prefixCls, className, style, rows } = props;
|
||||||
render() {
|
const rowList = [...Array(rows)].map((_, index) => (
|
||||||
const { prefixCls, className, style, rows } = this.props;
|
// eslint-disable-next-line react/no-array-index-key
|
||||||
const rowList = [...Array(rows)].map((_, index) => (
|
<li key={index} style={{ width: getWidth(index) }}/>
|
||||||
// eslint-disable-next-line react/no-array-index-key
|
));
|
||||||
<li key={index} style={{ width: this.getWidth(index) }} />
|
return (
|
||||||
));
|
<ul className={classNames(prefixCls, className)} style={style}>
|
||||||
return (
|
{rowList}
|
||||||
<ul className={classNames(prefixCls, className)} style={style}>
|
</ul>
|
||||||
{rowList}
|
);
|
||||||
</ul>
|
};
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Paragraph;
|
export default Paragraph;
|
||||||
|
@ -3,13 +3,14 @@ import classNames from 'classnames';
|
|||||||
import Title, { SkeletonTitleProps } from './Title';
|
import Title, { SkeletonTitleProps } from './Title';
|
||||||
import Paragraph, { SkeletonParagraphProps } from './Paragraph';
|
import Paragraph, { SkeletonParagraphProps } from './Paragraph';
|
||||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||||
import SkeletonButton from './Button';
|
|
||||||
import Element from './Element';
|
import Element from './Element';
|
||||||
import SkeletonAvatar, { AvatarProps } from './Avatar';
|
import SkeletonAvatar, { AvatarProps } from './Avatar';
|
||||||
|
import SkeletonButton from './Button';
|
||||||
import SkeletonInput from './Input';
|
import SkeletonInput from './Input';
|
||||||
|
|
||||||
/* This only for skeleton internal. */
|
/* This only for skeleton internal. */
|
||||||
interface SkeletonAvatarProps extends Omit<AvatarProps, 'active'> {}
|
interface SkeletonAvatarProps extends Omit<AvatarProps, 'active'> {
|
||||||
|
}
|
||||||
|
|
||||||
export interface SkeletonProps {
|
export interface SkeletonProps {
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
@ -68,20 +69,8 @@ function getParagraphBasicProps(hasAvatar: boolean, hasTitle: boolean): Skeleton
|
|||||||
return basicProps;
|
return basicProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Skeleton extends React.Component<SkeletonProps, any> {
|
const Skeleton = (props: SkeletonProps) => {
|
||||||
static Button: typeof SkeletonButton;
|
const renderSkeleton = ({ getPrefixCls, direction }: ConfigConsumerProps) => {
|
||||||
|
|
||||||
static Avatar: typeof SkeletonAvatar;
|
|
||||||
|
|
||||||
static Input: typeof SkeletonInput;
|
|
||||||
|
|
||||||
static defaultProps: Partial<SkeletonProps> = {
|
|
||||||
avatar: false,
|
|
||||||
title: true,
|
|
||||||
paragraph: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
renderSkeleton = ({ getPrefixCls, direction }: ConfigConsumerProps) => {
|
|
||||||
const {
|
const {
|
||||||
prefixCls: customizePrefixCls,
|
prefixCls: customizePrefixCls,
|
||||||
loading,
|
loading,
|
||||||
@ -91,11 +80,11 @@ class Skeleton extends React.Component<SkeletonProps, any> {
|
|||||||
title,
|
title,
|
||||||
paragraph,
|
paragraph,
|
||||||
active,
|
active,
|
||||||
} = this.props;
|
} = props;
|
||||||
|
|
||||||
const prefixCls = getPrefixCls('skeleton', customizePrefixCls);
|
const prefixCls = getPrefixCls('skeleton', customizePrefixCls);
|
||||||
|
|
||||||
if (loading || !('loading' in this.props)) {
|
if (loading || !('loading' in props)) {
|
||||||
const hasAvatar = !!avatar;
|
const hasAvatar = !!avatar;
|
||||||
const hasTitle = !!title;
|
const hasTitle = !!title;
|
||||||
const hasParagraph = !!paragraph;
|
const hasParagraph = !!paragraph;
|
||||||
@ -166,10 +155,17 @@ class Skeleton extends React.Component<SkeletonProps, any> {
|
|||||||
|
|
||||||
return children;
|
return children;
|
||||||
};
|
};
|
||||||
|
return <ConfigConsumer>{renderSkeleton}</ConfigConsumer>;
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
Skeleton.defaultProps = {
|
||||||
return <ConfigConsumer>{this.renderSkeleton}</ConfigConsumer>;
|
avatar: false,
|
||||||
}
|
title: true,
|
||||||
}
|
paragraph: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
Skeleton.Button = SkeletonButton;
|
||||||
|
Skeleton.Avatar = SkeletonAvatar;
|
||||||
|
Skeleton.Input = SkeletonInput;
|
||||||
|
|
||||||
export default Skeleton;
|
export default Skeleton;
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
import Skeleton from './Skeleton';
|
import Skeleton from './Skeleton';
|
||||||
import SkeletonButton from './Button';
|
|
||||||
import SkeletonAvatar from './Avatar';
|
|
||||||
import SkeletonInput from './Input';
|
|
||||||
|
|
||||||
export { SkeletonProps } from './Skeleton';
|
export { SkeletonProps } from './Skeleton';
|
||||||
|
|
||||||
Skeleton.Button = SkeletonButton;
|
|
||||||
Skeleton.Avatar = SkeletonAvatar;
|
|
||||||
Skeleton.Input = SkeletonInput;
|
|
||||||
export default Skeleton;
|
export default Skeleton;
|
||||||
|
@ -15,7 +15,7 @@ To input a value in a range.
|
|||||||
| Property | Description | Type | Default | Version |
|
| Property | Description | Type | Default | Version |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| autoFocus | get focus when component mounted | boolean | false | |
|
| autoFocus | get focus when component mounted | boolean | false | |
|
||||||
| defaultValue | The default value of slider. When `range` is `false`, use `number`, otherwise, use `[number, number]` | number\|number\[] | 0 or \[0, 0] | |
|
| defaultValue | The default value of slider. When `range` is `false`, use `number`, otherwise, use `[number, number]` | number\|\[number, number] | 0 or \[0, 0] | |
|
||||||
| disabled | If true, the slider will not be interactable. | boolean | false | |
|
| disabled | If true, the slider will not be interactable. | boolean | false | |
|
||||||
| dots | Whether the thumb can drag over tick only. | boolean | false | |
|
| dots | Whether the thumb can drag over tick only. | boolean | false | |
|
||||||
| included | Make effect when `marks` not null, `true` means containment and `false` means coordinative | boolean | true | |
|
| included | Make effect when `marks` not null, `true` means containment and `false` means coordinative | boolean | true | |
|
||||||
@ -26,7 +26,7 @@ To input a value in a range.
|
|||||||
| reverse | reverse the component | boolean | false | |
|
| reverse | reverse the component | boolean | false | |
|
||||||
| step | The granularity the slider can step through values. Must greater than 0, and be divided by (max - min) . When `marks` no null, `step` can be `null`. | number\|null | 1 | |
|
| step | The granularity the slider can step through values. Must greater than 0, and be divided by (max - min) . When `marks` no null, `step` can be `null`. | number\|null | 1 | |
|
||||||
| tipFormatter | Slider will pass its value to `tipFormatter`, and display its value in Tooltip, and hide Tooltip when return value is null. | Function\|null | IDENTITY | |
|
| tipFormatter | Slider will pass its value to `tipFormatter`, and display its value in Tooltip, and hide Tooltip when return value is null. | Function\|null | IDENTITY | |
|
||||||
| value | The value of slider. When `range` is `false`, use `number`, otherwise, use `[number, number]` | number\|number\[] | |
|
| value | The value of slider. When `range` is `false`, use `number`, otherwise, use `[number, number]` | number\|\[number, number] | |
|
||||||
| vertical | If true, the slider will be vertical. | Boolean | false | |
|
| vertical | If true, the slider will be vertical. | Boolean | false | |
|
||||||
| onAfterChange | Fire when `onmouseup` is fired. | Function(value) | NOOP | |
|
| onAfterChange | Fire when `onmouseup` is fired. | Function(value) | NOOP | |
|
||||||
| onChange | Callback function that is fired when the user changes the slider's value. | Function(value) | NOOP | |
|
| onChange | Callback function that is fired when the user changes the slider's value. | Function(value) | NOOP | |
|
||||||
|
@ -16,7 +16,7 @@ title: Slider
|
|||||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| allowClear | 支持清除, 单选模式有效 | boolean | false | |
|
| allowClear | 支持清除, 单选模式有效 | boolean | false | |
|
||||||
| defaultValue | 设置初始取值。当 `range` 为 `false` 时,使用 `number`,否则用 `[number, number]` | number\|number\[] | 0 or \[0, 0] | |
|
| defaultValue | 设置初始取值。当 `range` 为 `false` 时,使用 `number`,否则用 `[number, number]` | number\|\[number, number] | 0 or \[0, 0] | |
|
||||||
| disabled | 值为 `true` 时,滑块为禁用状态 | boolean | false | |
|
| disabled | 值为 `true` 时,滑块为禁用状态 | boolean | false | |
|
||||||
| dots | 是否只能拖拽到刻度上 | boolean | false | |
|
| dots | 是否只能拖拽到刻度上 | boolean | false | |
|
||||||
| included | `marks` 不为空对象时有效,值为 true 时表示值为包含关系,false 表示并列 | boolean | true | |
|
| included | `marks` 不为空对象时有效,值为 true 时表示值为包含关系,false 表示并列 | boolean | true | |
|
||||||
@ -27,7 +27,7 @@ title: Slider
|
|||||||
| reverse | 反向坐标轴 | boolean | false | |
|
| reverse | 反向坐标轴 | boolean | false | |
|
||||||
| step | 步长,取值必须大于 0,并且可被 (max - min) 整除。当 `marks` 不为空对象时,可以设置 `step` 为 `null`,此时 Slider 的可选值仅有 marks 标出来的部分。 | number\|null | 1 | |
|
| step | 步长,取值必须大于 0,并且可被 (max - min) 整除。当 `marks` 不为空对象时,可以设置 `step` 为 `null`,此时 Slider 的可选值仅有 marks 标出来的部分。 | number\|null | 1 | |
|
||||||
| tipFormatter | Slider 会把当前值传给 `tipFormatter`,并在 Tooltip 中显示 `tipFormatter` 的返回值,若为 null,则隐藏 Tooltip。 | Function\|null | IDENTITY | |
|
| tipFormatter | Slider 会把当前值传给 `tipFormatter`,并在 Tooltip 中显示 `tipFormatter` 的返回值,若为 null,则隐藏 Tooltip。 | Function\|null | IDENTITY | |
|
||||||
| value | 设置当前取值。当 `range` 为 `false` 时,使用 `number`,否则用 `[number, number]` | number\|number\[] | | |
|
| value | 设置当前取值。当 `range` 为 `false` 时,使用 `number`,否则用 `[number, number]` | number\|\[number, number] | | |
|
||||||
| vertical | 值为 `true` 时,Slider 为垂直方向 | Boolean | false | |
|
| vertical | 值为 `true` 时,Slider 为垂直方向 | Boolean | false | |
|
||||||
| onAfterChange | 与 `onmouseup` 触发时机一致,把当前值作为参数传入。 | Function(value) | NOOP | |
|
| onAfterChange | 与 `onmouseup` 触发时机一致,把当前值作为参数传入。 | Function(value) | NOOP | |
|
||||||
| onChange | 当 Slider 的值发生改变时,会触发 onChange 事件,并把改变后的值作为参数传入。 | Function(value) | NOOP | |
|
| onChange | 当 Slider 的值发生改变时,会触发 onChange 事件,并把改变后的值作为参数传入。 | Function(value) | NOOP | |
|
||||||
|
@ -10,28 +10,14 @@
|
|||||||
margin: 0 0 0 @steps-desciption-max-width / 2;
|
margin: 0 0 0 @steps-desciption-max-width / 2;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
.@{steps-prefix-cls}-rtl& {
|
|
||||||
margin: 0 @steps-desciption-max-width / 2 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
width: ~'calc(100% - 20px)';
|
width: ~'calc(100% - 20px)';
|
||||||
height: 3px;
|
height: 3px;
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
|
|
||||||
.@{steps-prefix-cls}-rtl& {
|
|
||||||
margin-right: 12px;
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&:first-child .@{steps-prefix-cls}-icon-dot {
|
&:first-child .@{steps-prefix-cls}-icon-dot {
|
||||||
left: 2px;
|
left: 2px;
|
||||||
|
|
||||||
.@{steps-prefix-cls}-rtl& {
|
|
||||||
right: 2px;
|
|
||||||
left: auto;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
&-icon {
|
&-icon {
|
||||||
width: @steps-dot-size;
|
width: @steps-dot-size;
|
||||||
@ -42,11 +28,6 @@
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
border: 0;
|
border: 0;
|
||||||
|
|
||||||
.@{steps-prefix-cls}-rtl& {
|
|
||||||
margin-right: 67px;
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.@{steps-prefix-cls}-icon-dot {
|
.@{steps-prefix-cls}-icon-dot {
|
||||||
position: relative;
|
position: relative;
|
||||||
float: left;
|
float: left;
|
||||||
@ -54,10 +35,6 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 100px;
|
border-radius: 100px;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
|
|
||||||
.@{steps-prefix-cls}-rtl& {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
/* expand hover area */
|
/* expand hover area */
|
||||||
&::after {
|
&::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -67,11 +44,6 @@
|
|||||||
height: 32px;
|
height: 32px;
|
||||||
background: fade(@black, 0.1%);
|
background: fade(@black, 0.1%);
|
||||||
content: '';
|
content: '';
|
||||||
|
|
||||||
.@{steps-prefix-cls}-rtl& {
|
|
||||||
right: -26px;
|
|
||||||
left: auto;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,11 +66,6 @@
|
|||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
background: none;
|
background: none;
|
||||||
|
|
||||||
.@{steps-prefix-cls}-rtl& {
|
|
||||||
margin-right: 0;
|
|
||||||
margin-left: 16px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// https://github.com/ant-design/ant-design/issues/18354
|
// https://github.com/ant-design/ant-design/issues/18354
|
||||||
.@{steps-prefix-cls}-item > .@{steps-prefix-cls}-item-container > .@{steps-prefix-cls}-item-tail {
|
.@{steps-prefix-cls}-item > .@{steps-prefix-cls}-item-container > .@{steps-prefix-cls}-item-tail {
|
||||||
@ -106,26 +73,11 @@
|
|||||||
left: -9px;
|
left: -9px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 22px 0 4px;
|
padding: 22px 0 4px;
|
||||||
|
|
||||||
.@{steps-prefix-cls}-rtl& {
|
|
||||||
right: -9px;
|
|
||||||
left: auto;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.@{steps-prefix-cls}-item:first-child .@{steps-prefix-cls}-icon-dot {
|
.@{steps-prefix-cls}-item:first-child .@{steps-prefix-cls}-icon-dot {
|
||||||
left: 0;
|
left: 0;
|
||||||
|
|
||||||
.@{steps-prefix-cls}-rtl& {
|
|
||||||
right: 0;
|
|
||||||
left: auto;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.@{steps-prefix-cls}-item-process .@{steps-prefix-cls}-icon-dot {
|
.@{steps-prefix-cls}-item-process .@{steps-prefix-cls}-icon-dot {
|
||||||
left: -2px;
|
left: -2px;
|
||||||
|
|
||||||
.@{steps-prefix-cls}-rtl& {
|
|
||||||
right: -2px;
|
|
||||||
left: auto;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,3 +174,75 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// progress-dot
|
||||||
|
.@{steps-prefix-cls}-dot,
|
||||||
|
.@{steps-prefix-cls}-dot.@{steps-prefix-cls}-small {
|
||||||
|
.@{steps-prefix-cls}-item {
|
||||||
|
&-tail {
|
||||||
|
.@{steps-prefix-cls}-rtl& {
|
||||||
|
margin: 0 @steps-desciption-max-width / 2 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
.@{steps-prefix-cls}-rtl& {
|
||||||
|
margin-right: 12px;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:first-child .@{steps-prefix-cls}-icon-dot {
|
||||||
|
.@{steps-prefix-cls}-rtl& {
|
||||||
|
right: 2px;
|
||||||
|
left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-icon {
|
||||||
|
.@{steps-prefix-cls}-rtl& {
|
||||||
|
margin-right: 67px;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{steps-prefix-cls}-icon-dot {
|
||||||
|
.@{steps-prefix-cls}-rtl& {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
/* expand hover area */
|
||||||
|
&::after {
|
||||||
|
.@{steps-prefix-cls}-rtl& {
|
||||||
|
right: -26px;
|
||||||
|
left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{steps-prefix-cls}-vertical.@{steps-prefix-cls}-dot {
|
||||||
|
.@{steps-prefix-cls}-item-icon {
|
||||||
|
.@{steps-prefix-cls}-rtl& {
|
||||||
|
margin-right: 0;
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// https://github.com/ant-design/ant-design/issues/18354
|
||||||
|
.@{steps-prefix-cls}-item > .@{steps-prefix-cls}-item-container > .@{steps-prefix-cls}-item-tail {
|
||||||
|
.@{steps-prefix-cls}-rtl& {
|
||||||
|
right: -9px;
|
||||||
|
left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.@{steps-prefix-cls}-item:first-child .@{steps-prefix-cls}-icon-dot {
|
||||||
|
.@{steps-prefix-cls}-rtl& {
|
||||||
|
right: 0;
|
||||||
|
left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.@{steps-prefix-cls}-item-process .@{steps-prefix-cls}-icon-dot {
|
||||||
|
.@{steps-prefix-cls}-rtl& {
|
||||||
|
right: -2px;
|
||||||
|
left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -656,7 +656,8 @@
|
|||||||
@tabs-card-head-background: @background-color-light;
|
@tabs-card-head-background: @background-color-light;
|
||||||
@tabs-card-height: 40px;
|
@tabs-card-height: 40px;
|
||||||
@tabs-card-active-color: @primary-color;
|
@tabs-card-active-color: @primary-color;
|
||||||
@tabs-card-horizontal-padding: 8px @padding-md;
|
@tabs-card-horizontal-padding: (@tabs-card-height - floor(@font-size-base * @line-height-base)) / 2 -
|
||||||
|
@border-width-base @padding-md;
|
||||||
@tabs-card-horizontal-padding-sm: 6px @padding-md;
|
@tabs-card-horizontal-padding-sm: 6px @padding-md;
|
||||||
@tabs-title-font-size: @font-size-base;
|
@tabs-title-font-size: @font-size-base;
|
||||||
@tabs-title-font-size-lg: @font-size-lg;
|
@tabs-title-font-size-lg: @font-size-lg;
|
||||||
|
@ -13,10 +13,7 @@ describe('Switch', () => {
|
|||||||
|
|
||||||
it('should has click wave effect', async () => {
|
it('should has click wave effect', async () => {
|
||||||
const wrapper = mount(<Switch />);
|
const wrapper = mount(<Switch />);
|
||||||
wrapper
|
wrapper.find('.ant-switch').getDOMNode().click();
|
||||||
.find('.ant-switch')
|
|
||||||
.getDOMNode()
|
|
||||||
.click();
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 0));
|
await new Promise(resolve => setTimeout(resolve, 0));
|
||||||
expect(wrapper.render()).toMatchSnapshot();
|
expect(wrapper.render()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
@ -27,7 +24,7 @@ describe('Switch', () => {
|
|||||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||||
mount(<Switch value />);
|
mount(<Switch value />);
|
||||||
expect(errorSpy).toHaveBeenCalledWith(
|
expect(errorSpy).toHaveBeenCalledWith(
|
||||||
'Warning: [antd: Switch] `value` is not validate prop, do you mean `checked`?',
|
'Warning: [antd: Switch] `value` is not a valid prop, do you mean `checked`?',
|
||||||
);
|
);
|
||||||
errorSpy.mockRestore();
|
errorSpy.mockRestore();
|
||||||
});
|
});
|
||||||
|
@ -41,7 +41,7 @@ export default class Switch extends React.Component<SwitchProps, {}> {
|
|||||||
warning(
|
warning(
|
||||||
'checked' in props || !('value' in props),
|
'checked' in props || !('value' in props),
|
||||||
'Switch',
|
'Switch',
|
||||||
'`value` is not validate prop, do you mean `checked`?',
|
'`value` is not a valid prop, do you mean `checked`?',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +98,7 @@ function Table<RecordType extends object = any>(props: TableProps<RecordType>) {
|
|||||||
rowClassName,
|
rowClassName,
|
||||||
columns,
|
columns,
|
||||||
children,
|
children,
|
||||||
|
childrenColumnName: legacyChildrenColumnName,
|
||||||
onChange,
|
onChange,
|
||||||
getPopupContainer,
|
getPopupContainer,
|
||||||
loading,
|
loading,
|
||||||
@ -106,7 +107,6 @@ function Table<RecordType extends object = any>(props: TableProps<RecordType>) {
|
|||||||
expandedRowRender,
|
expandedRowRender,
|
||||||
expandIconColumnIndex,
|
expandIconColumnIndex,
|
||||||
indentSize,
|
indentSize,
|
||||||
childrenColumnName = 'children',
|
|
||||||
scroll,
|
scroll,
|
||||||
sortDirections,
|
sortDirections,
|
||||||
locale,
|
locale,
|
||||||
@ -128,9 +128,11 @@ function Table<RecordType extends object = any>(props: TableProps<RecordType>) {
|
|||||||
const dropdownPrefixCls = getPrefixCls('dropdown', customizeDropdownPrefixCls);
|
const dropdownPrefixCls = getPrefixCls('dropdown', customizeDropdownPrefixCls);
|
||||||
|
|
||||||
const mergedExpandable: ExpandableConfig<RecordType> = {
|
const mergedExpandable: ExpandableConfig<RecordType> = {
|
||||||
|
childrenColumnName: legacyChildrenColumnName,
|
||||||
expandIconColumnIndex,
|
expandIconColumnIndex,
|
||||||
...expandable,
|
...expandable,
|
||||||
};
|
};
|
||||||
|
const { childrenColumnName = 'children' } = mergedExpandable;
|
||||||
|
|
||||||
const expandType: ExpandType = React.useMemo<ExpandType>(() => {
|
const expandType: ExpandType = React.useMemo<ExpandType>(() => {
|
||||||
if (rawData.some(item => (item as any)[childrenColumnName])) {
|
if (rawData.some(item => (item as any)[childrenColumnName])) {
|
||||||
|
@ -779,4 +779,22 @@ describe('Table.rowSelection', () => {
|
|||||||
jest.runAllTimers();
|
jest.runAllTimers();
|
||||||
expect(wrapper.render()).toMatchSnapshot();
|
expect(wrapper.render()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Table selection should check', () => {
|
||||||
|
const onChange = jest.fn();
|
||||||
|
const wrapper = mount(
|
||||||
|
<Table
|
||||||
|
dataSource={[{ name: 'light', sub: [{ name: 'bamboo' }] }]}
|
||||||
|
expandable={{ expandedRowKeys: ['light'], childrenColumnName: 'sub' }}
|
||||||
|
rowSelection={{ onChange }}
|
||||||
|
rowKey="name"
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
wrapper
|
||||||
|
.find('input')
|
||||||
|
.last()
|
||||||
|
.simulate('change', { target: { checked: true } });
|
||||||
|
expect(onChange.mock.calls[0][1]).toEqual([expect.objectContaining({ name: 'bamboo' })]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* eslint-disable no-unused-expressions */
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import Table from '../Table';
|
import Table from '../Table';
|
||||||
import { ColumnProps } from '..';
|
import { ColumnProps } from '..';
|
||||||
@ -44,4 +42,3 @@ describe('Table.typescript types', () => {
|
|||||||
expect(columns).toBeTruthy();
|
expect(columns).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
/* eslint-enable */
|
|
||||||
|
@ -85,7 +85,7 @@ const EditableTable = () => {
|
|||||||
const isEditing = (record: Item) => record.key === editingKey;
|
const isEditing = (record: Item) => record.key === editingKey;
|
||||||
|
|
||||||
const edit = (record: Item) => {
|
const edit = (record: Item) => {
|
||||||
form.setFieldsValue({ ...record });
|
form.setFieldsValue({ name: '', age: '', address: '', ...record });
|
||||||
setEditingKey(record.key);
|
setEditingKey(record.key);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ const columns = [
|
|||||||
| dataSource | Data record array to be displayed | any\[] | - |
|
| dataSource | Data record array to be displayed | any\[] | - |
|
||||||
| expandable | Config expandable content | [expandable](#expandable) | - |
|
| expandable | Config expandable content | [expandable](#expandable) | - |
|
||||||
| footer | Table footer renderer | Function(currentPageData) | - |
|
| footer | Table footer renderer | Function(currentPageData) | - |
|
||||||
| loading | Loading status of table | boolean\|[object](https://ant.design/components/spin-cn/#API) ([more](https://github.com/ant-design/ant-design/issues/4544#issuecomment-271533135)) | `false` |
|
| loading | Loading status of table | boolean\|[object](/components/spin/#API) ([more](https://github.com/ant-design/ant-design/issues/4544#issuecomment-271533135)) | `false` |
|
||||||
| locale | i18n text including filter, sort, empty text, etc | object | filterConfirm: 'Ok' <br> filterReset: 'Reset' <br> emptyText: 'No Data' <br> [Default](https://github.com/ant-design/ant-design/issues/575#issuecomment-159169511) |
|
| locale | i18n text including filter, sort, empty text, etc | object | filterConfirm: 'Ok' <br> filterReset: 'Reset' <br> emptyText: 'No Data' <br> [Default](https://github.com/ant-design/ant-design/issues/575#issuecomment-159169511) |
|
||||||
| pagination | Config of pagination. You can ref table pagination [config](#pagination) or full [`pagination`](/components/pagination/) document, hide it by setting it to `false` | object | - |
|
| pagination | Config of pagination. You can ref table pagination [config](#pagination) or full [`pagination`](/components/pagination/) document, hide it by setting it to `false` | object | - |
|
||||||
| rowClassName | Row's className | Function(record, index):string | - |
|
| rowClassName | Row's className | Function(record, index):string | - |
|
||||||
|
@ -71,7 +71,7 @@ const columns = [
|
|||||||
| dataSource | 数据数组 | any\[] | - |
|
| dataSource | 数据数组 | any\[] | - |
|
||||||
| expandable | 配置展开属性 | [expandable](#expandable) | - |
|
| expandable | 配置展开属性 | [expandable](#expandable) | - |
|
||||||
| footer | 表格尾部 | Function(currentPageData) | - |
|
| footer | 表格尾部 | Function(currentPageData) | - |
|
||||||
| loading | 页面是否加载中 | boolean\|[object](https://ant.design/components/spin-cn/#API) ([更多](https://github.com/ant-design/ant-design/issues/4544#issuecomment-271533135)) | false |
|
| loading | 页面是否加载中 | boolean\|[object](/components/spin/#API) ([更多](https://github.com/ant-design/ant-design/issues/4544#issuecomment-271533135)) | false |
|
||||||
| locale | 默认文案设置,目前包括排序、过滤、空数据文案 | object | filterConfirm: '确定' <br> filterReset: '重置' <br> emptyText: '暂无数据' <br> [默认值](https://github.com/ant-design/ant-design/issues/575#issuecomment-159169511) |
|
| locale | 默认文案设置,目前包括排序、过滤、空数据文案 | object | filterConfirm: '确定' <br> filterReset: '重置' <br> emptyText: '暂无数据' <br> [默认值](https://github.com/ant-design/ant-design/issues/575#issuecomment-159169511) |
|
||||||
| pagination | 分页器,参考[配置项](#pagination)或 [pagination](/components/pagination/) 文档,设为 false 时不展示和进行分页 | object | - |
|
| pagination | 分页器,参考[配置项](#pagination)或 [pagination](/components/pagination/) 文档,设为 false 时不展示和进行分页 | object | - |
|
||||||
| rowClassName | 表格行的类名 | Function(record, index):string | - |
|
| rowClassName | 表格行的类名 | Function(record, index):string | - |
|
||||||
|
@ -507,6 +507,8 @@
|
|||||||
// ============================ Fixed =============================
|
// ============================ Fixed =============================
|
||||||
&-cell-fix-left,
|
&-cell-fix-left,
|
||||||
&-cell-fix-right {
|
&-cell-fix-right {
|
||||||
|
position: -webkit-sticky !important;
|
||||||
|
position: sticky !important;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
background: @table-bg;
|
background: @table-bg;
|
||||||
}
|
}
|
||||||
|
@ -261,10 +261,10 @@
|
|||||||
.@{tab-prefix-cls}-top-content,
|
.@{tab-prefix-cls}-top-content,
|
||||||
.@{tab-prefix-cls}-bottom-content {
|
.@{tab-prefix-cls}-bottom-content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
> .@{tab-prefix-cls}-tabpane {
|
> .@{tab-prefix-cls}-tabpane {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
-webkit-backface-visibility: hidden;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transition: opacity 0.45s;
|
transition: opacity 0.45s;
|
||||||
}
|
}
|
||||||
|
@ -222,6 +222,51 @@ exports[`renders ./components/time-picker/demo/basic.md correctly 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`renders ./components/time-picker/demo/colored-popup.md correctly 1`] = `
|
||||||
|
<div
|
||||||
|
class="ant-picker"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-picker-input"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
placeholder="Select time"
|
||||||
|
readonly=""
|
||||||
|
size="10"
|
||||||
|
title=""
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class="ant-picker-suffix"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-label="clock-circle"
|
||||||
|
class="anticon anticon-clock-circle"
|
||||||
|
role="img"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="clock-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 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="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`renders ./components/time-picker/demo/disabled.md correctly 1`] = `
|
exports[`renders ./components/time-picker/demo/disabled.md correctly 1`] = `
|
||||||
<div
|
<div
|
||||||
class="ant-picker ant-picker-disabled"
|
class="ant-picker ant-picker-disabled"
|
||||||
|
@ -61,4 +61,15 @@ describe('TimePicker', () => {
|
|||||||
);
|
);
|
||||||
expect(wrapper.render()).toMatchSnapshot();
|
expect(wrapper.render()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should pass popupClassName prop to Picker as dropdownClassName prop', () => {
|
||||||
|
const popupClassName = 'myCustomClassName';
|
||||||
|
const wrapper = mount(
|
||||||
|
<TimePicker
|
||||||
|
defaultOpenValue={moment('00:00:00', 'HH:mm:ss')}
|
||||||
|
popupClassName={popupClassName}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
expect(wrapper.find('Picker').prop('dropdownClassName')).toEqual(popupClassName);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
39
components/time-picker/demo/colored-popup.md
Normal file
39
components/time-picker/demo/colored-popup.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
order: 9
|
||||||
|
title:
|
||||||
|
zh-CN: 色付きポップアップ
|
||||||
|
en-US: Colored Popup
|
||||||
|
debug: true
|
||||||
|
---
|
||||||
|
|
||||||
|
## zh-CN
|
||||||
|
|
||||||
|
カスタムクラスを `TimePicker`ポップアップに渡す
|
||||||
|
|
||||||
|
## en-US
|
||||||
|
|
||||||
|
Passing custom class to `TimePicker` popup
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import { TimePicker } from 'antd';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
const onChange = (time, timeString) => {
|
||||||
|
console.log(time, timeString);
|
||||||
|
};
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<TimePicker
|
||||||
|
onChange={onChange}
|
||||||
|
defaultOpenValue={moment('00:00:00', 'HH:mm:ss')}
|
||||||
|
popupClassName="myCustomClassName"
|
||||||
|
/>,
|
||||||
|
mountNode,
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
```css
|
||||||
|
.myCustomClassName .ant-picker-time-panel-cell-inner {
|
||||||
|
color: red !important;
|
||||||
|
}
|
||||||
|
```
|
@ -41,7 +41,7 @@ import moment from 'moment';
|
|||||||
| minuteStep | interval between minutes in picker | number | 1 | |
|
| minuteStep | interval between minutes in picker | number | 1 | |
|
||||||
| open | whether to popup panel | boolean | false | |
|
| open | whether to popup panel | boolean | false | |
|
||||||
| placeholder | display when there's no value | string | "Select a time" | |
|
| placeholder | display when there's no value | string | "Select a time" | |
|
||||||
| popupClassName | className of panel | string | '' | |
|
| popupClassName | className of panel | string | - | |
|
||||||
| popupStyle | style of panel | object | - | |
|
| popupStyle | style of panel | object | - | |
|
||||||
| secondStep | interval between seconds in picker | number | 1 | |
|
| secondStep | interval between seconds in picker | number | 1 | |
|
||||||
| suffixIcon | The custom suffix icon | ReactNode | - | |
|
| suffixIcon | The custom suffix icon | ReactNode | - | |
|
||||||
|
@ -20,10 +20,11 @@ const RangePicker = React.forwardRef<any, TimeRangePickerProps>((props, ref) =>
|
|||||||
|
|
||||||
export interface TimePickerProps extends Omit<PickerTimeProps<Moment>, 'picker'> {
|
export interface TimePickerProps extends Omit<PickerTimeProps<Moment>, 'picker'> {
|
||||||
addon?: () => React.ReactNode;
|
addon?: () => React.ReactNode;
|
||||||
|
popupClassName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TimePicker = React.forwardRef<any, TimePickerProps>(
|
const TimePicker = React.forwardRef<any, TimePickerProps>(
|
||||||
({ addon, renderExtraFooter, ...restProps }, ref) => {
|
({ addon, renderExtraFooter, popupClassName, ...restProps }, ref) => {
|
||||||
const internalRenderExtraFooter = React.useMemo(() => {
|
const internalRenderExtraFooter = React.useMemo(() => {
|
||||||
if (renderExtraFooter) {
|
if (renderExtraFooter) {
|
||||||
return renderExtraFooter;
|
return renderExtraFooter;
|
||||||
@ -42,6 +43,7 @@ const TimePicker = React.forwardRef<any, TimePickerProps>(
|
|||||||
return (
|
return (
|
||||||
<InternalTimePicker
|
<InternalTimePicker
|
||||||
{...restProps}
|
{...restProps}
|
||||||
|
dropdownClassName={popupClassName}
|
||||||
mode={undefined}
|
mode={undefined}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
renderExtraFooter={internalRenderExtraFooter}
|
renderExtraFooter={internalRenderExtraFooter}
|
||||||
|
@ -42,7 +42,7 @@ import moment from 'moment';
|
|||||||
| minuteStep | 分钟选项间隔 | number | 1 | |
|
| minuteStep | 分钟选项间隔 | number | 1 | |
|
||||||
| open | 面板是否打开 | boolean | false | |
|
| open | 面板是否打开 | boolean | false | |
|
||||||
| placeholder | 没有值的时候显示的内容 | string | "请选择时间" | |
|
| placeholder | 没有值的时候显示的内容 | string | "请选择时间" | |
|
||||||
| popupClassName | 弹出层类名 | string | '' | |
|
| popupClassName | 弹出层类名 | string | - | |
|
||||||
| popupStyle | 弹出层样式对象 | object | - | |
|
| popupStyle | 弹出层样式对象 | object | - | |
|
||||||
| secondStep | 秒选项间隔 | number | 1 | |
|
| secondStep | 秒选项间隔 | number | 1 | |
|
||||||
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
|
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
|
||||||
|
@ -128,18 +128,8 @@ describe('Tooltip', () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Tooltip>,
|
</Tooltip>,
|
||||||
);
|
);
|
||||||
expect(
|
expect(wrapper1.find('span').first().getDOMNode().style.display).toBe('inline-block');
|
||||||
wrapper1
|
expect(wrapper2.find('span').first().getDOMNode().style.display).toBe('block');
|
||||||
.find('span')
|
|
||||||
.first()
|
|
||||||
.getDOMNode().style.display,
|
|
||||||
).toBe('inline-block');
|
|
||||||
expect(
|
|
||||||
wrapper2
|
|
||||||
.find('span')
|
|
||||||
.first()
|
|
||||||
.getDOMNode().style.display,
|
|
||||||
).toBe('block');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should works for arrowPointAtCenter', () => {
|
it('should works for arrowPointAtCenter', () => {
|
||||||
@ -161,10 +151,7 @@ describe('Tooltip', () => {
|
|||||||
</button>
|
</button>
|
||||||
</Tooltip>,
|
</Tooltip>,
|
||||||
);
|
);
|
||||||
wrapper
|
wrapper.find('button').at(0).simulate('click');
|
||||||
.find('button')
|
|
||||||
.at(0)
|
|
||||||
.simulate('click');
|
|
||||||
const popupLeftDefault = parseInt(wrapper.instance().getPopupDomNode().style.left, 10);
|
const popupLeftDefault = parseInt(wrapper.instance().getPopupDomNode().style.left, 10);
|
||||||
|
|
||||||
const wrapper2 = mount(
|
const wrapper2 = mount(
|
||||||
@ -181,10 +168,7 @@ describe('Tooltip', () => {
|
|||||||
</button>
|
</button>
|
||||||
</Tooltip>,
|
</Tooltip>,
|
||||||
);
|
);
|
||||||
wrapper2
|
wrapper2.find('button').at(0).simulate('click');
|
||||||
.find('button')
|
|
||||||
.at(0)
|
|
||||||
.simulate('click');
|
|
||||||
const popupLeftArrowPointAtCenter = parseInt(
|
const popupLeftArrowPointAtCenter = parseInt(
|
||||||
wrapper2.instance().getPopupDomNode().style.left,
|
wrapper2.instance().getPopupDomNode().style.left,
|
||||||
10,
|
10,
|
||||||
@ -272,22 +256,26 @@ describe('Tooltip', () => {
|
|||||||
}).not.toThrow();
|
}).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('support other placement', async () => {
|
it('support other placement', done => {
|
||||||
const wrapper = mount(
|
const wrapper = mount(
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title="xxxxx"
|
title="xxxxx"
|
||||||
placement="bottomLeft"
|
placement="bottomLeft"
|
||||||
visible
|
|
||||||
transitionName=""
|
transitionName=""
|
||||||
mouseEnterDelay={0}
|
mouseEnterDelay={0}
|
||||||
|
afterVisibleChange={visible => {
|
||||||
|
if (visible) {
|
||||||
|
expect(wrapper.find('Trigger').props().popupPlacement).toBe('bottomLeft');
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<span>
|
<span>Hello world!</span>
|
||||||
Hello world!
|
|
||||||
</span>
|
|
||||||
</Tooltip>,
|
</Tooltip>,
|
||||||
);
|
);
|
||||||
await sleep(1000);
|
expect(wrapper.find('span')).toHaveLength(1);
|
||||||
expect(wrapper.instance().getPopupDomNode().className).toContain('placement-bottomLeft');
|
const button = wrapper.find('span').at(0);
|
||||||
|
button.simulate('mouseenter');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('other placement when mouse enter', async () => {
|
it('other placement when mouse enter', async () => {
|
||||||
@ -296,18 +284,17 @@ describe('Tooltip', () => {
|
|||||||
title="xxxxx"
|
title="xxxxx"
|
||||||
placement="topRight"
|
placement="topRight"
|
||||||
transitionName=""
|
transitionName=""
|
||||||
|
popupTransitionName=""
|
||||||
mouseEnterDelay={0}
|
mouseEnterDelay={0}
|
||||||
>
|
>
|
||||||
<span>
|
<span>Hello world!</span>
|
||||||
Hello world!
|
|
||||||
</span>
|
|
||||||
</Tooltip>,
|
</Tooltip>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(wrapper.find('span')).toHaveLength(1);
|
expect(wrapper.find('span')).toHaveLength(1);
|
||||||
const button = wrapper.find('span').at(0);
|
const button = wrapper.find('span').at(0);
|
||||||
button.simulate('mouseenter');
|
button.simulate('mouseenter');
|
||||||
await sleep(300);
|
await sleep(500);
|
||||||
expect(wrapper.instance().getPopupDomNode().className).toContain('placement-topRight');
|
expect(wrapper.instance().getPopupDomNode().className).toContain('placement-topRight');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user