diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index e1137d1e14..d0347c38a6 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -15,6 +15,21 @@ timeline: true --- +## 4.6.6 + +`2020-09-27` + +- 🐞 Fix Steps first item shifts in small screen. [#26894](https://github.com/ant-design/ant-design/pull/26894) +- 💄 Fix Divider border style not work when text is provided. [#26863](https://github.com/ant-design/ant-design/pull/26863) +- 🐞 Fix Radio.Button validation error highlight. [#26849](https://github.com/ant-design/ant-design/pull/26849) [@dhorelik](https://github.com/dhorelik) +- 💄 Fix Typography link-decoration style. [#26854](https://github.com/ant-design/ant-design/pull/26854) [@vineetvk01](https://github.com/vineetvk01) +- Locale + - 🌐 Add Thai locale support. [#26906](https://github.com/ant-design/ant-design/pull/26906) [@anawinwz](https://github.com/anawinwz) +- TypeScript + - 🤖 Fix message.destroy parameter type. [#26864](https://github.com/ant-design/ant-design/pull/26864) [@lihqi](https://github.com/lihqi) + - 🤖 Optimize Slider type definition. [#26884](https://github.com/ant-design/ant-design/pull/26884) + - 🤖 Form properly export `FormListProps` type. [#26831](https://github.com/ant-design/ant-design/pull/26831) [@mgcrea](https://github.com/mgcrea) + ## 4.6.5 `2020-09-20` diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 40980ba7bf..64f65066b5 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -15,6 +15,21 @@ timeline: true --- +## 4.6.6 + +`2020-09-27` + +- 🐞 修复 Steps 在小屏幕下第一项偏移的问题。[#26894](https://github.com/ant-design/ant-design/pull/26894) +- 💄 修复 Divider 在有文字时,设置边框颜色无效的问题。[#26863](https://github.com/ant-design/ant-design/pull/26863) +- 🐞 修复 Radio.Button 错误校验高亮样式的问题。[#26849](https://github.com/ant-design/ant-design/pull/26849) [@dhorelik](https://github.com/dhorelik) +- 💄 修复 Typography 链接下划线样式。[#26854](https://github.com/ant-design/ant-design/pull/26854) [@vineetvk01](https://github.com/vineetvk01) +- 国际化 + - 🌐 添加泰语支持。[#26906](https://github.com/ant-design/ant-design/pull/26906) [@anawinwz](https://github.com/anawinwz) +- TypeScript + - 🤖 修复 message.destroy 参数类型错误。[#26864](https://github.com/ant-design/ant-design/pull/26864) [@lihqi](https://github.com/lihqi) + - 🤖 优化 Slider 类型定义。[#26884](https://github.com/ant-design/ant-design/pull/26884) + - 🤖 导出 Form 中的 `FormListProps` 类型。[#26831](https://github.com/ant-design/ant-design/pull/26831) [@mgcrea](https://github.com/mgcrea) + ## 4.6.5 `2020-09-20` diff --git a/components/locale/pl_PL.tsx b/components/locale/pl_PL.tsx index ac3c2c836c..ec7d2dc2d4 100644 --- a/components/locale/pl_PL.tsx +++ b/components/locale/pl_PL.tsx @@ -16,6 +16,9 @@ const localeValues: Locale = { filterReset: 'Wyczyść', selectAll: 'Zaznacz bieżącą stronę', selectInvert: 'Odwróć zaznaczenie', + triggerDesc: 'Sortuj rosnąco', + triggerAsc: 'Sortuj malejąco', + cancelSort: 'Usuń sortowanie', }, Modal: { okText: 'OK', diff --git a/components/message/__tests__/config.test.js b/components/message/__tests__/config.test.js index e32e82e9e2..4e9ea08856 100644 --- a/components/message/__tests__/config.test.js +++ b/components/message/__tests__/config.test.js @@ -1,7 +1,15 @@ import { sleep } from '../../../tests/utils'; -import message from '..'; +import message, { getInstance } from '..'; describe('message.config', () => { + // Mock for rc-util raf + window.requestAnimationFrame = callback => { + return window.setTimeout(callback, 16); + }; + window.cancelAnimationFrame = id => { + window.clearTimeout(id); + }; + beforeEach(() => { jest.useFakeTimers(); }); @@ -47,11 +55,12 @@ describe('message.config', () => { for (let i = 0; i < 10; i += 1) { message.info('test'); } + message.info('last'); expect(document.querySelectorAll('.ant-message-notice').length).toBe(5); expect(document.querySelectorAll('.ant-message-notice')[4].textContent).toBe('last'); jest.runAllTimers(); - expect(document.querySelectorAll('.ant-message-notice').length).toBe(0); + expect(getInstance().component.state.notices).toHaveLength(0); }); it('should be able to config duration', async () => { @@ -60,8 +69,10 @@ describe('message.config', () => { duration: 0.5, }); message.info('last'); + expect(getInstance().component.state.notices).toHaveLength(1); + await sleep(1000); - expect(document.querySelectorAll('.ant-message-notice').length).toBe(0); + expect(getInstance().component.state.notices).toHaveLength(0); message.config({ duration: 3, }); diff --git a/components/message/__tests__/hooks.test.js b/components/message/__tests__/hooks.test.js index 7b452b9cfe..b1d3811b62 100644 --- a/components/message/__tests__/hooks.test.js +++ b/components/message/__tests__/hooks.test.js @@ -1,7 +1,7 @@ /* eslint-disable jsx-a11y/control-has-associated-label */ import React from 'react'; import { mount } from 'enzyme'; -import message from '..'; +import message, { getInstance } from '..'; import ConfigProvider from '../../config-provider'; describe('message.hooks', () => { @@ -171,7 +171,7 @@ describe('message.hooks', () => { expect(document.querySelectorAll('.my-test-message-notice').length).toBe(1); hide(); jest.runAllTimers(); - expect(document.querySelectorAll('.my-test-message-notice').length).toBe(0); + expect(getInstance().component.state.notices).toHaveLength(0); }); it('should be same hook', () => { diff --git a/components/message/__tests__/index.test.js b/components/message/__tests__/index.test.js index b91b26dc00..cb8d4d14c0 100644 --- a/components/message/__tests__/index.test.js +++ b/components/message/__tests__/index.test.js @@ -1,7 +1,7 @@ import React from 'react'; import { mount } from 'enzyme'; import { SmileOutlined } from '@ant-design/icons'; -import message from '..'; +import message, { getInstance } from '..'; describe('message', () => { beforeEach(() => { @@ -19,10 +19,10 @@ describe('message', () => { expect(document.querySelectorAll('.ant-message-notice').length).toBe(2); hide1(); jest.runAllTimers(); - expect(document.querySelectorAll('.ant-message-notice').length).toBe(1); + expect(getInstance().component.state.notices).toHaveLength(1); hide2(); jest.runAllTimers(); - expect(document.querySelectorAll('.ant-message-notice').length).toBe(0); + expect(getInstance().component.state.notices).toHaveLength(0); }); it('should be able to remove manually with a unique key', () => { @@ -33,10 +33,10 @@ describe('message', () => { expect(document.querySelectorAll('.ant-message-notice').length).toBe(2); message.destroy(key1); jest.runAllTimers(); - expect(document.querySelectorAll('.ant-message-notice').length).toBe(1); + expect(getInstance().component.state.notices).toHaveLength(1); message.destroy(key2); jest.runAllTimers(); - expect(document.querySelectorAll('.ant-message-notice').length).toBe(0); + expect(getInstance().component.state.notices).toHaveLength(0); }); it('should be able to destroy globally', () => { @@ -93,7 +93,7 @@ describe('message', () => { expect(document.querySelectorAll('.ant-message-notice').length).toBe(1); hide(); jest.runAllTimers(); - expect(document.querySelectorAll('.ant-message-notice').length).toBe(0); + expect(getInstance().component.state.notices).toHaveLength(0); }); it('should allow custom icon', () => { @@ -169,7 +169,7 @@ describe('message', () => { mount(); expect(document.querySelectorAll('.ant-message-notice').length).toBe(1); jest.advanceTimersByTime(1500); - expect(document.querySelectorAll('.ant-message-notice').length).toBe(0); + expect(getInstance().component.state.notices).toHaveLength(0); }); it('should not throw error when pass null', () => { diff --git a/components/message/index.tsx b/components/message/index.tsx index 5367ab5df1..e58344b0d3 100755 --- a/components/message/index.tsx +++ b/components/message/index.tsx @@ -193,7 +193,7 @@ function isArgsProps(content: JointContent): content is ArgsProps { const api: any = { open: notice, config: setMessageConfig, - destroy(messageKey?: number | string) { + destroy(messageKey?: React.Key) { if (messageInstance) { if (messageKey) { const { removeNotice } = messageInstance; @@ -243,8 +243,13 @@ export interface MessageInstance { export interface MessageApi extends MessageInstance { warn(content: JointContent, duration?: ConfigDuration, onClose?: ConfigOnClose): MessageType; config(options: ConfigOptions): void; - destroy(): void; + destroy(messageKey?: React.Key): void; useMessage(): [MessageInstance, React.ReactElement]; } +/** @private test only function. Not work on production */ +export const getInstance = () => { + return process.env.NODE_ENV === 'test' ? messageInstance : null; +}; + export default api as MessageApi; diff --git a/components/notification/__tests__/index.test.js b/components/notification/__tests__/index.test.js index 518aac9e26..6b17f3fc8d 100644 --- a/components/notification/__tests__/index.test.js +++ b/components/notification/__tests__/index.test.js @@ -1,18 +1,15 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { UserOutlined } from '@ant-design/icons'; -import notification from '..'; +import notification, { getInstance } from '..'; describe('notification', () => { - beforeAll(() => { + beforeEach(() => { jest.useFakeTimers(); }); - afterAll(() => { - jest.useRealTimers(); - }); - afterEach(() => { + jest.useRealTimers(); notification.destroy(); }); @@ -54,14 +51,18 @@ describe('notification', () => { await Promise.resolve(); expect(document.querySelectorAll('.ant-notification-notice').length).toBe(2); + notification.close('1'); - await Promise.resolve(); jest.runAllTimers(); - expect(document.querySelectorAll('.ant-notification-notice').length).toBe(1); + expect((await getInstance('ant-notification-topRight')).component.state.notices).toHaveLength( + 1, + ); + notification.close('2'); - await Promise.resolve(); jest.runAllTimers(); - expect(document.querySelectorAll('.ant-notification-notice').length).toBe(0); + expect((await getInstance('ant-notification-topRight')).component.state.notices).toHaveLength( + 0, + ); }); it('should be able to destroy globally', async () => { diff --git a/components/notification/index.tsx b/components/notification/index.tsx index d772908640..c38ea8acb9 100755 --- a/components/notification/index.tsx +++ b/components/notification/index.tsx @@ -280,4 +280,9 @@ export interface NotificationApi extends NotificationInstance { useNotification: () => [NotificationInstance, React.ReactElement]; } +/** @private test only function. Not work on production */ +export const getInstance = async (cacheKey: string) => { + return process.env.NODE_ENV === 'test' ? notificationInstance[cacheKey] : null; +}; + export default api as NotificationApi; diff --git a/components/slider/index.tsx b/components/slider/index.tsx index 95121dd936..a8363f2824 100644 --- a/components/slider/index.tsx +++ b/components/slider/index.tsx @@ -1,7 +1,5 @@ import * as React from 'react'; -import RcSlider from 'rc-slider/lib/Slider'; -import RcRange from 'rc-slider/lib/Range'; -import RcHandle from 'rc-slider/lib/Handle'; +import RcSlider, { Range as RcRange, Handle as RcHandle } from 'rc-slider'; import classNames from 'classnames'; import { TooltipPlacement } from '../tooltip'; import SliderTooltip from './SliderTooltip'; @@ -18,16 +16,15 @@ export interface SliderMarks { interface HandleGeneratorInfo { value?: number; - dragging: boolean; + dragging?: boolean; index: number; - rest: any[]; } export type HandleGeneratorFn = (config: { tooltipPrefixCls?: string; prefixCls?: string; info: HandleGeneratorInfo; -}) => React.ReactNode; +}) => React.ReactElement; export interface SliderBaseProps { prefixCls?: string; @@ -35,7 +32,7 @@ export interface SliderBaseProps { reverse?: boolean; min?: number; max?: number; - step?: number | null; + step?: number; marks?: SliderMarks; dots?: boolean; included?: boolean; @@ -70,81 +67,98 @@ export interface SliderRangeProps extends SliderBaseProps { export type Visibles = { [index: number]: boolean }; -const Slider = React.forwardRef((props, ref) => { - const { getPrefixCls, direction, getPopupContainer } = React.useContext(ConfigContext); - const [visibles, setVisibles] = React.useState({}); +const Slider = React.forwardRef( + (props, ref: any) => { + const { getPrefixCls, direction, getPopupContainer } = React.useContext(ConfigContext); + const [visibles, setVisibles] = React.useState({}); - const toggleTooltipVisible = (index: number, visible: boolean) => { - setVisibles((prev: Visibles) => { - return { ...prev, [index]: visible }; - }); - }; + const toggleTooltipVisible = (index: number, visible: boolean) => { + setVisibles((prev: Visibles) => { + return { ...prev, [index]: visible }; + }); + }; - const getTooltipPlacement = (tooltipPlacement?: TooltipPlacement, vertical?: boolean) => { - if (tooltipPlacement) { - return tooltipPlacement; - } - if (!vertical) { - return 'top'; - } - return direction === 'rtl' ? 'left' : 'right'; - }; + const getTooltipPlacement = (tooltipPlacement?: TooltipPlacement, vertical?: boolean) => { + if (tooltipPlacement) { + return tooltipPlacement; + } + if (!vertical) { + return 'top'; + } + return direction === 'rtl' ? 'left' : 'right'; + }; + + const handleWithTooltip: HandleGeneratorFn = ({ + tooltipPrefixCls, + prefixCls, + info: { value, dragging, index, ...restProps }, + }) => { + const { + tipFormatter, + tooltipVisible, + tooltipPlacement, + getTooltipPopupContainer, + vertical, + } = props; + const isTipFormatter = tipFormatter ? visibles[index] || dragging : false; + const visible = tooltipVisible || (tooltipVisible === undefined && isTipFormatter); + return ( + document.body)} + > + toggleTooltipVisible(index, true)} + onMouseLeave={() => toggleTooltipVisible(index, false)} + /> + + ); + }; - const handleWithTooltip: HandleGeneratorFn = ({ - tooltipPrefixCls, - prefixCls, - info: { value, dragging, index, ...restProps }, - }) => { const { - tipFormatter, - tooltipVisible, - tooltipPlacement, - getTooltipPopupContainer, - vertical, + prefixCls: customizePrefixCls, + tooltipPrefixCls: customizeTooltipPrefixCls, + range, + className, + ...restProps } = props; - const isTipFormatter = tipFormatter ? visibles[index] || dragging : false; - const visible = tooltipVisible || (tooltipVisible === undefined && isTipFormatter); - return ( - document.body)} - > - toggleTooltipVisible(index, true)} - onMouseLeave={() => toggleTooltipVisible(index, false)} + const prefixCls = getPrefixCls('slider', customizePrefixCls); + const tooltipPrefixCls = getPrefixCls('tooltip', customizeTooltipPrefixCls); + const cls = classNames(className, { + [`${prefixCls}-rtl`]: direction === 'rtl', + }); + // make reverse default on rtl direction + if (direction === 'rtl' && !restProps.vertical) { + restProps.reverse = !restProps.reverse; + } + if (range) { + return ( + + handleWithTooltip({ + tooltipPrefixCls, + prefixCls, + info, + }) + } + prefixCls={prefixCls} /> - - ); - }; - - const { - prefixCls: customizePrefixCls, - tooltipPrefixCls: customizeTooltipPrefixCls, - range, - className, - ...restProps - } = props; - const prefixCls = getPrefixCls('slider', customizePrefixCls); - const tooltipPrefixCls = getPrefixCls('tooltip', customizeTooltipPrefixCls); - const cls = classNames(className, { - [`${prefixCls}-rtl`]: direction === 'rtl', - }); - // make reverse default on rtl direction - if (direction === 'rtl' && !restProps.vertical) { - restProps.reverse = !restProps.reverse; - } - if (range) { + ); + } return ( - @@ -155,27 +169,10 @@ const Slider = React.forwardRef(( }) } prefixCls={prefixCls} - tooltipPrefixCls={tooltipPrefixCls} /> ); - } - return ( - - handleWithTooltip({ - tooltipPrefixCls, - prefixCls, - info, - }) - } - prefixCls={prefixCls} - tooltipPrefixCls={tooltipPrefixCls} - /> - ); -}); + }, +); Slider.displayName = 'Slider'; diff --git a/components/steps/style/vertical.less b/components/steps/style/vertical.less index d9c87bbc52..cc316946df 100644 --- a/components/steps/style/vertical.less +++ b/components/steps/style/vertical.less @@ -4,7 +4,9 @@ .@{steps-prefix-cls}-item { display: block; flex: 1 0 auto; + padding-left: 0; overflow: visible; + &-icon { float: left; margin-right: @steps-vertical-icon-width; diff --git a/index.js b/index.js index 465ade6fd6..0a58333efa 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ /* eslint no-console:0 */ -function camelCase(name) { +function pascalCase(name) { return name.charAt(0).toUpperCase() + name.slice(1).replace(/-(\w)/g, (m, n) => n.toUpperCase()); } @@ -17,7 +17,7 @@ req.keys().forEach(mod => { // message & notification should not be capitalized exports[match[1]] = v; } else { - exports[camelCase(match[1])] = v; + exports[pascalCase(match[1])] = v; } } }); diff --git a/package.json b/package.json index b32f66b1f4..332217bc00 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "antd", - "version": "4.6.5", + "version": "4.6.6", "description": "An enterprise-class UI design language and React components implementation", "title": "Ant Design", "keywords": [ @@ -129,15 +129,15 @@ "rc-input-number": "~6.0.0", "rc-mentions": "~1.5.0", "rc-menu": "~8.7.1", - "rc-motion": "^2.0.0", - "rc-notification": "~4.4.0", + "rc-motion": "^2.2.0", + "rc-notification": "~4.5.2", "rc-pagination": "~3.0.3", "rc-picker": "~2.2.1", "rc-progress": "~3.1.0", "rc-rate": "~2.8.2", "rc-resize-observer": "^0.2.3", "rc-select": "~11.3.2", - "rc-slider": "~9.4.1", + "rc-slider": "~9.5.1", "rc-steps": "~4.1.0", "rc-switch": "~3.2.0", "rc-table": "~7.9.2", @@ -195,7 +195,7 @@ "enquire-js": "^0.2.1", "enzyme": "^3.10.0", "enzyme-adapter-react-16": "^1.14.0", - "enzyme-to-json": "^3.3.5", + "enzyme-to-json": "^3.6.0", "esbuild-webpack-plugin": "^1.0.0", "eslint": "^7.9.0", "eslint-config-airbnb": "^18.0.0", diff --git a/typings/custom-typings.d.ts b/typings/custom-typings.d.ts index 6a302f4c67..809a080bfd 100644 --- a/typings/custom-typings.d.ts +++ b/typings/custom-typings.d.ts @@ -46,14 +46,6 @@ declare module 'rc-rate'; declare module 'rc-queue-anim'; -declare module 'rc-slider'; - -declare module 'rc-slider/lib/Slider'; - -declare module 'rc-slider/lib/Range'; - -declare module 'rc-slider/lib/Handle'; - declare module 'rc-steps'; declare module 'rc-switch';