From 25883ca53a058c4e24bf374bf01514d88c0dbdc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BA=A2=E6=9E=9C=E6=B1=81?= Date: Mon, 29 May 2023 16:50:12 +0800 Subject: [PATCH] fix: ColorPicker hover boundary issues (#42669) * fix: fix hover boundary issues * test: fix test case * build: modify dependency scope * Update package.json Co-authored-by: MadCcc <1075746765@qq.com> --------- Co-authored-by: afc163 Co-authored-by: MadCcc <1075746765@qq.com> --- components/color-picker/ColorPicker.tsx | 26 +++++++++-- components/color-picker/ColorPickerPanel.tsx | 17 +++++-- .../color-picker/__tests__/index.test.tsx | 44 ++++++++++++++++++- package.json | 4 +- 4 files changed, 81 insertions(+), 10 deletions(-) diff --git a/components/color-picker/ColorPicker.tsx b/components/color-picker/ColorPicker.tsx index ea8083c1ba..221b396f7a 100644 --- a/components/color-picker/ColorPicker.tsx +++ b/components/color-picker/ColorPicker.tsx @@ -5,7 +5,7 @@ import type { import classNames from 'classnames'; import useMergedState from 'rc-util/lib/hooks/useMergedState'; import type { CSSProperties } from 'react'; -import React, { useContext, useEffect, useState } from 'react'; +import React, { useContext, useEffect, useRef, useState } from 'react'; import genPurePanel from '../_util/PurePanel'; import type { ConfigConsumerProps } from '../config-provider/context'; import { ConfigContext } from '../config-provider/context'; @@ -97,8 +97,9 @@ const ColorPicker: CompoundedComponent = (props) => { [`${prefixCls}-rtl`]: direction, }); const mergeCls = classNames(mergeRootCls, className, hashId); + const popupAllowCloseRef = useRef(true); - const handleChange = (data: Color, type?: HsbaColorType) => { + const handleChange = (data: Color, type?: HsbaColorType, pickColor?: boolean) => { let color: Color = generateColor(data); if (colorCleared) { setColorCleared(false); @@ -112,6 +113,10 @@ const ColorPicker: CompoundedComponent = (props) => { if (!value) { setColorValue(color); } + // Only for drag-and-drop color picking + if (pickColor) { + popupAllowCloseRef.current = false; + } onChange?.(color, color.toHexString()); }; @@ -119,6 +124,10 @@ const ColorPicker: CompoundedComponent = (props) => { setColorCleared(clear); }; + const handleChangeComplete = () => { + popupAllowCloseRef.current = true; + }; + const popoverProps: PopoverProps = { open: popupOpen, trigger, @@ -149,9 +158,18 @@ const ColorPicker: CompoundedComponent = (props) => { return wrapSSR( { + if (popupAllowCloseRef.current) { + setPopupOpen(visible); + } + }} content={ - + } overlayClassName={prefixCls} {...popoverProps} diff --git a/components/color-picker/ColorPickerPanel.tsx b/components/color-picker/ColorPickerPanel.tsx index c6841c2dae..f0138b9588 100644 --- a/components/color-picker/ColorPickerPanel.tsx +++ b/components/color-picker/ColorPickerPanel.tsx @@ -10,12 +10,22 @@ import ColorPresets from './components/ColorPresets'; import type { ColorPickerBaseProps } from './interface'; interface ColorPickerPanelProps extends ColorPickerBaseProps { - onChange?: (value?: Color, type?: HsbaColorType) => void; + onChange?: (value?: Color, type?: HsbaColorType, pickColor?: boolean) => void; + onChangeComplete?: (type?: HsbaColorType) => void; onClear?: (clear?: boolean) => void; } const ColorPickerPanel: FC = (props) => { - const { prefixCls, allowClear, presets, onChange, onClear, color, ...injectProps } = props; + const { + prefixCls, + allowClear, + presets, + onChange, + onClear, + onChangeComplete, + color, + ...injectProps + } = props; const colorPickerPanelPrefixCls = `${prefixCls}-inner-panel`; const extraPanelRender = (panel: React.ReactNode) => ( @@ -45,8 +55,9 @@ const ColorPickerPanel: FC = (props) => { onChange?.(colorValue, type, true)} panelRender={extraPanelRender} + onChangeComplete={onChangeComplete} /> ); }; diff --git a/components/color-picker/__tests__/index.test.tsx b/components/color-picker/__tests__/index.test.tsx index 7f9083d061..554ed18f12 100644 --- a/components/color-picker/__tests__/index.test.tsx +++ b/components/color-picker/__tests__/index.test.tsx @@ -1,4 +1,5 @@ -import { fireEvent, render } from '@testing-library/react'; +import { createEvent, fireEvent, render } from '@testing-library/react'; +import { spyElementPrototypes } from 'rc-util/lib/test/domHook'; import React, { useMemo, useState } from 'react'; import mountTest from '../../../tests/shared/mountTest'; import rtlTest from '../../../tests/shared/rtlTest'; @@ -6,6 +7,28 @@ import { waitFakeTimer } from '../../../tests/utils'; import ColorPicker from '../ColorPicker'; import type { Color } from '../color'; +function doMouseMove( + container: HTMLElement, + start: number, + end: number, + element = 'ant-color-picker-handler', +) { + const mouseDown = createEvent.mouseDown(container.getElementsByClassName(element)[0], { + pageX: start, + pageY: start, + }); + fireEvent(container.getElementsByClassName(element)[0], mouseDown); + // Drag + const mouseMove: any = new Event('mousemove'); + mouseMove.pageX = end; + mouseMove.pageY = end; + + fireEvent(document, mouseMove); + + const mouseUp = createEvent.mouseUp(document); + fireEvent(document, mouseUp); +} + describe('ColorPicker', () => { mountTest(ColorPicker); rtlTest(ColorPicker); @@ -257,4 +280,23 @@ describe('ColorPicker', () => { container.querySelector('.ant-color-picker-color-block-inner')?.getAttribute('style'), ).toEqual('background: rgb(99, 22, 22);'); }); + + it('Should fix hover boundary issues', async () => { + spyElementPrototypes(HTMLElement, { + getBoundingClientRect: () => ({ + x: 0, + y: 100, + width: 100, + height: 100, + }), + }); + const { container } = render(); + fireEvent.mouseEnter(container.querySelector('.ant-color-picker-trigger')!); + await waitFakeTimer(); + doMouseMove(container, 0, 999); + expect(container.querySelector('.ant-popover-hidden')).toBeFalsy(); + fireEvent.mouseLeave(container.querySelector('.ant-color-picker-trigger')!); + await waitFakeTimer(); + expect(container.querySelector('.ant-popover-hidden')).toBeTruthy(); + }); }); diff --git a/package.json b/package.json index f522cfd2d3..73f205f2da 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,7 @@ "@ant-design/react-slick": "~1.0.0", "@babel/runtime": "^7.18.3", "@ctrl/tinycolor": "^3.6.0", - "@rc-component/color-picker": "~1.1.1", + "@rc-component/color-picker": "~1.2.0", "@rc-component/mutate-observer": "^1.0.0", "@rc-component/tour": "~1.8.0", "@rc-component/trigger": "^1.13.0", @@ -324,4 +324,4 @@ "*.{ts,tsx,js,jsx}": "prettier --ignore-unknown --write", "*.{json,less,md}": "prettier --ignore-unknown --write" } -} +} \ No newline at end of file