From f288aff9fe2013233cb9f4bb1a96c5d0069dfd8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B6=E6=9E=AB?= <7971419+crazyair@users.noreply.github.com> Date: Sun, 6 Jun 2021 13:41:26 +0800 Subject: [PATCH] fix: Typography `icons` should ignore React.Fragment (#30869) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: fix-text * feat: 优化代码 * chore: add test * chore: 打算重构 test * chore: 重构 test * chore: 重构 test * chore: 重构 test * feat: review * feat: review * feat: add test * feat: add test * fix: test * fix: tsx * fix: 优化 * fix: 优化 * fix: map 当前处理 * fix: getNode function * fix: 优化方法 * fix: ci * fix: aria-label --- components/typography/Base.tsx | 31 ++- components/typography/__tests__/copy.test.tsx | 197 ++++++++++++++++++ 2 files changed, 218 insertions(+), 10 deletions(-) create mode 100644 components/typography/__tests__/copy.test.tsx diff --git a/components/typography/Base.tsx b/components/typography/Base.tsx index 2eb116f1c9..049dc75bd7 100644 --- a/components/typography/Base.tsx +++ b/components/typography/Base.tsx @@ -93,6 +93,13 @@ function wrapperDecorations( return currentContent; } +function getNode(dom: React.ReactNode, defaultNode: React.ReactNode, needDom?: boolean) { + if (dom === true || dom === undefined) { + return defaultNode; + } + return dom || (needDom && defaultNode); +} + interface InternalBlockProps extends BlockProps { component: string; } @@ -391,23 +398,27 @@ class Base extends React.Component { const prefixCls = this.getPrefixCls(); - const { tooltips } = copyable as CopyConfig; - let tooltipNodes = toArray(tooltips) as React.ReactNode[]; - if (tooltipNodes.length === 0) { - tooltipNodes = [this.copyStr, this.copiedStr]; - } - const title = copied ? tooltipNodes[1] : tooltipNodes[0]; - const ariaLabel = typeof title === 'string' ? title : ''; - const icons = toArray((copyable as CopyConfig).icon); + const { tooltips, icon } = copyable as CopyConfig; + + const tooltipNodes = Array.isArray(tooltips) ? tooltips : [tooltips]; + const iconNodes = Array.isArray(icon) ? icon : [icon]; + + const title = copied + ? getNode(tooltipNodes[1], this.copiedStr) + : getNode(tooltipNodes[0], this.copyStr); + const systemStr = copied ? this.copiedStr : this.copyStr; + const ariaLabel = typeof title === 'string' ? title : systemStr; return ( - + - {copied ? icons[1] || : icons[0] || } + {copied + ? getNode(iconNodes[1], , true) + : getNode(iconNodes[0], , true)} ); diff --git a/components/typography/__tests__/copy.test.tsx b/components/typography/__tests__/copy.test.tsx new file mode 100644 index 0000000000..7dc502cd1e --- /dev/null +++ b/components/typography/__tests__/copy.test.tsx @@ -0,0 +1,197 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import { SmileOutlined, LikeOutlined } from '@ant-design/icons'; + +import Base from '../Base'; + +describe('Typography copy', () => { + const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + + afterEach(() => { + errorSpy.mockReset(); + }); + + describe('Base', () => { + describe('copyable', () => { + function copyTest({ + name, + icon, + tooltips, + iconClassNames = [], + iconTexts = [], + tooltipTexts = [], + tooltipLength, + }: { + name: string; + icon?: boolean | React.ReactNode; + tooltips?: boolean | React.ReactNode; + iconClassNames?: string[]; + iconTexts?: string[]; + tooltipTexts?: string[]; + tooltipLength?: number; + }) { + it(name, async () => { + jest.useFakeTimers(); + const wrapper = mount( + + test copy + , + ); + if (iconClassNames[0] !== undefined) { + expect(wrapper.exists(iconClassNames[0])).toBeTruthy(); + } + if (iconTexts[0] !== undefined) { + expect(wrapper.find('.ant-typography-copy').at(0).text()).toBe(iconTexts[0]); + } + + wrapper.find('.ant-typography-copy').first().simulate('mouseenter'); + jest.runAllTimers(); + wrapper.update(); + + if (tooltipTexts[0] !== undefined) { + expect(wrapper.find('.ant-tooltip-inner').text()).toBe(tooltipTexts[0]); + } + + if (tooltipLength !== undefined) { + expect(wrapper.find('.ant-tooltip-inner').length).toBe(tooltipLength); + } + + wrapper.find('.ant-typography-copy').first().simulate('click'); + jest.useRealTimers(); + if (iconClassNames[1] !== undefined) { + expect(wrapper.exists(iconClassNames[1])).toBeTruthy(); + } + wrapper.find('.ant-typography-copy').first().simulate('mouseenter'); + wrapper.update(); + + wrapper.find('.ant-typography-copy').first().simulate('mouseenter'); + + if (tooltipTexts[1] !== undefined) { + expect(wrapper.find('.ant-tooltip-inner').text()).toBe(tooltipTexts[1]); + } + + if (iconTexts[1] !== undefined) { + expect(wrapper.find('.ant-typography-copy').at(0).text()).toBe(iconTexts[1]); + } + + jest.useFakeTimers(); + wrapper.find('.ant-typography-copy').first().simulate('click'); + jest.runAllTimers(); + wrapper.update(); + + wrapper.unmount(); + jest.useRealTimers(); + }); + } + const dom = ( + <> + 12 + + ); + const dom2 = ( + <> + 34 + + ); + const copy = '.anticon-copy'; + const check = '.anticon-check'; + + copyTest({ + name: 'icon basic copy', + iconClassNames: [copy, check], + tooltipTexts: ['Copy', 'Copied'], + }); + copyTest({ name: 'icon true', icon: true, iconClassNames: [copy, check] }); + copyTest({ name: 'icon two true', icon: [true, true], iconClassNames: [copy, check] }); + copyTest({ name: 'icon false', icon: false, iconClassNames: [copy, check] }); + copyTest({ name: 'icon custom text', icon: ['a', 'b'], iconTexts: ['a', 'b'] }); + copyTest({ name: 'icon custom element', icon: [dom, dom2], iconTexts: ['12', '34'] }); + copyTest({ + name: 'icon custom icon', + icon: , + iconClassNames: ['.anticon-smile', check], + }); + copyTest({ + name: 'icon custom icon2', + icon: [, ], + iconClassNames: ['.anticon-smile', '.anticon-like'], + }); + copyTest({ + name: 'icon custom icon3', + icon: [ + <> + + + , + , + ], + iconClassNames: ['.anticon-smile', '.anticon-like'], + }); + copyTest({ + name: 'icon custom icon4', + icon: ( + <> + + + + ), + iconClassNames: ['.anticon-smile', check], + }); + copyTest({ + name: 'icon custom icon5', + icon: ( + <> + + + + ), + iconClassNames: ['.anticon-like', check], + }); + copyTest({ + name: 'tooltips true', + tooltips: true, + tooltipLength: 1, + tooltipTexts: ['Copy', 'Copied'], + }); + copyTest({ name: 'tooltips false', tooltips: false, tooltipLength: 0 }); + copyTest({ + name: 'tooltips custom text', + tooltips: ['a', 'b'], + tooltipLength: 1, + tooltipTexts: ['a', 'b'], + }); + copyTest({ + name: 'tooltips custom element ', + tooltips: [dom, dom2], + tooltipTexts: ['12', '34'], + }); + copyTest({ + name: 'tooltips first empty', + tooltips: ['', 'xxx'], + tooltipLength: 0, + }); + copyTest({ + name: 'tooltips first empty 2', + tooltips: [''], + tooltipLength: 0, + }); + + copyTest({ + name: 'tooltips true true', + tooltips: [true, true], + tooltipTexts: ['Copy', 'Copied'], + }); + copyTest({ + name: 'tooltips true false', + tooltips: [true, false], + tooltipTexts: ['Copy', ''], + }); + + copyTest({ + name: 'tooltips false true', + tooltips: [false, true], + tooltipLength: 0, + }); + }); + }); +});