diff --git a/.husky/.gitignore b/.husky/.gitignore
new file mode 100644
index 0000000000..31354ec138
--- /dev/null
+++ b/.husky/.gitignore
@@ -0,0 +1 @@
+_
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100755
index 0000000000..f91359dc2a
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1,4 @@
+#!/bin/sh
+. "$(dirname "$0")/_/husky.sh"
+
+npx --no-install pretty-quick --staged
diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md
index c9c1af2a95..7d97a07e0b 100644
--- a/CHANGELOG.en-US.md
+++ b/CHANGELOG.en-US.md
@@ -15,6 +15,20 @@ timeline: true
---
+## 4.15.2
+
+`2021-04-19`
+
+- 🐞 Fix Tabs `centered` doesn't work. [#30106](https://github.com/ant-design/ant-design/pull/30106) [@kerm1it](https://github.com/kerm1it)
+- 💄 Fix Button `type="link"` disabled cursor style missing. [#30197](https://github.com/ant-design/ant-design/pull/30197)
+- 🐞 Fix TextArea `onChange` event return `target` object is not a HTMLTextArea object. [#30124](https://github.com/ant-design/ant-design/pull/30124)
+- 🐞 Files in Upload `fileList` will always have uid. [#30087](https://github.com/ant-design/ant-design/pull/30087) [@jameslahm](https://github.com/jameslahm)
+- 💄 Fix Badge toggle style when `status` is `warning`. [#30090](https://github.com/ant-design/ant-design/pull/30090) [@jameslahm](https://github.com/jameslahm)
+- 🌐 Add Image component `preview` text in zh_HK and zh_TW. [#30100](https://github.com/ant-design/ant-design/pull/30100) [@jameslahm](https://github.com/jameslahm)
+- 🌐 add da_DK locale TimePicker.RangePicker. [#30128](https://github.com/ant-design/ant-design/pull/30128) [@themitvp](https://github.com/themitvp)
+- TypeScript
+ - 🤖 Modify LIST_IGNORE field is of string type. [#30188](https://github.com/ant-design/ant-design/pull/30188) [@hello-chinese](https://github.com/hello-chinese)
+
## 4.15.1
`2021-04-10`
diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md
index 633e6bb74e..ce07366394 100644
--- a/CHANGELOG.zh-CN.md
+++ b/CHANGELOG.zh-CN.md
@@ -15,6 +15,20 @@ timeline: true
---
+## 4.15.2
+
+`2021-04-19`
+
+- 🐞 修复 Tabs `centered` 失效问题。[#30106](https://github.com/ant-design/ant-design/pull/30106) [@kerm1it](https://github.com/kerm1it)
+- 💄 修复 Badge `status="warning"` 时切换样式异常的问题。[#30090](https://github.com/ant-design/ant-design/pull/30090) [@jameslahm](https://github.com/jameslahm)
+- 💄 修复 Button 为 `type="link"` 时禁用手型丢失的问题。[#30197](https://github.com/ant-design/ant-design/pull/30197)
+- 🐞 修复 TextArea `onChange` 事件返回 `target` 对象不是 HTMLTextArea 对象的问题。[#30124](https://github.com/ant-design/ant-design/pull/30124)
+- 🐞 Upload `fileList` 中的文件将总是有 uid。[#30087](https://github.com/ant-design/ant-design/pull/30087) [@jameslahm](https://github.com/jameslahm)
+- 🌐 补充遗漏的 TimePicker 丹麦语国际化。[#30128](https://github.com/ant-design/ant-design/pull/30128) [@themitvp](https://github.com/themitvp)
+- 🌐 增加 Image 组件 `preview` 繁體文案。[#30100](https://github.com/ant-design/ant-design/pull/30100) [@jameslahm](https://github.com/jameslahm)
+- TypeScript
+ - 🤖 修改 LIST_IGNORE 字段为 string 类型。[#30188](https://github.com/ant-design/ant-design/pull/30188) [@hello-chinese](https://github.com/hello-chinese)
+
## 4.15.1
`2021-04-10`
diff --git a/components/badge/index.tsx b/components/badge/index.tsx
index 5f058c46b1..578f11d1a8 100644
--- a/components/badge/index.tsx
+++ b/components/badge/index.tsx
@@ -191,7 +191,7 @@ const Badge: CompoundedComponent = ({
[`${prefixCls}-count`]: !isDot && !isHidden,
[`${prefixCls}-count-sm`]: !isDot && !isHidden && size === 'small',
[`${prefixCls}-multiple-words`]:
- !isDot && displayCount && displayCount?.toString().length > 1,
+ !isDot && displayCount && displayCount.toString().length > 1,
[`${prefixCls}-status-${status}`]: !!status,
[`${prefixCls}-status-${color}`]: isPresetColor(color),
});
diff --git a/components/breadcrumb/BreadcrumbItem.tsx b/components/breadcrumb/BreadcrumbItem.tsx
index 1744211a8f..fd29f24a85 100644
--- a/components/breadcrumb/BreadcrumbItem.tsx
+++ b/components/breadcrumb/BreadcrumbItem.tsx
@@ -62,7 +62,7 @@ const BreadcrumbItem: BreadcrumbItemInterface = ({
return (
{link}
- {separator && separator !== '' && (
+ {separator && (
{separator}
)}
diff --git a/components/button/__tests__/index.test.tsx b/components/button/__tests__/index.test.tsx
index 944ce28fff..cb20998415 100644
--- a/components/button/__tests__/index.test.tsx
+++ b/components/button/__tests__/index.test.tsx
@@ -300,4 +300,15 @@ describe('Button', () => {
},
});
});
+
+ it('should not redirect when button is disabled', () => {
+ const onClick = jest.fn();
+ const wrapper = mount(
+ ,
+ );
+ wrapper.simulate('click');
+ expect(onClick).not.toHaveBeenCalled();
+ });
});
diff --git a/components/button/button.tsx b/components/button/button.tsx
index fe86a35883..4a8a2b4566 100644
--- a/components/button/button.tsx
+++ b/components/button/button.tsx
@@ -193,8 +193,10 @@ const InternalButton: React.ForwardRefRenderFunction = (pr
React.useEffect(fixTwoCNChar, [buttonRef]);
const handleClick = (e: React.MouseEvent) => {
- const { onClick } = props;
- if (innerLoading) {
+ const { onClick, disabled } = props;
+ // https://github.com/ant-design/ant-design/issues/30207
+ if (innerLoading || disabled) {
+ e.preventDefault();
return;
}
(onClick as React.MouseEventHandler)?.(e);
diff --git a/components/dropdown/__tests__/image.test.ts b/components/dropdown/__tests__/image.test.ts
index 19c1b959f8..ca5dea8239 100644
--- a/components/dropdown/__tests__/image.test.ts
+++ b/components/dropdown/__tests__/image.test.ts
@@ -1,5 +1,5 @@
import { imageDemoTest } from '../../../tests/shared/imageTest';
describe('Dropdown image', () => {
- imageDemoTest('dropdown');
+ imageDemoTest('dropdown', { skip: ['dropdown-button.md'] });
});
diff --git a/components/form/demo/advanced-search.md b/components/form/demo/advanced-search.md
index a000c7b37f..e2ee2be8cb 100644
--- a/components/form/demo/advanced-search.md
+++ b/components/form/demo/advanced-search.md
@@ -11,7 +11,7 @@ title:
有部分定制的样式代码,由于输入标签长度不确定,需要根据具体情况自行调整。
-> 🛎️ 想要 3 分钟实现? 试试 ProFrom 的[查询表单](https://procomponents.ant.design/components/form#%E6%9F%A5%E8%AF%A2%E7%AD%9B%E9%80%89)!
+> 🛎️ 想要 3 分钟实现? 试试 ProForm 的[查询表单](https://procomponents.ant.design/components/form#%E6%9F%A5%E8%AF%A2%E7%AD%9B%E9%80%89)!
## en-US
diff --git a/components/form/demo/form-in-modal.md b/components/form/demo/form-in-modal.md
index c4fa1b2dbd..bf63d2698c 100644
--- a/components/form/demo/form-in-modal.md
+++ b/components/form/demo/form-in-modal.md
@@ -9,7 +9,7 @@ title:
当用户访问一个展示了某个列表的页面,想新建一项但又不想跳转页面时,可以用 Modal 弹出一个表单,用户填写必要信息后创建新的项。
-> 🛎️ 想要 3 分钟实现?试试 ProFrom 的 [Modal 表单](https://procomponents.ant.design/components/form#modal-%E8%A1%A8%E5%8D%95)!
+> 🛎️ 想要 3 分钟实现?试试 ProForm 的 [Modal 表单](https://procomponents.ant.design/components/form#modal-%E8%A1%A8%E5%8D%95)!
## en-US
diff --git a/components/form/demo/normal-login.md b/components/form/demo/normal-login.md
index fb51679f47..a8080c2936 100644
--- a/components/form/demo/normal-login.md
+++ b/components/form/demo/normal-login.md
@@ -9,7 +9,7 @@ title:
普通的登录框,可以容纳更多的元素。
-> 🛎️ 想要 3 分钟实现?试试 [ProFrom](https://procomponents.ant.design/components/form/#%E7%99%BB%E5%BD%95)!
+> 🛎️ 想要 3 分钟实现?试试 [ProForm](https://procomponents.ant.design/components/form/#%E7%99%BB%E5%BD%95)!
## en-US
diff --git a/components/form/index.en-US.md b/components/form/index.en-US.md
index 2f87a802aa..707cf03508 100644
--- a/components/form/index.en-US.md
+++ b/components/form/index.en-US.md
@@ -79,7 +79,7 @@ Form field component for data bidirectional binding, validation, layout, and so
| getValueProps | Additional props with sub component | (value: any) => any | - | 4.2.0 |
| hasFeedback | Used with `validateStatus`, this option specifies the validation status icon. Recommended to be used only with `Input` | boolean | false | |
| help | The prompt message. If not provided, the prompt message will be generated by the validation rule. | ReactNode | - | |
-| hidden | Whether to hide Form.Item (still collect and validate value) | boolean | false | |
+| hidden | Whether to hide Form.Item (still collect and validate value) | boolean | false | 4.4.0 |
| htmlFor | Set sub label `htmlFor` | string | - | |
| initialValue | Config sub default value. Form `initialValues` get higher priority when conflict | string | - | 4.2.0 |
| label | Label text | ReactNode | - | |
@@ -203,9 +203,9 @@ Some operator functions in render form of Form.List.
New in 4.7.0. Show error messages, should only work with `rules` of Form.List. See [example](#components-form-demo-dynamic-form-item).
-| Property | Description | Type | Default |
-| --- | --- | --- | --- |
-| errors | Error list | ReactNode\[] | - |
+| Property | Description | Type | Default |
+| -------- | ----------- | ------------ | ------- |
+| errors | Error list | ReactNode\[] | - |
## Form.Provider
@@ -286,13 +286,13 @@ validateFields()
#### FieldData
-| Name | Description | Type |
-| --- | --- | --- |
-| errors | Error messages | string\[] |
-| name | Field name path | [NamePath](#NamePath)\[] |
-| touched | Whether is operated | boolean |
-| validating | Whether is in validating | boolean |
-| value | Field value | any |
+| Name | Description | Type |
+| ---------- | ------------------------ | ------------------------ |
+| errors | Error messages | string\[] |
+| name | Field name path | [NamePath](#NamePath)\[] |
+| touched | Whether is operated | boolean |
+| validating | Whether is in validating | boolean |
+| value | Field value | any |
#### Rule
@@ -435,3 +435,7 @@ React can not get correct interaction of controlled component with async value u
See similar issues: [#28370](https://github.com/ant-design/ant-design/issues/28370) [#27994](https://github.com/ant-design/ant-design/issues/27994)
`scrollToFirstError` and `scrollToField` deps on `id` attribute passed to form control, please mark sure that it hasn't been ignored in your custom form control. Check [codesandbox](https://codesandbox.io/s/antd-reproduction-template-forked-25nul?file=/index.js) for solution.
+
+### `setFieldsValue` do not trigger `onFieldsChange` or `onValuesChange`?
+
+It's by design. Only user interactive can trigger the change event. This design is aim to avoid call `setFieldsValue` in change event which may makes loop calling.
diff --git a/components/form/index.zh-CN.md b/components/form/index.zh-CN.md
index a647617ea9..84ac879165 100644
--- a/components/form/index.zh-CN.md
+++ b/components/form/index.zh-CN.md
@@ -34,7 +34,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/ORmcdeaoO/Form.svg
| scrollToFirstError | 提交失败自动滚动到第一个错误字段 | boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options) | false | |
| size | 设置字段组件的尺寸(仅限 antd 组件) | `small` \| `middle` \| `large` | - | |
| validateMessages | 验证提示模板,说明[见下](#validateMessages) | [ValidateMessages](https://github.com/react-component/field-form/blob/master/src/utils/messages.ts) | - | |
-| validateTrigger | 统一设置字段校验规则 | string \| string\[] | `onChange` | 4.3.0 |
+| validateTrigger | 统一设置字段触发验证的时机 | string \| string\[] | `onChange` | 4.3.0 |
| wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol | [object](/components/grid/#Col) | - | |
| onFieldsChange | 字段更新时触发回调事件 | function(changedFields, allFields) | - | |
| onFinish | 提交表单且数据验证成功后回调事件 | function(values) | - | |
@@ -80,7 +80,7 @@ const validateMessages = {
| getValueProps | 为子元素添加额外的属性 | (value: any) => any | - | 4.2.0 |
| hasFeedback | 配合 `validateStatus` 属性使用,展示校验状态图标,建议只配合 Input 组件使用 | boolean | false | |
| help | 提示信息,如不设置,则会根据校验规则自动生成 | ReactNode | - | |
-| hidden | 是否隐藏字段(依然会收集和校验字段) | boolean | false | |
+| hidden | 是否隐藏字段(依然会收集和校验字段) | boolean | false | 4.4.0 |
| htmlFor | 设置子元素 label `htmlFor` 属性 | string | - | |
| initialValue | 设置子元素默认值,如果与 Form 的 `initialValues` 冲突则以 Form 为准 | string | - | 4.2.0 |
| label | `label` 标签的文本 | ReactNode | - | |
@@ -192,19 +192,19 @@ Form 通过增量更新方式,只更新被修改的字段相关组件以达到
Form.List 渲染表单相关操作函数。
-| 参数 | 说明 | 类型 | 默认值 |
-| --- | --- | --- | --- |
-| add | 新增表单项 | (defaultValue?: any, insertIndex?: number) => void | insertIndex: 4.6.0 |
-| move | 移动表单项 | (from: number, to: number) => void | - |
-| remove | 删除表单项 | (index: number \| number\[]) => void | number\[]: 4.5.0 |
+| 参数 | 说明 | 类型 | 默认值 |
+| ------ | ---------- | -------------------------------------------------- | ------------------ |
+| add | 新增表单项 | (defaultValue?: any, insertIndex?: number) => void | insertIndex: 4.6.0 |
+| move | 移动表单项 | (from: number, to: number) => void | - |
+| remove | 删除表单项 | (index: number \| number\[]) => void | number\[]: 4.5.0 |
## Form.ErrorList
4.7.0 新增。错误展示组件,仅限配合 Form.List 的 rules 一同使用。参考[示例](#components-form-demo-dynamic-form-item)。
-| 参数 | 说明 | 类型 | 默认值 |
-| --- | --- | --- | --- |
-| errors | 错误列表 | ReactNode\[] | - |
+| 参数 | 说明 | 类型 | 默认值 |
+| ------ | -------- | ------------ | ------ |
+| errors | 错误列表 | ReactNode\[] | - |
## Form.Provider
@@ -285,13 +285,13 @@ validateFields()
#### FieldData
-| 名称 | 说明 | 类型 |
-| --- | --- | --- |
-| errors | 错误信息 | string\[] |
-| name | 字段名称 | [NamePath](#NamePath)\[] |
-| touched | 是否被用户操作过 | boolean |
-| validating | 是否正在校验 | boolean |
-| value | 字段对应值 | any |
+| 名称 | 说明 | 类型 |
+| ---------- | ---------------- | ------------------------ |
+| errors | 错误信息 | string\[] |
+| name | 字段名称 | [NamePath](#NamePath)\[] |
+| touched | 是否被用户操作过 | boolean |
+| validating | 是否正在校验 | boolean |
+| value | 字段对应值 | any |
#### Rule
@@ -434,3 +434,7 @@ React 中异步更新会导致受控组件交互行为异常。当用户交互
类似问题:[#28370](https://github.com/ant-design/ant-design/issues/28370) [#27994](https://github.com/ant-design/ant-design/issues/27994)
滚动依赖于表单控件元素上绑定的 `id` 字段,如果自定义控件没有将 `id` 赋到正确的元素上,这个功能将失效。你可以参考这个 [codesandbox](https://codesandbox.io/s/antd-reproduction-template-forked-25nul?file=/index.js)。
+
+### `setFieldsValue` 不会触发 `onFieldsChange` 和 `onValuesChange`?
+
+是的,change 事件仅当用户交互才会触发。该设计是为了防止在 change 事件中调用 `setFieldsValue` 导致的循环问题。
diff --git a/components/input/TextArea.tsx b/components/input/TextArea.tsx
index ae11bfa08f..815ea26555 100644
--- a/components/input/TextArea.tsx
+++ b/components/input/TextArea.tsx
@@ -86,7 +86,7 @@ const TextArea = React.forwardRef(
// Patch composition onChange when value changed
if (triggerValue !== value) {
handleSetValue(triggerValue);
- resolveOnChange(innerRef.current as any, e, onChange, triggerValue);
+ resolveOnChange(e.currentTarget, e, onChange, triggerValue);
}
onCompositionEnd?.(e);
@@ -99,7 +99,7 @@ const TextArea = React.forwardRef(
}
handleSetValue(triggerValue);
- resolveOnChange(innerRef.current as any, e, onChange, triggerValue);
+ resolveOnChange(e.currentTarget, e, onChange, triggerValue);
};
// ============================== Reset ===============================
@@ -107,7 +107,7 @@ const TextArea = React.forwardRef(
handleSetValue('', () => {
innerRef.current?.focus();
});
- resolveOnChange(innerRef.current as any, e, onChange);
+ resolveOnChange(innerRef.current?.resizableTextArea?.textArea!, e, onChange);
};
const prefixCls = getPrefixCls('input', customizePrefixCls);
diff --git a/components/input/__tests__/textarea.test.js b/components/input/__tests__/textarea.test.js
index c17dd1886f..1bb392f70c 100644
--- a/components/input/__tests__/textarea.test.js
+++ b/components/input/__tests__/textarea.test.js
@@ -379,4 +379,36 @@ describe('TextArea allowClear', () => {
const wrapper = mount();
expect(wrapper.find('textarea').at(0).getDOMNode().value).toBe('Light');
});
+
+ it('onChange event should return HTMLInputElement', () => {
+ const onChange = jest.fn();
+ const wrapper = mount();
+
+ function isNativeElement() {
+ expect(onChange).toHaveBeenCalledWith(
+ expect.objectContaining({
+ target: wrapper.find('textarea').instance(),
+ }),
+ );
+
+ onChange.mockReset();
+ }
+
+ // Change
+ wrapper.find('textarea').simulate('change', {
+ target: {
+ value: 'bamboo',
+ },
+ });
+ isNativeElement();
+
+ // Composition End
+ wrapper.find('textarea').instance().value = 'light'; // enzyme not support change `currentTarget`
+ wrapper.find('textarea').simulate('compositionEnd');
+ isNativeElement();
+
+ // Reset
+ wrapper.find('.ant-input-clear-icon').first().simulate('click');
+ isNativeElement();
+ });
});
diff --git a/components/pagination/index.en-US.md b/components/pagination/index.en-US.md
index e0c0416453..6ac047ab63 100644
--- a/components/pagination/index.en-US.md
+++ b/components/pagination/index.en-US.md
@@ -38,5 +38,5 @@ A long list can be divided into several pages using `Pagination`, and only one p
| simple | Whether to use simple mode | boolean | - | |
| size | Specify the size of `Pagination`, can be set to `small` | `default` \| `small` | `default` | |
| total | Total number of data items | number | 0 | |
-| onChange | Called when the page number is changed, and it takes the resulting page number and pageSize as its arguments | function(page, pageSize) | - | |
+| onChange | Called when the page number or `pageSize` is changed, and it takes the resulting page number and pageSize as its arguments | function(page, pageSize) | - | |
| onShowSizeChange | Called when `pageSize` is changed | function(current, size) | - | |
diff --git a/components/pagination/index.zh-CN.md b/components/pagination/index.zh-CN.md
index fb9c05dee1..3be1589d17 100644
--- a/components/pagination/index.zh-CN.md
+++ b/components/pagination/index.zh-CN.md
@@ -39,5 +39,5 @@ cover: https://gw.alipayobjects.com/zos/alicdn/1vqv2bj68/Pagination.svg
| simple | 当添加该属性时,显示为简单分页 | boolean | - | |
| size | 当为 `small` 时,是小尺寸分页 | `default` \| `small` | `default` | |
| total | 数据总数 | number | 0 | |
-| onChange | 页码改变的回调,参数是改变后的页码及每页条数 | function(page, pageSize) | - | |
+| onChange | 页码或 `pageSize` 改变的回调,参数是改变后的页码及每页条数 | function(page, pageSize) | - | |
| onShowSizeChange | pageSize 变化的回调 | function(current, size) | - | |
diff --git a/components/style/core/global.less b/components/style/core/global.less
index 1b3e8d2849..d4f53103fb 100644
--- a/components/style/core/global.less
+++ b/components/style/core/global.less
@@ -239,7 +239,6 @@ a {
&[disabled] {
color: @disabled-color;
cursor: not-allowed;
- pointer-events: none;
}
}
diff --git a/components/table/hooks/useSelection.tsx b/components/table/hooks/useSelection.tsx
index 3503836a9d..ec822f9897 100644
--- a/components/table/hooks/useSelection.tsx
+++ b/components/table/hooks/useSelection.tsx
@@ -452,7 +452,7 @@ export default function useSelection(
if (expandType === 'nest') {
mergedIndeterminate = indeterminate;
devWarning(
- !(typeof checkboxProps?.indeterminate === 'boolean'),
+ typeof checkboxProps?.indeterminate !== 'boolean',
'Table',
'set `indeterminate` using `rowSelection.getCheckboxProps` is not allowed with tree structured dataSource.',
);
diff --git a/components/table/index.en-US.md b/components/table/index.en-US.md
index 2c8197830a..ccc2f0f4a7 100644
--- a/components/table/index.en-US.md
+++ b/components/table/index.en-US.md
@@ -289,7 +289,7 @@ Besides, the breaking change is changing `dataIndex` from nest string path like
## FAQ
-### How to hide pagination when single page or not data?
+### How to hide pagination when single page or no data?
You can set `hideOnSinglePage` with `pagination` prop.
diff --git a/components/tabs/style/index.less b/components/tabs/style/index.less
index 178e6ea655..44645b3304 100644
--- a/components/tabs/style/index.less
+++ b/components/tabs/style/index.less
@@ -47,14 +47,12 @@
.@{tab-prefix-cls}-nav-list {
position: relative;
display: flex;
- flex: 1 0 auto; // fix safari scroll problem
transition: transform @animation-duration-slow;
}
// >>>>>>>> Operations
.@{tab-prefix-cls}-nav-operations {
display: flex;
- flex: 1 0 auto; // fix safari scroll problem
align-self: stretch;
&-hidden {
diff --git a/components/tabs/style/position.less b/components/tabs/style/position.less
index e015e76969..9da9bb8ad1 100644
--- a/components/tabs/style/position.less
+++ b/components/tabs/style/position.less
@@ -146,6 +146,7 @@
.@{tab-prefix-cls}-nav-list,
.@{tab-prefix-cls}-nav-operations {
+ flex: 1 0 auto; // fix safari scroll problem
flex-direction: column;
}
}
diff --git a/components/time-picker/locale/da_DK.tsx b/components/time-picker/locale/da_DK.tsx
index 6fe3131473..0bf9bf9ca7 100644
--- a/components/time-picker/locale/da_DK.tsx
+++ b/components/time-picker/locale/da_DK.tsx
@@ -2,6 +2,7 @@ import { TimePickerLocale } from '../index';
const locale: TimePickerLocale = {
placeholder: 'Vælg tid',
+ rangePlaceholder: ['Starttidspunkt', 'Sluttidspunkt'],
};
export default locale;
diff --git a/components/upload/Upload.tsx b/components/upload/Upload.tsx
index 0deac73092..28d387f7d9 100644
--- a/components/upload/Upload.tsx
+++ b/components/upload/Upload.tsx
@@ -73,7 +73,7 @@ const InternalUpload: React.ForwardRefRenderFunction = (pr
}, []);
// Control mode will auto fill file uid if not provided
- React.useEffect(() => {
+ React.useMemo(() => {
const timestamp = Date.now();
(fileList || []).forEach((file, index) => {
@@ -403,7 +403,7 @@ interface CompoundedComponent
React.PropsWithChildren & React.RefAttributes
> {
Dragger: typeof Dragger;
- LIST_IGNORE: {};
+ LIST_IGNORE: string;
}
const Upload = React.forwardRef(InternalUpload) as CompoundedComponent;
diff --git a/components/upload/__tests__/upload.test.js b/components/upload/__tests__/upload.test.js
index 989b5f4c83..fe7c048777 100644
--- a/components/upload/__tests__/upload.test.js
+++ b/components/upload/__tests__/upload.test.js
@@ -1,12 +1,12 @@
/* eslint-disable react/no-string-refs, react/prefer-es6-class */
import React from 'react';
-import { mount } from 'enzyme';
+import { mount, render } from 'enzyme';
import { act } from 'react-dom/test-utils';
import produce from 'immer';
import { cloneDeep } from 'lodash';
import Upload from '..';
import Form from '../../form';
-import { getFileItem, removeFileItem } from '../utils';
+import { getFileItem, removeFileItem, isImageUrl } from '../utils';
import { setup, teardown } from './mock';
import { resetWarned } from '../../_util/devWarning';
import mountTest from '../../../tests/shared/mountTest';
@@ -285,6 +285,20 @@ describe('Upload', () => {
jest.useRealTimers();
});
+ it('should be able to get uid at first', () => {
+ const fileList = [
+ {
+ name: 'foo.png',
+ status: 'done',
+ url: 'http://www.baidu.com/xxx.png',
+ },
+ ];
+ render();
+ fileList.forEach(file => {
+ expect(file.uid).toBeDefined();
+ });
+ });
+
describe('util', () => {
it('should be able to get fileItem', () => {
const file = { uid: '-1', name: 'item.jpg' };
@@ -353,6 +367,13 @@ describe('Upload', () => {
const targetItem = removeFileItem(file, fileList);
expect(targetItem).toBe(null);
});
+
+ it('isImageUrl should work correctly when file.url is null', () => {
+ const file = {
+ url: null,
+ };
+ expect(isImageUrl(file)).toBe(true);
+ });
});
it('should support linkProps as object', () => {
diff --git a/components/upload/utils.tsx b/components/upload/utils.tsx
index 178d558db6..1499436a1f 100644
--- a/components/upload/utils.tsx
+++ b/components/upload/utils.tsx
@@ -54,7 +54,7 @@ export const isImageUrl = (file: UploadFile): boolean => {
if (file.type && !file.thumbUrl) {
return isImageFileType(file.type);
}
- const url: string = (file.thumbUrl || file.url) as string;
+ const url: string = (file.thumbUrl || file.url || '') as string;
const extension = extname(url);
if (
/^data:image\//.test(url) ||
diff --git a/docs/react/customize-theme.en-US.md b/docs/react/customize-theme.en-US.md
index 2a69eac1a4..ccff12a204 100644
--- a/docs/react/customize-theme.en-US.md
+++ b/docs/react/customize-theme.en-US.md
@@ -125,7 +125,7 @@ Where the src/myStylesReplacement.less file loads the same files as the index.le
### Use a postcss processor to scope all styles
-See an example of usage with gulp and [postcss-prefixwrap](https://github.com/dbtedman/postcss-prefixwrap) : https://gist.github.com/sbusch/a90eafaf5a5b61c6d6172da6ff76ddaa
+See an example of usage with [gulp and postcss-prefixwrap](https://gist.github.com/sbusch/a90eafaf5a5b61c6d6172da6ff76ddaa).
## Not working?
diff --git a/docs/react/recommendation.en-US.md b/docs/react/recommendation.en-US.md
index 3a0b8a5042..4fd7c95e5b 100644
--- a/docs/react/recommendation.en-US.md
+++ b/docs/react/recommendation.en-US.md
@@ -9,8 +9,9 @@ title: Third-Party Libraries
| --- | --- |
| Visualization and charts | [AntV Data Visualization](https://antv.vision/en) [🔥 AntV Charting Library](https://g2plot.antv.vision/en) [BizCharts](https://github.com/alibaba/BizCharts) [recharts](https://github.com/recharts/recharts/) [viser](https://viserjs.github.io/) |
| React Hooks Library | [ahooks](https://github.com/alibaba/hooks) |
+| React Form Library | [Formily](https://github.com/alibaba/formily) [react-hook-form](https://github.com/react-hook-form/react-hook-form) [formik](https://github.com/formium/formik) |
| Router | [react-router](https://github.com/ReactTraining/react-router) |
-| Layout |[react-grid-layout](https://github.com/react-grid-layout/react-grid-layout) [react-grid-system](https://github.com/sealninja/react-grid-system) [rc-dock](https://github.com/ticlo/rc-dock) |
+| Layout | [react-grid-layout](https://github.com/react-grid-layout/react-grid-layout) [react-grid-system](https://github.com/sealninja/react-grid-system) [rc-dock](https://github.com/ticlo/rc-dock) |
| Drag and drop | [react-beautiful-dnd](https://github.com/atlassian/react-beautiful-dnd/) [react-dnd](https://github.com/gaearon/react-dnd) [react-sortable-hoc](https://github.com/clauderic/react-sortable-hoc) |
| Code Editor | [react-codemirror2](https://github.com/scniro/react-codemirror2) [react-monaco-editor](https://github.com/superRaytin/react-monaco-editor) |
| Rich Text Editor | [react-quill](https://github.com/zenoamaro/react-quill) [braft-editor](https://github.com/margox/braft-editor) |
diff --git a/docs/react/recommendation.zh-CN.md b/docs/react/recommendation.zh-CN.md
index f0c74afb67..22d76f1796 100644
--- a/docs/react/recommendation.zh-CN.md
+++ b/docs/react/recommendation.zh-CN.md
@@ -9,8 +9,9 @@ title: 社区精选组件
| --- | --- |
| 可视化图表 | [AntV 数据可视化解决方案](https://antv.vision/zh) [🔥 AntV 图表库](https://g2plot.antv.vision/zh) [BizCharts](https://github.com/alibaba/BizCharts) [recharts](https://github.com/recharts/recharts/) [viser](https://viserjs.github.io/) |
| React Hooks 库 | [ahooks](https://github.com/alibaba/hooks) |
+| React 表单库 | [Formily](https://github.com/alibaba/formily) [react-hook-form](https://github.com/react-hook-form/react-hook-form) [formik](https://github.com/formium/formik) |
| 路由 | [react-router](https://github.com/ReactTraining/react-router) |
-| 布局 |[react-grid-layout](https://github.com/react-grid-layout/react-grid-layout) [react-grid-system](https://github.com/sealninja/react-grid-system) [rc-dock](https://github.com/ticlo/rc-dock) |
+| 布局 | [react-grid-layout](https://github.com/react-grid-layout/react-grid-layout) [react-grid-system](https://github.com/sealninja/react-grid-system) [rc-dock](https://github.com/ticlo/rc-dock) |
| 拖拽 | [react-beautiful-dnd](https://github.com/atlassian/react-beautiful-dnd/) [react-dnd](https://github.com/gaearon/react-dnd) [react-sortable-hoc](https://github.com/clauderic/react-sortable-hoc) |
| 代码编辑器 | [react-codemirror2](https://github.com/scniro/react-codemirror2) [react-monaco-editor](https://github.com/superRaytin/react-monaco-editor) |
| 富文本编辑器 | [react-quill](https://github.com/zenoamaro/react-quill) [braft-editor](https://github.com/margox/braft-editor) |
diff --git a/package.json b/package.json
index a4b9ee6680..e77b720871 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "antd",
- "version": "4.15.1",
+ "version": "4.15.2",
"description": "An enterprise-class UI design language and React components implementation",
"title": "Ant Design",
"keywords": [
@@ -46,6 +46,7 @@
"unpkg": "dist/antd.min.js",
"typings": "lib/index.d.ts",
"scripts": {
+ "prepare": "husky install",
"api-collection": "antd-tools run api-collection",
"authors": "node ./scripts/generate-authors",
"build": "npm run compile && NODE_OPTIONS='--max-old-space-size=4096' npm run dist",
@@ -97,11 +98,6 @@
"install-react-16": "npm i --no-save react@16 react-dom@16 react-test-renderer@16 enzyme-adapter-react-16",
"argos": "argos upload imageSnapshots"
},
- "husky": {
- "hooks": {
- "pre-commit": "pretty-quick --staged"
- }
- },
"browserslist": [
"> 0.5%",
"last 2 versions",
@@ -211,13 +207,13 @@
"eslint-plugin-markdown": "^2.0.0",
"eslint-plugin-react": "^7.20.6",
"eslint-plugin-react-hooks": "^4.1.2",
- "eslint-plugin-unicorn": "^29.0.0",
+ "eslint-plugin-unicorn": "^30.0.0",
"fetch-jsonp": "^1.1.3",
"fs-extra": "^9.0.0",
"full-icu": "^1.3.0",
"glob": "^7.1.4",
"http-server": "^0.12.0",
- "husky": "^4.0.3",
+ "husky": "^6.0.0",
"identity-obj-proxy": "^3.0.0",
"ignore-emit-webpack-plugin": "^2.0.6",
"immer": "^9.0.1",
@@ -226,7 +222,7 @@
"intersection-observer": "^0.12.0",
"jest": "^26.0.0",
"jest-image-snapshot": "^4.0.0",
- "jest-puppeteer": "^4.4.0",
+ "jest-puppeteer": "^5.0.1",
"jquery": "^3.4.1",
"jsdom": "^16.0.0",
"jsonml.js": "^0.1.0",
diff --git a/site/theme/template/Content/ComponentDoc.jsx b/site/theme/template/Content/ComponentDoc.jsx
index 4be80cf991..a5c0b5eaff 100644
--- a/site/theme/template/Content/ComponentDoc.jsx
+++ b/site/theme/template/Content/ComponentDoc.jsx
@@ -149,8 +149,8 @@ class ComponentDoc extends React.Component {
return (
- {helmetTitle && {helmetTitle}}
- {helmetTitle && }
+ {helmetTitle}
+
{contentChild && }
diff --git a/site/theme/template/Content/MainContent.jsx b/site/theme/template/Content/MainContent.jsx
index 9acbf1f3a9..5588c65aae 100644
--- a/site/theme/template/Content/MainContent.jsx
+++ b/site/theme/template/Content/MainContent.jsx
@@ -115,7 +115,7 @@ class MainContent extends Component {
if (this.scroller) {
this.scroller.destroy();
}
- window.removeEventListener('load', this.handleInitialHashOnLoad);
+ window.removeEventListener('load', this.handleLoad);
window.removeEventListener('hashchange', this.handleHashChange);
clearTimeout(this.timeout);
}
diff --git a/site/theme/template/Layout/Header/SearchBox.tsx b/site/theme/template/Layout/Header/SearchBox.tsx
index abe949ff24..05179577fd 100644
--- a/site/theme/template/Layout/Header/SearchBox.tsx
+++ b/site/theme/template/Layout/Header/SearchBox.tsx
@@ -22,11 +22,17 @@ export default ({ isZhCN, responsive, onTriggerFocus }: SearchBoxProps) => {
}
React.useEffect(() => {
- document.addEventListener('keyup', event => {
+ function keyupHandler(event: KeyboardEvent) {
if (event.keyCode === 83 && event.target === document.body) {
inputRef.current.focus();
}
- });
+ }
+
+ document.addEventListener('keyup', keyupHandler);
+
+ return () => {
+ document.removeEventListener('keyup', keyupHandler);
+ };
}, []);
return (