From 2c262dd3ec5c8458a632c3e6a11534ebbfd95ce4 Mon Sep 17 00:00:00 2001 From: jinchaofs <48663486+jinchaofs@users.noreply.github.com> Date: Fri, 12 Mar 2021 15:36:46 +0800 Subject: [PATCH 01/18] fix: Tabs centered property invalid (#29495) close https://github.com/ant-design/ant-design/issues/29482 Co-authored-by: jinchao.li --- components/style/themes/default.less | 5 +++-- components/tabs/style/card.less | 8 ++++---- components/tabs/style/index.less | 10 ++++------ components/tabs/style/position.less | 9 ++++----- components/tabs/style/rtl.less | 2 +- 5 files changed, 16 insertions(+), 18 deletions(-) diff --git a/components/style/themes/default.less b/components/style/themes/default.less index 8c5e94e47d..1fde0fae5b 100644 --- a/components/style/themes/default.less +++ b/components/style/themes/default.less @@ -734,13 +734,14 @@ @tabs-title-font-size-sm: @font-size-base; @tabs-ink-bar-color: @primary-color; @tabs-bar-margin: 0 0 @margin-md 0; -@tabs-horizontal-margin: 0 32px 0 0; +@tabs-horizontal-gutter: 32px; +@tabs-horizontal-margin: 0 0 0 @tabs-horizontal-gutter; @tabs-horizontal-margin-rtl: 0 0 0 32px; @tabs-horizontal-padding: @padding-sm 0; @tabs-horizontal-padding-lg: @padding-md 0; @tabs-horizontal-padding-sm: @padding-xs 0; @tabs-vertical-padding: @padding-xs @padding-lg; -@tabs-vertical-margin: 0 0 @margin-md 0; +@tabs-vertical-margin: @margin-md 0 0 0; @tabs-scrolling-size: 32px; @tabs-highlight-color: @primary-color; @tabs-hover-color: @primary-5; diff --git a/components/tabs/style/card.less b/components/tabs/style/card.less index 7cfc47e512..471da77069 100644 --- a/components/tabs/style/card.less +++ b/components/tabs/style/card.less @@ -28,8 +28,8 @@ &.@{tab-prefix-cls}-bottom { > .@{tab-prefix-cls}-nav, > div > .@{tab-prefix-cls}-nav { - .@{tab-prefix-cls}-tab:not(:last-of-type) { - margin-right: @tabs-card-gutter; + .@{tab-prefix-cls}-tab + .@{tab-prefix-cls}-tab { + margin-left: @tabs-card-gutter; } } } @@ -64,8 +64,8 @@ &.@{tab-prefix-cls}-right { > .@{tab-prefix-cls}-nav, > div > .@{tab-prefix-cls}-nav { - .@{tab-prefix-cls}-tab:not(:last-of-type) { - margin-bottom: @tabs-card-gutter; + .@{tab-prefix-cls}-tab + .@{tab-prefix-cls}-tab { + margin-top: @tabs-card-gutter; } } } diff --git a/components/tabs/style/index.less b/components/tabs/style/index.less index 500a4c4b84..7929742b7e 100644 --- a/components/tabs/style/index.less +++ b/components/tabs/style/index.less @@ -127,7 +127,6 @@ position: relative; display: inline-flex; align-items: center; - margin: @tabs-horizontal-margin; padding: @tabs-horizontal-padding; font-size: @tabs-title-font-size; background: transparent; @@ -135,11 +134,6 @@ outline: none; cursor: pointer; - &:last-of-type { - margin-right: 0; - margin-left: 0; - } - &-btn, &-remove { &:focus, @@ -201,6 +195,10 @@ } } + &-tab + &-tab { + margin: @tabs-horizontal-margin; + } + // =========================== TabPanes =========================== &-content { &-holder { diff --git a/components/tabs/style/position.less b/components/tabs/style/position.less index 62d6465a7b..475ff590fe 100644 --- a/components/tabs/style/position.less +++ b/components/tabs/style/position.less @@ -99,20 +99,19 @@ // >>>>>>>>>>> Tab .@{tab-prefix-cls}-tab { - margin: @tabs-vertical-margin; padding: @tabs-vertical-padding; text-align: center; - &:last-of-type { - margin-bottom: 0; - } - &-active .@{tab-prefix-cls}-tab-btn { font-weight: normal; text-shadow: 0 0 0.25px @tabs-active-color; } } + .@{tab-prefix-cls}-tab + .@{tab-prefix-cls}-tab { + margin: @tabs-vertical-margin; + } + // >>>>>>>>>>> Nav .@{tab-prefix-cls}-nav-wrap { flex-direction: column; diff --git a/components/tabs/style/rtl.less b/components/tabs/style/rtl.less index 214dceb735..b49e77b579 100644 --- a/components/tabs/style/rtl.less +++ b/components/tabs/style/rtl.less @@ -56,7 +56,7 @@ &.@{tab-prefix-cls}-bottom { > .@{tab-prefix-cls}-nav, > div > .@{tab-prefix-cls}-nav { - .@{tab-prefix-cls}-tab:not(:last-of-type) { + .@{tab-prefix-cls}-tab + .@{tab-prefix-cls}-tab { .@{tab-prefix-cls}-rtl& { margin-right: 0; margin-left: @tabs-card-gutter; From 462c7fcdf858feb155a9bf4197c0f8d0866d7222 Mon Sep 17 00:00:00 2001 From: afc163 Date: Fri, 12 Mar 2021 15:58:42 +0800 Subject: [PATCH 02/18] fix snapshot --- components/table/__tests__/__snapshots__/demo.test.js.snap | 6 +++--- components/table/demo/drag-sorting-handler.md | 5 +---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/components/table/__tests__/__snapshots__/demo.test.js.snap b/components/table/__tests__/__snapshots__/demo.test.js.snap index 9dc9fa3abd..6151ed1b3c 100644 --- a/components/table/__tests__/__snapshots__/demo.test.js.snap +++ b/components/table/__tests__/__snapshots__/demo.test.js.snap @@ -1629,7 +1629,7 @@ exports[`renders ./components/table/demo/drag-sorting-handler.md correctly 1`] = aria-label="menu" class="anticon anticon-menu" role="img" - style="cursor:pointer;color:#999" + style="cursor:grab;color:#999" > {args.icon}; - } else if (args.type) { - iconNode = React.createElement(typeToIcon[args.type] || null, { - className: `${prefixCls}-icon ${prefixCls}-icon-${args.type}`, + } else if (type) { + iconNode = React.createElement(typeToIcon[type] || null, { + className: `${prefixCls}-icon ${prefixCls}-icon-${type}`, }); } const autoMarginTag = - !args.description && iconNode ? ( + !description && iconNode ? ( ) : null; @@ -211,19 +225,21 @@ function getRCNoticeProps(args: ArgsProps, prefixCls: string) { {iconNode}
{autoMarginTag} - {args.message} + {message}
-
{args.description}
- {args.btn ? {args.btn} : null} +
{description}
+ {btn ? {btn} : null} ), duration, closable: true, - onClose: args.onClose, - onClick: args.onClick, - key: args.key, - style: args.style || {}, - className: args.className, + onClose, + onClick, + key, + style: style || {}, + className: classNames(className, { + [`${prefixCls}-${type}`]: !!type, + }), }; } diff --git a/components/tabs/index.en-US.md b/components/tabs/index.en-US.md index cf12178f30..256f25c9e6 100644 --- a/components/tabs/index.en-US.md +++ b/components/tabs/index.en-US.md @@ -28,6 +28,7 @@ Ant Design has 3 types of Tabs for different situations. | centered | Centers tabs | boolean | false | 4.4.0 | | defaultActiveKey | Initial active TabPane's key, if `activeKey` is not set | string | - | | | hideAdd | Hide plus icon or not. Only works while `type="editable-card"` | boolean | false | | +| moreIcon | The custom icon of ellipsis | ReactNode | <EllipsisOutlined /> | 4.14.0 | | renderTabBar | Replace the TabBar | (props: DefaultTabBarProps, DefaultTabBar: React.ComponentClass) => React.ReactElement | - | | | size | Preset tab bar size | `large` \| `default` \| `small` | `default` | | | tabBarExtraContent | Extra content in tab bar | ReactNode \| {left?: ReactNode, right?: ReactNode} | - | object: 4.6.0 | diff --git a/components/tabs/index.tsx b/components/tabs/index.tsx index c828625b17..a83c81c91b 100755 --- a/components/tabs/index.tsx +++ b/components/tabs/index.tsx @@ -25,7 +25,10 @@ export interface TabsProps extends Omit { } function Tabs({ type, className, size, onEdit, hideAdd, centered, addIcon, ...props }: TabsProps) { - const { prefixCls: customizePrefixCls } = props; + const { + prefixCls: customizePrefixCls, + moreIcon = , + } = props; const { getPrefixCls, direction } = React.useContext(ConfigContext); const prefixCls = getPrefixCls('tabs', customizePrefixCls); @@ -63,7 +66,7 @@ function Tabs({ type, className, size, onEdit, hideAdd, centered, addIcon, ...pr className, )} editable={editable} - moreIcon={} + moreIcon={moreIcon} prefixCls={prefixCls} /> ); diff --git a/components/tabs/index.zh-CN.md b/components/tabs/index.zh-CN.md index 14662b6e7d..075193cb25 100644 --- a/components/tabs/index.zh-CN.md +++ b/components/tabs/index.zh-CN.md @@ -31,6 +31,7 @@ Ant Design 依次提供了三级选项卡,分别用于不同的场景。 | centered | 标签居中展示 | boolean | false | 4.4.0 | | defaultActiveKey | 初始化选中面板的 key,如果没有设置 activeKey | string | `第一个面板` | | | hideAdd | 是否隐藏加号图标,在 `type="editable-card"` 时有效 | boolean | false | | +| moreIcon | 自定义折叠 icon | ReactNode | <EllipsisOutlined /> | 4.14.0 | | renderTabBar | 替换 TabBar,用于二次封装标签头 | (props: DefaultTabBarProps, DefaultTabBar: React.ComponentClass) => React.ReactElement | - | | | size | 大小,提供 `large` `default` 和 `small` 三种大小 | string | `default` | | | tabBarExtraContent | tab bar 上额外的元素 | ReactNode \| {left?: ReactNode, right?: ReactNode} | - | object: 4.6.0 | diff --git a/components/typography/Base.tsx b/components/typography/Base.tsx index 0a81979779..721d33489b 100644 --- a/components/typography/Base.tsx +++ b/components/typography/Base.tsx @@ -37,6 +37,8 @@ interface EditConfig { tooltip?: boolean | React.ReactNode; onStart?: () => void; onChange?: (value: string) => void; + onCancel?: () => void; + onEnd?: () => void; maxLength?: number; autoSize?: boolean | AutoSizeType; } @@ -204,6 +206,7 @@ class Base extends React.Component { }; onEditCancel = () => { + this.getEditable().onCancel?.(); this.triggerEdit(false); }; @@ -410,12 +413,13 @@ class Base extends React.Component { renderEditInput() { const { children, className, style } = this.props; const { direction } = this.context; - const { maxLength, autoSize } = this.getEditable(); + const { maxLength, autoSize, onEnd } = this.getEditable(); return ( void; onCancel: () => void; + onEnd?: () => void; className?: string; style?: React.CSSProperties; direction?: DirectionType; @@ -30,6 +31,7 @@ const Editable: React.FC = ({ value, onSave, onCancel, + onEnd, }) => { const ref = React.useRef(); @@ -92,6 +94,7 @@ const Editable: React.FC = ({ ) { if (keyCode === KeyCode.ENTER) { confirmChange(); + onEnd?.(); } else if (keyCode === KeyCode.ESC) { onCancel(); } diff --git a/components/typography/__tests__/index.test.js b/components/typography/__tests__/index.test.js index 96c99a9df6..2670165cef 100644 --- a/components/typography/__tests__/index.test.js +++ b/components/typography/__tests__/index.test.js @@ -469,6 +469,24 @@ describe('Typography', () => { testStep({ name: 'customize edit hide tooltip', tooltip: false }); testStep({ name: 'customize edit tooltip text', tooltip: 'click to edit text' }); + it('should trigger onEnd when type Enter', () => { + const onEnd = jest.fn(); + const wrapper = mount(Bamboo); + wrapper.find('.ant-typography-edit').first().simulate('click'); + wrapper.find('textarea').simulate('keyDown', { keyCode: KeyCode.ENTER }); + wrapper.find('textarea').simulate('keyUp', { keyCode: KeyCode.ENTER }); + expect(onEnd).toHaveBeenCalledTimes(1); + }); + + it('should trigger onCancel when type ESC', () => { + const onCancel = jest.fn(); + const wrapper = mount(Bamboo); + wrapper.find('.ant-typography-edit').first().simulate('click'); + wrapper.find('textarea').simulate('keyDown', { keyCode: KeyCode.ESC }); + wrapper.find('textarea').simulate('keyUp', { keyCode: KeyCode.ESC }); + expect(onCancel).toHaveBeenCalledTimes(1); + }); + it('should only trigger focus on the first time', () => { let triggerTimes = 0; const mockFocus = spyElementPrototype(HTMLElement, 'focus', () => { diff --git a/components/typography/index.en-US.md b/components/typography/index.en-US.md index 97e8a5e919..f0949dd2e6 100644 --- a/components/typography/index.en-US.md +++ b/components/typography/index.en-US.md @@ -90,6 +90,8 @@ Basic text writing, including headings, body text, lists, and more. autoSize: boolean | { minRows: number, maxRows: number }, onStart: function, onChange: function(string), + onCancel: function, + onEnd: function, } | Property | Description | Type | Default | Version | @@ -101,6 +103,8 @@ Basic text writing, including headings, body text, lists, and more. | tooltip | Custom tooltip text, hide when it is false | boolean \| ReactNode | `Edit` | 4.6.0 | | onChange | Called when input at textarea | function(event) | - | | | onStart | Called when enter editable state | function | - | | +| onCancel | Called when type ESC to exit editable state | function | - | | +| onEnd | Called when type ENTER to exit editable state | function | - | | ### ellipsis diff --git a/components/typography/index.zh-CN.md b/components/typography/index.zh-CN.md index f69d4e3372..da03cda485 100644 --- a/components/typography/index.zh-CN.md +++ b/components/typography/index.zh-CN.md @@ -90,6 +90,8 @@ cover: https://gw.alipayobjects.com/zos/alicdn/GOM1KQ24O/Typography.svg autoSize: boolean | { minRows: number, maxRows: number }, onStart: function, onChange: function(string), + onCancel: function, + onEnd: function, } | 参数 | 说明 | 类型 | 默认值 | 版本 | @@ -101,6 +103,8 @@ cover: https://gw.alipayobjects.com/zos/alicdn/GOM1KQ24O/Typography.svg | tooltip | 自定义提示文本,为 false 时关闭 | boolean \| ReactNode | `编辑` | 4.6.0 | | onChange | 文本域编辑时触发 | function(event) | - | | | onStart | 进入编辑中状态时触发 | function | - | | +| onCancel | 按 ESC 退出编辑状态时触发 | function | - | | +| onEnd | 按 ENTER 结束编辑状态时触发 | function | - | | ### ellipsis diff --git a/components/upload/Upload.tsx b/components/upload/Upload.tsx index 7f579163be..c2ffbad1be 100644 --- a/components/upload/Upload.tsx +++ b/components/upload/Upload.tsx @@ -14,7 +14,7 @@ import { UploadType, UploadListType, } from './interface'; -import { T, wrapFile, getFileItem, removeFileItem, replaceFileList } from './utils'; +import { file2Obj, getFileItem, removeFileItem, updateFileList } from './utils'; import LocaleReceiver from '../locale-provider/LocaleReceiver'; import defaultLocale from '../locale/default'; import { ConfigContext } from '../config-provider'; @@ -99,8 +99,8 @@ const InternalUpload: React.ForwardRefRenderFunction = (pr setMergedFileList(cloneList); - const changeInfo: UploadChangeParam = { - file, + const changeInfo: UploadChangeParam = { + file: file as UploadFile, fileList: cloneList, }; @@ -111,123 +111,6 @@ const InternalUpload: React.ForwardRefRenderFunction = (pr onChange?.(changeInfo); }; - const onBatchStart: RcUploadProps['onBatchStart'] = batchFileInfoList => { - // Skip file which marked as `LIST_IGNORE`, these file will not add to file list - const filteredFileInfoList = batchFileInfoList.filter(info => !(info.file as any)[LIST_IGNORE]); - - // Nothing to do since no file need upload - if (!filteredFileInfoList.length) { - return; - } - - const objectFileList = filteredFileInfoList.map(info => wrapFile(info.file)); - - // Concat new files with prev files - const newFileList = [...mergedFileList]; - objectFileList.forEach(fileObj => { - if (newFileList.every(existFile => existFile.uid !== fileObj.uid)) { - newFileList.push(fileObj); - } - }); - - onInternalChange(objectFileList[0], newFileList); - }; - - const onStart = (file: RcFile) => { - const targetItem = wrapFile(file); - targetItem.status = 'uploading'; - - const nextFileList = replaceFileList(targetItem, mergedFileList); - - onInternalChange(targetItem, nextFileList); - }; - - const onSuccess = (response: any, file: UploadFile, xhr: any) => { - try { - if (typeof response === 'string') { - response = JSON.parse(response); - } - } catch (e) { - /* do nothing */ - } - - // removed - if (!getFileItem(file, mergedFileList)) { - return; - } - - const targetItem = wrapFile(file); - targetItem.status = 'done'; - targetItem.percent = 100; - targetItem.response = response; - targetItem.xhr = xhr; - - const nextFileList = replaceFileList(targetItem, mergedFileList); - - onInternalChange(targetItem, nextFileList); - }; - - const onProgress = (e: { percent: number }, file: UploadFile) => { - // removed - if (!getFileItem(file, mergedFileList)) { - return; - } - - const targetItem = wrapFile(file); - targetItem.status = 'uploading'; - targetItem.percent = e.percent; - - const nextFileList = replaceFileList(targetItem, mergedFileList); - - onInternalChange(targetItem, nextFileList, e); - }; - - const onError = (error: Error, response: any, file: UploadFile) => { - // removed - if (!getFileItem(file, mergedFileList)) { - return; - } - - const targetItem = wrapFile(file); - targetItem.error = error; - targetItem.response = response; - targetItem.status = 'error'; - - const nextFileList = replaceFileList(targetItem, mergedFileList); - - onInternalChange(targetItem, nextFileList); - }; - - const handleRemove = (file: UploadFile) => { - let currentFile: UploadFile; - Promise.resolve(typeof onRemove === 'function' ? onRemove(file) : onRemove).then(ret => { - // Prevent removing file - if (ret === false) { - return; - } - - const removedFileList = removeFileItem(file, mergedFileList); - - if (removedFileList) { - currentFile = wrapFile(file); - currentFile.status = 'removed'; - mergedFileList?.forEach(item => { - const matchKey = currentFile.uid !== undefined ? 'uid' : 'name'; - if (item[matchKey] === currentFile[matchKey]) { - item.status = 'removed'; - } - }); - upload.current?.abort(currentFile); - - onInternalChange(currentFile, removedFileList); - } - }); - }; - - const onFileDrop = (e: React.DragEvent) => { - setDragState(e.type); - }; - const mergedBeforeUpload = async (file: RcFile, fileListArgs: RcFile[]) => { const { beforeUpload, transformFile } = props; @@ -261,9 +144,134 @@ const InternalUpload: React.ForwardRefRenderFunction = (pr return parsedFile as RcFile; }; + const onBatchStart: RcUploadProps['onBatchStart'] = batchFileInfoList => { + // Skip file which marked as `LIST_IGNORE`, these file will not add to file list + const filteredFileInfoList = batchFileInfoList.filter(info => !(info.file as any)[LIST_IGNORE]); + + // Nothing to do since no file need upload + if (!filteredFileInfoList.length) { + return; + } + + const objectFileList = filteredFileInfoList.map(info => file2Obj(info.file as RcFile)); + + // Concat new files with prev files + let newFileList = [...mergedFileList]; + + objectFileList.forEach(fileObj => { + // Replace file if exist + newFileList = updateFileList(fileObj, newFileList); + }); + + objectFileList.forEach((fileObj, index) => { + // Repeat trigger `onChange` event for compatible + let triggerFileObj: UploadFile = fileObj; + + if (!filteredFileInfoList[index].parsedFile) { + // `beforeUpload` return false + const { originFileObj } = fileObj; + const clone = (new File([originFileObj], originFileObj.name, { + type: originFileObj.type, + }) as any) as UploadFile; + clone.uid = fileObj.uid; + triggerFileObj = clone; + } else { + // Inject `uploading` status + fileObj.status = 'uploading'; + } + + onInternalChange(triggerFileObj, newFileList); + }); + }; + + const onSuccess = (response: any, file: RcFile, xhr: any) => { + try { + if (typeof response === 'string') { + response = JSON.parse(response); + } + } catch (e) { + /* do nothing */ + } + + // removed + if (!getFileItem(file, mergedFileList)) { + return; + } + + const targetItem = file2Obj(file); + targetItem.status = 'done'; + targetItem.percent = 100; + targetItem.response = response; + targetItem.xhr = xhr; + + const nextFileList = updateFileList(targetItem, mergedFileList); + + onInternalChange(targetItem, nextFileList); + }; + + const onProgress = (e: { percent: number }, file: RcFile) => { + // removed + if (!getFileItem(file, mergedFileList)) { + return; + } + + const targetItem = file2Obj(file); + targetItem.status = 'uploading'; + targetItem.percent = e.percent; + + const nextFileList = updateFileList(targetItem, mergedFileList); + + onInternalChange(targetItem, nextFileList, e); + }; + + const onError = (error: Error, response: any, file: RcFile) => { + // removed + if (!getFileItem(file, mergedFileList)) { + return; + } + + const targetItem = file2Obj(file); + targetItem.error = error; + targetItem.response = response; + targetItem.status = 'error'; + + const nextFileList = updateFileList(targetItem, mergedFileList); + + onInternalChange(targetItem, nextFileList); + }; + + const handleRemove = (file: UploadFile) => { + let currentFile: UploadFile; + Promise.resolve(typeof onRemove === 'function' ? onRemove(file) : onRemove).then(ret => { + // Prevent removing file + if (ret === false) { + return; + } + + const removedFileList = removeFileItem(file, mergedFileList); + + if (removedFileList) { + currentFile = { ...file, status: 'removed' }; + mergedFileList?.forEach(item => { + const matchKey = currentFile.uid !== undefined ? 'uid' : 'name'; + if (item[matchKey] === currentFile[matchKey]) { + item.status = 'removed'; + } + }); + upload.current?.abort(currentFile); + + onInternalChange(currentFile, removedFileList); + } + }); + }; + + const onFileDrop = (e: React.DragEvent) => { + setDragState(e.type); + }; + // Test needs React.useImperativeHandle(ref, () => ({ - onStart, + onBatchStart, onSuccess, onProgress, onError, @@ -277,7 +285,6 @@ const InternalUpload: React.ForwardRefRenderFunction = (pr const rcUploadProps = { onBatchStart, - onStart, onError, onProgress, onSuccess, @@ -412,7 +419,6 @@ Upload.defaultProps = { action: '', data: {}, accept: '', - beforeUpload: T, showUploadList: true, listType: 'text' as UploadListType, // or picture className: '', diff --git a/components/upload/UploadList/index.tsx b/components/upload/UploadList/index.tsx index 7a12edef8f..679a3dcd08 100644 --- a/components/upload/UploadList/index.tsx +++ b/components/upload/UploadList/index.tsx @@ -59,7 +59,7 @@ const InternalUploadList: React.ForwardRefRenderFunction { }); describe('util', () => { - // https://github.com/react-component/upload/issues/36 - it('should T() return true', () => { - const res = T(); - expect(res).toBe(true); - }); - - describe('wrapFile', () => { - it('should be able to copy file instance when Proxy not support', () => { - const file = new File([], 'aaa.zip'); - - const OriginProxy = global.Proxy; - global.Proxy = undefined; - - const copiedFile = wrapFile(file); - ['uid', 'lastModified', 'lastModifiedDate', 'name', 'size', 'type'].forEach(key => { - expect(key in copiedFile).toBe(true); - }); - - global.Proxy = OriginProxy; - }); - - it('Proxy support', () => { - const file = new File([], 'aaa.zip'); - const copiedFile = wrapFile(file); - ['uid', 'lastModified', 'lastModifiedDate', 'name', 'size', 'type'].forEach(key => { - expect(key in copiedFile).toBe(true); - }); - }); - }); - it('should be able to get fileItem', () => { const file = { uid: '-1', name: 'item.jpg' }; const fileList = [ @@ -344,7 +314,7 @@ describe('Upload', () => { expect(targetItem).toEqual(fileList.slice(1)); }); - it('remove fileItem and fileList with immuable data', () => { + it('remove fileItem and fileList with immutable data', () => { const file = { uid: '-3', name: 'item3.jpg' }; const fileList = produce( [ @@ -457,17 +427,16 @@ describe('Upload', () => { let wrapper; const props = { - onRemove: () => - new Promise( - resolve => - setTimeout(() => { - wrapper.update(); - expect(props.fileList).toHaveLength(1); - expect(props.fileList[0].status).toBe('uploading'); - resolve(true); - }), - 100, - ), + onRemove: async () => { + await act(async () => { + await sleep(100); + wrapper.update(); + expect(props.fileList).toHaveLength(1); + expect(props.fileList[0].status).toBe('uploading'); + }); + + return true; + }, fileList: [ { uid: '-1', @@ -555,20 +524,35 @@ describe('Upload', () => { it('should replace file when targetItem already exists', () => { const fileList = [{ uid: 'file', name: 'file' }]; const ref = React.createRef(); - mount( + const wrapper = mount( , ); - ref.current.onStart({ + + const newFile = { uid: 'file', name: 'file1', + }; + + act(() => { + ref.current.onBatchStart([ + { + file: newFile, + parsedFile: newFile, + }, + ]); }); + + wrapper.update(); + expect(ref.current.fileList.length).toBe(1); expect(ref.current.fileList[0].originFileObj).toEqual({ name: 'file1', uid: 'file', }); + + wrapper.unmount(); }); it('warning if set `value`', () => { @@ -634,20 +618,15 @@ describe('Upload', () => { switch (callTimes) { case 1: - expect(e.file.originFileObj).toBeTruthy(); - expect(file.status).toBe(undefined); - break; - case 2: - case 3: expect(file).toEqual(expect.objectContaining({ status: 'uploading', percent: 0 })); break; - case 4: + case 3: expect(file).toEqual(expect.objectContaining({ status: 'uploading', percent: 100 })); break; - case 5: + case 4: expect(file).toEqual(expect.objectContaining({ status: 'done', percent: 100 })); break; diff --git a/components/upload/__tests__/uploadlist.test.js b/components/upload/__tests__/uploadlist.test.js index dda9812c86..56f40c0415 100644 --- a/components/upload/__tests__/uploadlist.test.js +++ b/components/upload/__tests__/uploadlist.test.js @@ -1127,22 +1127,28 @@ describe('Upload List', () => { const wrapper = mount(); // Mock async update in a frame - const files = ['light', 'bamboo', 'little']; + const fileNames = ['light', 'bamboo', 'little']; - /* eslint-disable no-await-in-loop */ - for (let i = 0; i < files.length; i += 1) { - await Promise.resolve(); - uploadRef.current.onStart({ - uid: files[i], - name: files[i], - }); - } - /* eslint-enable */ + act(() => { + uploadRef.current.onBatchStart( + fileNames.map(fileName => { + const file = new File([], fileName); + file.uid = fileName; - expect(uploadRef.current.fileList).toHaveLength(files.length); + return { + file, + parsedFile: file, + }; + }), + ); + }); - jest.runAllTimers(); - expect(uploadRef.current.fileList).toHaveLength(files.length); + expect(uploadRef.current.fileList).toHaveLength(fileNames.length); + + act(() => { + jest.runAllTimers(); + }); + expect(uploadRef.current.fileList).toHaveLength(fileNames.length); wrapper.unmount(); diff --git a/components/upload/index.en-US.md b/components/upload/index.en-US.md index a4b0184f6d..f13486110f 100644 --- a/components/upload/index.en-US.md +++ b/components/upload/index.en-US.md @@ -108,3 +108,7 @@ See . ### Why `fileList` in control will not trigger `onChange` `status` update when file not in the list? `onChange` only trigger when file in the list, it will ignore left events when removed from the list. Please note that there exist bug which makes event still trigger even the file is not in the list before `4.13.0`. + +### Why sometime `onChange` return File object and sometime return { originFileObj: File }? + +For compatible case, we return File object when `beforeUpload` return `false`. It will merge to `{ originFileObj: File }` in next major version. Current version is compatible to get origin file by `info.file.originFileObj`. You can change this before major release. diff --git a/components/upload/index.zh-CN.md b/components/upload/index.zh-CN.md index 02877aa157..437eab3730 100644 --- a/components/upload/index.zh-CN.md +++ b/components/upload/index.zh-CN.md @@ -108,3 +108,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/QaeBt_ZMg/Upload.svg ### 为何 `fileList` 受控时,上传不在列表中的文件不会触发 `onChange` 后续的 `status` 更新事件? `onChange` 事件仅会作用于在列表中的文件,因而 `fileList` 不存在对应文件时后续事件会被忽略。请注意,在 `4.13.0` 版本之前受控状态存在 bug 导致不在列表中的文件也会触发。 + +### `onChange` 为什么有时候返回 File 有时候返回 { originFileObj: File }? + +历史原因,在 `beforeUpload` 返回 `false` 时,会返回 File 对象。在下个大版本我们会统一返回 `{ originFileObj: File }` 对象。当前版本已经兼容所有场景下 `info.file.originFileObj` 获取原 File 写法。你可以提前切换。 diff --git a/components/upload/interface.tsx b/components/upload/interface.tsx index 8843b27b05..98d330d1b0 100755 --- a/components/upload/interface.tsx +++ b/components/upload/interface.tsx @@ -1,19 +1,20 @@ import * as React from 'react'; -import { UploadRequestOption as RcCustomRequestOptions } from 'rc-upload/lib/interface'; +import { + RcFile as OriRcFile, + UploadRequestOption as RcCustomRequestOptions, +} from 'rc-upload/lib/interface'; import { ProgressProps } from '../progress'; +export interface RcFile extends OriRcFile { + readonly lastModifiedDate: Date; +} + export type UploadFileStatus = 'error' | 'success' | 'done' | 'uploading' | 'removed'; export interface HttpRequestHeader { [key: string]: string; } -export interface RcFile extends File { - uid: string; - readonly lastModifiedDate: Date; - readonly webkitRelativePath: string; -} - export interface UploadFile { uid: string; size: number; @@ -25,7 +26,7 @@ export interface UploadFile { status?: UploadFileStatus; percent?: number; thumbUrl?: string; - originFileObj?: File | Blob; + originFileObj: RcFile; response?: T; error?: any; linkProps?: any; diff --git a/components/upload/utils.tsx b/components/upload/utils.tsx index 05023f15eb..178d558db6 100644 --- a/components/upload/utils.tsx +++ b/components/upload/utils.tsx @@ -1,18 +1,8 @@ import { RcFile, UploadFile } from './interface'; -export function T() { - return true; -} - -type WrapFile = RcFile | UploadFile; - -/** - * Wrap file with Proxy to provides more info. Will fallback to object if Proxy not support. - * - * Origin comment: Fix IE file.status problem via coping a new Object - */ -export function wrapFile(file: WrapFile): UploadFile { - const filledProps = { +export function file2Obj(file: RcFile): UploadFile { + return { + ...file, lastModified: file.lastModified, lastModifiedDate: file.lastModifiedDate, name: file.name, @@ -22,76 +12,10 @@ export function wrapFile(file: WrapFile): UploadFile { percent: 0, originFileObj: file, }; - - if (typeof Proxy !== 'undefined') { - const data = new Map(Object.entries(filledProps)); - - const getValue = (key: string | symbol) => { - if (data.has(key)) { - return data.get(key); - } - return (file as any)[key]; - }; - - return new Proxy(file, { - get(_, key) { - return getValue(key); - }, - set(_, key, value) { - data.set(key, value); - return true; - }, - has(target, prop) { - return data.has(prop) || prop in target; - }, - ownKeys(target) { - const keys = [...Object.keys(target), ...data.keys()]; - return [...new Set(keys)]; - }, - - /** - * Lodash cloneDeep will use `Object.create(Object.getPrototypeOf(file))` which do not map to - * the correct context. We need do a sub class to make skip fetch the context and still - * instance of File. ref: https://github.com/ant-design/ant-design/issues/29646 - */ - getPrototypeOf() { - class ProxyFile extends File {} - - const fileProtoKeys = Object.keys(File.prototype); - - [...fileProtoKeys, 'size', 'type'].forEach(key => { - Object.defineProperty(ProxyFile.prototype, key, { - // Get will never reach but we provide fallback here - /* istanbul ignore next */ - get: () => getValue(key), - }); - }); - - return ProxyFile.prototype; - }, - getOwnPropertyDescriptor(target, prop) { - if (data.has(prop)) { - return { - value: data.get(prop), - writable: true, - enumerable: true, - configurable: true, - }; - } - - const descriptor = Object.getOwnPropertyDescriptor(target, prop); - return descriptor; - }, - }); - } - - return { - ...file, - ...filledProps, - } as UploadFile; } -export function replaceFileList(file: UploadFile, fileList: UploadFile[]) { +/** Upload fileList. Replace file if exist or just push into it. */ +export function updateFileList(file: UploadFile, fileList: UploadFile[]) { const nextFileList = [...fileList]; const fileIndex = nextFileList.findIndex(({ uid }: UploadFile) => uid === file.uid); if (fileIndex === -1) { @@ -102,7 +26,7 @@ export function replaceFileList(file: UploadFile, fileList: UploadFile return nextFileList; } -export function getFileItem(file: UploadFile, fileList: UploadFile[]) { +export function getFileItem(file: RcFile, fileList: UploadFile[]) { const matchKey = file.uid !== undefined ? 'uid' : 'name'; return fileList.filter(item => item[matchKey] === file[matchKey])[0]; } diff --git a/package.json b/package.json index 6b4ebe0d1a..695dfb4c2d 100644 --- a/package.json +++ b/package.json @@ -147,7 +147,7 @@ "rc-tree": "~4.1.0", "rc-tree-select": "~4.3.0", "rc-trigger": "^5.2.1", - "rc-upload": "~4.0.1", + "rc-upload": "~4.2.0-alpha.0", "rc-util": "^5.8.1", "scroll-into-view-if-needed": "^2.2.25", "warning": "^4.0.3" From 55aa0f457bb1bfb9796e4d4c032d6afa0a9dd391 Mon Sep 17 00:00:00 2001 From: Eric Lee Date: Sun, 14 Mar 2021 00:06:29 +0800 Subject: [PATCH 06/18] code review --- components/upload/index.en-US.md | 2 +- components/upload/index.zh-CN.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/upload/index.en-US.md b/components/upload/index.en-US.md index 296f5d6902..1386d738f2 100644 --- a/components/upload/index.en-US.md +++ b/components/upload/index.en-US.md @@ -33,9 +33,9 @@ Uploading is the process of publishing information (web pages, text, pictures, v | isImageUrl | Customize if render <img /> in thumbnail | (file: UploadFile) => boolean | [(inside implementation)](https://github.com/ant-design/ant-design/blob/4ad5830eecfb87471cd8ac588c5d992862b70770/components/upload/utils.tsx#L47-L68) | | | itemRender | Custom item of uploadList | (originNode: ReactElement, file: UploadFile, fileList?: object\[]) => React.ReactNode | - | 4.7.0 | | listType | Built-in stylesheets, support for three types: `text`, `picture` or `picture-card` | string | `text` | | +| maxCount | Limit the number of uploaded files. Will replace current one when `maxCount` is `1` | number | - | | | method | The http method of upload request | string | `post` | | | multiple | Whether to support selected multiple file. `IE10+` supported. You can select multiple files with CTRL holding down while multiple is set to be true | boolean | false | | -| maxCount | Limit the number of uploaded files. Will replace current one when `maxCount` is `1` | number | - | | | name | The name of uploading file | string | `file` | | | openFileDialogOnClick | Click open file dialog | boolean | true | | | previewFile | Customize preview file logic | (file: File \| Blob) => Promise<dataURL: string> | - | | diff --git a/components/upload/index.zh-CN.md b/components/upload/index.zh-CN.md index c5dba172e2..ca433c1e60 100644 --- a/components/upload/index.zh-CN.md +++ b/components/upload/index.zh-CN.md @@ -34,9 +34,9 @@ cover: https://gw.alipayobjects.com/zos/alicdn/QaeBt_ZMg/Upload.svg | isImageUrl | 自定义缩略图是否使用 <img /> 标签进行显示 | (file: UploadFile) => boolean | [(内部实现)](https://github.com/ant-design/ant-design/blob/4ad5830eecfb87471cd8ac588c5d992862b70770/components/upload/utils.tsx#L47-L68) | | | itemRender | 自定义上传列表项 | (originNode: ReactElement, file: UploadFile, fileList?: object\[]) => React.ReactNode | - | 4.7.0 | | listType | 上传列表的内建样式,支持三种基本样式 `text`, `picture` 和 `picture-card` | string | `text` | | +| maxCount | 限制上传数量。当为 1 时,始终用最新上传的文件代替当前文件 | number | - | | | method | 上传请求的 http method | string | `post` | | | multiple | 是否支持多选文件,`ie10+` 支持。开启后按住 ctrl 可选择多个文件 | boolean | false | | -| maxCount | 限制上传数量。当为 1 时,始终用最新上传的文件代替当前 | number | - | | | name | 发到后台的文件参数名 | string | `file` | | | openFileDialogOnClick | 点击打开文件对话框 | boolean | true | | | previewFile | 自定义文件预览逻辑 | (file: File \| Blob) => Promise<dataURL: string> | - | | From d05112427af1fed5a8ebb7ff969bcf8fc04e09f0 Mon Sep 17 00:00:00 2001 From: xrkffgg Date: Sun, 14 Mar 2021 00:10:54 +0800 Subject: [PATCH 07/18] Update components/upload/index.en-US.md --- components/upload/index.en-US.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/upload/index.en-US.md b/components/upload/index.en-US.md index 1386d738f2..dfd6d0d977 100644 --- a/components/upload/index.en-US.md +++ b/components/upload/index.en-US.md @@ -33,7 +33,7 @@ Uploading is the process of publishing information (web pages, text, pictures, v | isImageUrl | Customize if render <img /> in thumbnail | (file: UploadFile) => boolean | [(inside implementation)](https://github.com/ant-design/ant-design/blob/4ad5830eecfb87471cd8ac588c5d992862b70770/components/upload/utils.tsx#L47-L68) | | | itemRender | Custom item of uploadList | (originNode: ReactElement, file: UploadFile, fileList?: object\[]) => React.ReactNode | - | 4.7.0 | | listType | Built-in stylesheets, support for three types: `text`, `picture` or `picture-card` | string | `text` | | -| maxCount | Limit the number of uploaded files. Will replace current one when `maxCount` is `1` | number | - | | +| maxCount | Limit the number of uploaded files. Will replace current one when `maxCount` is `1` | number | - | 4.10.0 | | method | The http method of upload request | string | `post` | | | multiple | Whether to support selected multiple file. `IE10+` supported. You can select multiple files with CTRL holding down while multiple is set to be true | boolean | false | | | name | The name of uploading file | string | `file` | | From 135f39ddd41de3a330cd95eba732e15037490361 Mon Sep 17 00:00:00 2001 From: xrkffgg Date: Sun, 14 Mar 2021 00:11:00 +0800 Subject: [PATCH 08/18] Update components/upload/index.zh-CN.md --- components/upload/index.zh-CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/upload/index.zh-CN.md b/components/upload/index.zh-CN.md index ca433c1e60..85d057ba21 100644 --- a/components/upload/index.zh-CN.md +++ b/components/upload/index.zh-CN.md @@ -34,7 +34,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/QaeBt_ZMg/Upload.svg | isImageUrl | 自定义缩略图是否使用 <img /> 标签进行显示 | (file: UploadFile) => boolean | [(内部实现)](https://github.com/ant-design/ant-design/blob/4ad5830eecfb87471cd8ac588c5d992862b70770/components/upload/utils.tsx#L47-L68) | | | itemRender | 自定义上传列表项 | (originNode: ReactElement, file: UploadFile, fileList?: object\[]) => React.ReactNode | - | 4.7.0 | | listType | 上传列表的内建样式,支持三种基本样式 `text`, `picture` 和 `picture-card` | string | `text` | | -| maxCount | 限制上传数量。当为 1 时,始终用最新上传的文件代替当前文件 | number | - | | +| maxCount | 限制上传数量。当为 1 时,始终用最新上传的文件代替当前文件 | number | - | 4.10.0 | | method | 上传请求的 http method | string | `post` | | | multiple | 是否支持多选文件,`ie10+` 支持。开启后按住 ctrl 可选择多个文件 | boolean | false | | | name | 发到后台的文件参数名 | string | `file` | | From 8622c39f29de1a3d589d0c7904ca5b7f90bd31c5 Mon Sep 17 00:00:00 2001 From: xrkffgg Date: Sun, 14 Mar 2021 16:35:09 +0800 Subject: [PATCH 09/18] docs: update typography api show --- components/typography/index.en-US.md | 6 +++--- components/typography/index.zh-CN.md | 24 ++++++++++++------------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/components/typography/index.en-US.md b/components/typography/index.en-US.md index f0949dd2e6..6ee58afe32 100644 --- a/components/typography/index.en-US.md +++ b/components/typography/index.en-US.md @@ -101,10 +101,10 @@ Basic text writing, including headings, body text, lists, and more. | icon | Custom editable icon | ReactNode | <EditOutlined /> | 4.6.0 | | maxLength | `maxLength` attribute of textarea | number | - | 4.4.0 | | tooltip | Custom tooltip text, hide when it is false | boolean \| ReactNode | `Edit` | 4.6.0 | -| onChange | Called when input at textarea | function(event) | - | | -| onStart | Called when enter editable state | function | - | | | onCancel | Called when type ESC to exit editable state | function | - | | -| onEnd | Called when type ENTER to exit editable state | function | - | | +| onChange | Called when input at textarea | function(event) | - | | +| onEnd | Called when type ENTER to exit editable state | function | - | 4.14.0 | +| onStart | Called when enter editable state | function | - | | ### ellipsis diff --git a/components/typography/index.zh-CN.md b/components/typography/index.zh-CN.md index da03cda485..76db0eaf80 100644 --- a/components/typography/index.zh-CN.md +++ b/components/typography/index.zh-CN.md @@ -101,10 +101,10 @@ cover: https://gw.alipayobjects.com/zos/alicdn/GOM1KQ24O/Typography.svg | icon | 自定义编辑图标 | ReactNode | <EditOutlined /> | 4.6.0 | | maxLength | 编辑中文本域最大长度 | number | - | 4.4.0 | | tooltip | 自定义提示文本,为 false 时关闭 | boolean \| ReactNode | `编辑` | 4.6.0 | -| onChange | 文本域编辑时触发 | function(event) | - | | -| onStart | 进入编辑中状态时触发 | function | - | | | onCancel | 按 ESC 退出编辑状态时触发 | function | - | | -| onEnd | 按 ENTER 结束编辑状态时触发 | function | - | | +| onChange | 文本域编辑时触发 | function(event) | - | | +| onEnd | 按 ENTER 结束编辑状态时触发 | function | - | 4.14.0 | +| onStart | 进入编辑中状态时触发 | function | - | | ### ellipsis @@ -118,15 +118,15 @@ cover: https://gw.alipayobjects.com/zos/alicdn/GOM1KQ24O/Typography.svg onEllipsis: function(ellipsis), } -| 参数 | 说明 | 类型 | 默认值 | 版本 | -| --- | --- | --- | --- | --- | -| expandable | 是否可展开 | boolean | - | | -| rows | 最多显示的行数 | number | - | | -| suffix | 自定义省略内容后缀 | string | - | | -| symbol | 自定义展开描述文案 | ReactNode | `展开` | | -| tooltip | 省略时,展示提示信息 | boolean \| ReactNode | - | 4.11.0 | -| onEllipsis | 触发省略时的回调 | function(ellipsis) | - | 4.2.0 | -| onExpand | 点击展开时的回调 | function(event) | - | | +| 参数 | 说明 | 类型 | 默认值 | 版本 | +| ---------- | -------------------- | -------------------- | ------ | ------ | +| expandable | 是否可展开 | boolean | - | | +| rows | 最多显示的行数 | number | - | | +| suffix | 自定义省略内容后缀 | string | - | | +| symbol | 自定义展开描述文案 | ReactNode | `展开` | | +| tooltip | 省略时,展示提示信息 | boolean \| ReactNode | - | 4.11.0 | +| onEllipsis | 触发省略时的回调 | function(ellipsis) | - | 4.2.0 | +| onExpand | 点击展开时的回调 | function(event) | - | | ## FAQ From 93140265f3d1316918f643b3c1b2886e066b2f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=B8=85?= Date: Sun, 14 Mar 2021 20:09:56 +0800 Subject: [PATCH 10/18] docs: add 4.14.0 changelog (#29760) * docs: add 4.14.0 changelog * fix typo * fix typo * Update CHANGELOG.en-US.md Co-authored-by: xrkffgg * Update CHANGELOG.en-US.md Co-authored-by: xrkffgg * Update CHANGELOG.en-US.md Co-authored-by: xrkffgg * fix typo * add merge * fix lint * Update CHANGELOG.en-US.md Co-authored-by: afc163 * fix typo * add 29615 * add 29615 * fix typo * fix typo Co-authored-by: xrkffgg Co-authored-by: afc163 --- CHANGELOG.en-US.md | 23 +++++++++++++++++++++++ CHANGELOG.zh-CN.md | 23 +++++++++++++++++++++++ package.json | 2 +- 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index faccbd91eb..2f791534a8 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -15,6 +15,29 @@ timeline: true --- +## 4.14.0 + +`2021-03-14` + +- Upload + - 🆕 Upload `onChange` to change back to the original behavior (before Upload false returns the original file, and the rest of the scene returns the encapsulated object). Now you can always get the original file via `onChange.info.originFileObj`. For future upgrades, please use this method to access the original text first. [#29737](https://github.com/ant-design/ant-design/pull/29737) + - 🐞 Fix Upload `onChange` params `file` can not `cloneDeep` by lodash. [#29718](https://github.com/ant-design/ant-design/pull/29718) + - 🐞 Fix Upload crash when `fileList` is `null`. [#29702](https://github.com/ant-design/ant-design/pull/29702) + - 💄 Upload motion add 2s deadline to avoid developer manually remove motion makes hanging. [#29686](https://github.com/ant-design/ant-design/pull/29686) +- 🐞 Fix the bug that Modal footer buttons not spaced properly when using href in button. [#29681](https://github.com/ant-design/ant-design/pull/29681) [@n0ruSh](https://github.com/n0ruSh) +- 🆕 Add parent class for different Notification types. [#29634](https://github.com/ant-design/ant-design/pull/29634) [@n0ruSh](https://github.com/n0ruSh) +- 🆕 Typography editable supports `onCancel` and `onEnd`. [#29615](https://github.com/ant-design/ant-design/pull/29615) [@jueinin](https://github.com/jueinin) +- Tabs + - 🐞 Fix Tabs `centered` prop is not actually center. [#29495](https://github.com/ant-design/ant-design/pull/29495) [@jinchaofs](https://github.com/jinchaofs) + - 🆕 Tabs support `moreIcon`. [#29744](https://github.com/ant-design/ant-design/pull/29744) [@tianyuan233](https://github.com/tianyuan233) +- 🐞 Button with `htmlType='reset'` will reset all form fileds. [#29752](https://github.com/ant-design/ant-design/pull/29752) [@jueinin](https://github.com/jueinin) +- 🐞 Fix AutoComplete custom input `className` missing. [#29725](https://github.com/ant-design/ant-design/pull/29725) +- 💄 Fix console warning when setting `margin` style on Row. [#29688](https://github.com/ant-design/ant-design/pull/29688) +- 💄 Fix the error style where the `disabled` Input has affix elements. [#29670](https://github.com/ant-design/ant-design/pull/29670) +- 💄 Optimize the cursor style of Form.Item tooltip info. [#29650](https://github.com/ant-design/ant-design/pull/29650) +- 🇨🇿 Fix typo in cs_CZ locale. [#29675](https://github.com/ant-design/ant-design/pull/29675) [@jvaclavik](https://github.com/jvaclavik) +- 🇨🇦 Add fr_CA locale. [#29748](https://github.com/ant-design/ant-design/pull/29748) [@liufenghua808](https://github.com/liufenghua808) + ## 4.13.1 `2021-03-06` diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 892535e0bd..8f465bcf6e 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -15,6 +15,29 @@ timeline: true --- +## 4.14.0 + +`2021-03-14` + +- Upload + - 🆕 Upload `onChange` 改回原本行为(before Upload false 返回原始文件,其余场景返回封装对象)。现在你始终可以通过 `onChange.info.originFileObj` 获得原始文件。为了未来升级,请优先使用该方式访问原始文。[#29737](https://github.com/ant-design/ant-design/pull/29737) + - 🐞 修复 Upload `onChange` 参数 `file` 不能被 lodash `cloneDeep` 的问题。[#29718](https://github.com/ant-design/ant-design/pull/29718) + - 🐞 修复 Upload 当 `fileList` 为 `null` 时崩溃的问题。[#29702](https://github.com/ant-design/ant-design/pull/29702) + - 💄 Upload 添加 2 秒时限以防止开发者手工移除动画样式时导致动画卡住的问题。[#29686](https://github.com/ant-design/ant-design/pull/29686) +- 🆕 为不同的 Notification 类型添加相应默认类名。[#29634](https://github.com/ant-design/ant-design/pull/29634) [@n0ruSh](https://github.com/n0ruSh) +- 🆕 Typography editable 新增 `onCancel` 和 `onEnd` 回调。[#29615](https://github.com/ant-design/ant-design/pull/29615) [@jueinin](https://github.com/jueinin) +- Tabs + - 🆕 Tabs 新增 `moreIcon` 参数。[#29744](https://github.com/ant-design/ant-design/pull/29744) [@tianyuan233](https://github.com/tianyuan233) + - 🐞 修复 Tabs 设置 `centered` 后居中位置偏移。[#29495](https://github.com/ant-design/ant-design/pull/29495) [@jinchaofs](https://github.com/jinchaofs) +- 🐞 Form 表单现在可以自动响应 reset 事件。[#29752](https://github.com/ant-design/ant-design/pull/29752) [@jueinin](https://github.com/jueinin) +- 🐞 修复 AutoComplete 自定义 input 上 `className` 属性丢失的问题。[#29725](https://github.com/ant-design/ant-design/pull/29725) +- 💄 修复 Row 设置 `margin` 样式时控制台警告的问题。[#29688](https://github.com/ant-design/ant-design/pull/29688) +- 🐞 修复 Modal 页脚里使用 href 按钮导致的间距丢失问题。[#29681](https://github.com/ant-design/ant-design/pull/29681) [@n0ruSh](https://github.com/n0ruSh) +- 💄 修复 Input 组件配置附件元素时禁用样式异常的问题。[#29670](https://github.com/ant-design/ant-design/pull/29670) +- 💄 优化 Form.Item 提示信息的鼠标显示样式。[#29650](https://github.com/ant-design/ant-design/pull/29650) +- 🇨🇿 修复 cs_CZ 语言环境中的错字。 [#29675](https://github.com/ant-design/ant-design/pull/29675) [@jvaclavik](https://github.com/jvaclavik) +- 🇨🇦 添加 fr_CA 语言。[#29748](https://github.com/ant-design/ant-design/pull/29748) [@liufenghua808](https://github.com/liufenghua808) + ## 4.13.1 `2021-03-06` diff --git a/package.json b/package.json index 7e2f37a27e..96eaac42ef 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "antd", - "version": "4.13.1", + "version": "4.14.0", "description": "An enterprise-class UI design language and React components implementation", "title": "Ant Design", "keywords": [ From 6368b33663e91f8c130c817f72da934ea5a0d848 Mon Sep 17 00:00:00 2001 From: afc163 Date: Mon, 15 Mar 2021 13:23:38 +0800 Subject: [PATCH 11/18] docs(:book:) update changelog --- CHANGELOG.zh-CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 8f465bcf6e..be6ac366bb 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -20,7 +20,7 @@ timeline: true `2021-03-14` - Upload - - 🆕 Upload `onChange` 改回原本行为(before Upload false 返回原始文件,其余场景返回封装对象)。现在你始终可以通过 `onChange.info.originFileObj` 获得原始文件。为了未来升级,请优先使用该方式访问原始文。[#29737](https://github.com/ant-design/ant-design/pull/29737) + - 🆕 Upload `onChange` 改回原本行为(`beforeUpload` 返回时 false 参数返回原始文件,其余场景返回封装对象)。现在你始终可以通过 `onChange.info.originFileObj` 获得原始文件。为了未来升级,请优先使用该方式访问原始文件。[#29737](https://github.com/ant-design/ant-design/pull/29737) - 🐞 修复 Upload `onChange` 参数 `file` 不能被 lodash `cloneDeep` 的问题。[#29718](https://github.com/ant-design/ant-design/pull/29718) - 🐞 修复 Upload 当 `fileList` 为 `null` 时崩溃的问题。[#29702](https://github.com/ant-design/ant-design/pull/29702) - 💄 Upload 添加 2 秒时限以防止开发者手工移除动画样式时导致动画卡住的问题。[#29686](https://github.com/ant-design/ant-design/pull/29686) From 7fdfa4e64d256b888a93bfc905f7b0df1d7e7026 Mon Sep 17 00:00:00 2001 From: Kermit Date: Mon, 15 Mar 2021 18:20:36 +0800 Subject: [PATCH 12/18] fix(upload): beforeUpload type declaration (#29766) * fix(upload): beforeUpload type declaration * chore: update doc --- components/upload/__tests__/type.test.tsx | 19 +++++++++++++++++++ components/upload/index.en-US.md | 2 +- components/upload/index.zh-CN.md | 2 +- components/upload/interface.tsx | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/components/upload/__tests__/type.test.tsx b/components/upload/__tests__/type.test.tsx index 33a36af89d..8c56b731f4 100644 --- a/components/upload/__tests__/type.test.tsx +++ b/components/upload/__tests__/type.test.tsx @@ -27,4 +27,23 @@ describe('Upload.typescript', () => { ); expect(upload).toBeTruthy(); }); + + it('beforeUpload', () => { + const upload = ( + { + if (file.type === 'image/png') { + return true; + } + if (file.type === 'image/webp') { + return Promise.resolve(file); + } + return Upload.LIST_IGNORE; + }} + > + click to upload + + ); + expect(upload).toBeTruthy(); + }); }); diff --git a/components/upload/index.en-US.md b/components/upload/index.en-US.md index 47336b8716..c715845954 100644 --- a/components/upload/index.en-US.md +++ b/components/upload/index.en-US.md @@ -21,7 +21,7 @@ Uploading is the process of publishing information (web pages, text, pictures, v | --- | --- | --- | --- | --- | | accept | File types that can be accepted. See [input accept Attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept) | string | - | | | action | Uploading URL | string \| (file) => Promise<string> | - | | -| beforeUpload | Hook function which will be executed before uploading. Uploading will be stopped with `false` or a rejected Promise returned. **Warning:this function is not supported in IE9** | (file, fileList) => boolean \| Promise<File> | - | | +| beforeUpload | Hook function which will be executed before uploading. Uploading will be stopped with `false` or a rejected Promise returned. When returned value is `Upload.LIST_IGNORE`, the list of files that have been uploaded will ignore it. **Warning:this function is not supported in IE9** | (file, fileList) => boolean \| Promise<File> \| `Upload.LIST_IGNORE` | - | | | customRequest | Override for the default xhr behavior allowing for additional customization and ability to implement your own XMLHttpRequest | function | - | | | data | Uploading extra params or function which can return uploading extra params | object \| (file) => object \| Promise<object> | - | | | defaultFileList | Default list of files that have been uploaded | object\[] | - | | diff --git a/components/upload/index.zh-CN.md b/components/upload/index.zh-CN.md index 5138827980..203c0a02e4 100644 --- a/components/upload/index.zh-CN.md +++ b/components/upload/index.zh-CN.md @@ -22,7 +22,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/QaeBt_ZMg/Upload.svg | --- | --- | --- | --- | --- | | accept | 接受上传的文件类型, 详见 [input accept Attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept) | string | - | | | action | 上传的地址 | string \| (file) => Promise<string> | - | | -| beforeUpload | 上传文件之前的钩子,参数为上传的文件,若返回 `false` 则停止上传。支持返回一个 Promise 对象,Promise 对象 reject 时则停止上传,resolve 时开始上传( resolve 传入 `File` 或 `Blob` 对象则上传 resolve 传入对象)。**注意:IE9 不支持该方法** | (file, fileList) => boolean \| Promise<File> | - | | +| beforeUpload | 上传文件之前的钩子,参数为上传的文件,若返回 `false` 则停止上传。支持返回一个 Promise 对象,Promise 对象 reject 时则停止上传,resolve 时开始上传( resolve 传入 `File` 或 `Blob` 对象则上传 resolve 传入对象);也可以返回 `Upload.LIST_IGNORE`,此时列表中将不展示此文件。 `**注意:IE9 不支持该方法** | (file, fileList) => boolean \| Promise<File> \| `Upload.LIST_IGNORE` | - | | | customRequest | 通过覆盖默认的上传行为,可以自定义自己的上传实现 | function | - | | | data | 上传所需额外参数或返回上传额外参数的方法 | object\|(file) => object \| Promise<object> | - | | | defaultFileList | 默认已经上传的文件列表 | object\[] | - | | diff --git a/components/upload/interface.tsx b/components/upload/interface.tsx index 98d330d1b0..9ece53f14d 100755 --- a/components/upload/interface.tsx +++ b/components/upload/interface.tsx @@ -86,7 +86,7 @@ export interface UploadProps { showUploadList?: boolean | ShowUploadListInterface; multiple?: boolean; accept?: string; - beforeUpload?: (file: RcFile, FileList: RcFile[]) => boolean | Promise; + beforeUpload?: (file: RcFile, FileList: RcFile[]) => boolean | {} | Promise; onChange?: (info: UploadChangeParam) => void; listType?: UploadListType; className?: string; From d775fb830d581f30bc9346a1ad529a0074757437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E5=85=B4=E6=B4=B2?= Date: Mon, 15 Mar 2021 22:40:40 +0800 Subject: [PATCH 13/18] docs: fix ConfigProvider doc typo (#29776) --- components/config-provider/index.en-US.md | 2 +- components/config-provider/index.zh-CN.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/config-provider/index.en-US.md b/components/config-provider/index.en-US.md index 6a15f863d0..58e9bac2ef 100644 --- a/components/config-provider/index.en-US.md +++ b/components/config-provider/index.en-US.md @@ -57,7 +57,7 @@ Some components use dynamic style to support wave effect. You can config `csp` p ### ConfigProvider.config() `4.13.0+` -setting `Modal`、`messge`、`Notification` rootPrefixCls +setting `Modal`、`message`、`Notification` rootPrefixCls ```jsx ConfigProvider.config({ diff --git a/components/config-provider/index.zh-CN.md b/components/config-provider/index.zh-CN.md index 3b8c5b1eee..2804e6bc8c 100644 --- a/components/config-provider/index.zh-CN.md +++ b/components/config-provider/index.zh-CN.md @@ -58,7 +58,7 @@ export default () => ( ### ConfigProvider.config() `4.13.0+` -设置 `Modal`、`messge`、`Notification` rootPrefixCls +设置 `Modal`、`message`、`Notification` rootPrefixCls ```jsx ConfigProvider.config({ From b5ac892ada7305717aa58a9adc1c785908b04188 Mon Sep 17 00:00:00 2001 From: xrkffgg Date: Tue, 16 Mar 2021 09:33:16 +0800 Subject: [PATCH 14/18] docs: perf website --- components/config-provider/index.en-US.md | 2 +- components/config-provider/index.zh-CN.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/config-provider/index.en-US.md b/components/config-provider/index.en-US.md index 58e9bac2ef..bc20d84eff 100644 --- a/components/config-provider/index.en-US.md +++ b/components/config-provider/index.en-US.md @@ -57,7 +57,7 @@ Some components use dynamic style to support wave effect. You can config `csp` p ### ConfigProvider.config() `4.13.0+` -setting `Modal`、`message`、`Notification` rootPrefixCls +Setting `Modal`、`Message`、`Notification` rootPrefixCls. ```jsx ConfigProvider.config({ diff --git a/components/config-provider/index.zh-CN.md b/components/config-provider/index.zh-CN.md index 2804e6bc8c..2ba888256c 100644 --- a/components/config-provider/index.zh-CN.md +++ b/components/config-provider/index.zh-CN.md @@ -58,7 +58,7 @@ export default () => ( ### ConfigProvider.config() `4.13.0+` -设置 `Modal`、`message`、`Notification` rootPrefixCls +设置 `Modal`、`Message`、`Notification` rootPrefixCls。 ```jsx ConfigProvider.config({ From 92bc5dfdcd0e4c6e285629f6aeef444a11f46ad9 Mon Sep 17 00:00:00 2001 From: afc163 Date: Tue, 16 Mar 2021 14:44:06 +0800 Subject: [PATCH 15/18] fix: Tabs active item width changes (#29781) close #29532 --- components/tabs/style/index.less | 2 +- components/tabs/style/position.less | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/components/tabs/style/index.less b/components/tabs/style/index.less index 7929742b7e..44645b3304 100644 --- a/components/tabs/style/index.less +++ b/components/tabs/style/index.less @@ -170,7 +170,7 @@ &&-active &-btn { color: @tabs-highlight-color; - font-weight: 500; + text-shadow: 0 0 0.25px currentColor; } &&-disabled { diff --git a/components/tabs/style/position.less b/components/tabs/style/position.less index 475ff590fe..e015e76969 100644 --- a/components/tabs/style/position.less +++ b/components/tabs/style/position.less @@ -101,11 +101,6 @@ .@{tab-prefix-cls}-tab { padding: @tabs-vertical-padding; text-align: center; - - &-active .@{tab-prefix-cls}-tab-btn { - font-weight: normal; - text-shadow: 0 0 0.25px @tabs-active-color; - } } .@{tab-prefix-cls}-tab + .@{tab-prefix-cls}-tab { From 99289a0856c738c12a88d5f122ad9aebdbbd2b78 Mon Sep 17 00:00:00 2001 From: afc163 Date: Wed, 17 Mar 2021 14:56:32 +0800 Subject: [PATCH 16/18] style: better Checkbox/Radio label text layout (#29788) * style: optimize checkbox label text layout * style: optimize radio label text layout * fix transfer checkbox position * fix checkbox align issue in Transfer * fix snapshot --- components/checkbox/style/mixin.less | 13 +- .../__snapshots__/components.test.js.snap | 24 +- .../__tests__/__snapshots__/demo.test.js.snap | 4 +- .../__snapshots__/index.test.js.snap | 240 +++++++++--------- components/radio/style/index.less | 8 +- components/style/themes/compact.less | 1 - components/style/themes/default.less | 2 +- components/transfer/ListItem.tsx | 6 +- .../__tests__/__snapshots__/demo.test.js.snap | 110 ++++---- .../__snapshots__/index.test.js.snap | 36 +-- .../__tests__/__snapshots__/list.test.js.snap | 8 +- components/transfer/list.tsx | 4 +- components/transfer/style/index.less | 6 +- 13 files changed, 233 insertions(+), 229 deletions(-) diff --git a/components/checkbox/style/mixin.less b/components/checkbox/style/mixin.less index 990fcd79dd..ef8262ce1d 100644 --- a/components/checkbox/style/mixin.less +++ b/components/checkbox/style/mixin.less @@ -7,11 +7,9 @@ .reset-component(); position: relative; - top: -0.09em; - display: inline-block; + top: 0.2em; line-height: 1; white-space: nowrap; - vertical-align: middle; outline: none; cursor: pointer; @@ -148,13 +146,15 @@ .@{checkbox-prefix-cls}-wrapper { .reset-component(); - - display: inline-block; + display: inline-flex; + align-items: baseline; line-height: unset; cursor: pointer; + &.@{checkbox-prefix-cls}-wrapper-disabled { cursor: not-allowed; } + & + & { margin-left: 8px; } @@ -167,10 +167,9 @@ .@{checkbox-prefix-cls}-group { .reset-component(); - display: inline-block; + &-item { - display: inline-block; margin-right: @checkbox-group-item-margin-right; &:last-child { margin-right: 0; diff --git a/components/config-provider/__tests__/__snapshots__/components.test.js.snap b/components/config-provider/__tests__/__snapshots__/components.test.js.snap index 305ab6da70..ef5e12bdfe 100644 --- a/components/config-provider/__tests__/__snapshots__/components.test.js.snap +++ b/components/config-provider/__tests__/__snapshots__/components.test.js.snap @@ -33761,7 +33761,7 @@ exports[`ConfigProvider components Transfer configProvider 1`] = ` class="config-transfer-list-header" >