diff --git a/src/Rate.tsx b/src/Rate.tsx index b029992..b0a1143 100644 --- a/src/Rate.tsx +++ b/src/Rate.tsx @@ -22,6 +22,9 @@ export interface RateProps onFocus?: () => void; onBlur?: () => void; onKeyDown?: React.KeyboardEventHandler; + onMouseEnter?: React.MouseEventHandler; + onMouseLeave?: React.MouseEventHandler; + id?: string; autoFocus?: boolean; direction?: string; } @@ -37,6 +40,7 @@ function Rate(props: RateProps, ref: React.Ref) { prefixCls = 'rc-rate', className, style, + id, // Value defaultValue, @@ -61,8 +65,27 @@ function Rate(props: RateProps, ref: React.Ref) { onFocus, onBlur, onKeyDown, + onMouseEnter, + onMouseLeave, + + ...restProps } = props; + const dataOrAriaAttributeProps = Object.keys(restProps).reduce( + (prev, key) => { + if ( + key.substr(0, 5) === 'data-' || + key.substr(0, 5) === 'aria-' || + key === 'role' + ) { + // eslint-disable-next-line no-param-reassign + prev[key] = restProps[key]; + } + return prev; + }, + {}, + ); + const [getStarRef, setStarRef] = useRefs(); const rateRef = React.useRef(null); @@ -135,10 +158,15 @@ function Rate(props: RateProps, ref: React.Ref) { onHoverChange?.(nextHoverValue); }; - const onMouseLeave = () => { - setHoverValue(null); - setCleanedValue(null); - onHoverChange?.(undefined); + const onMouseLeaveCallback = (event?: React.MouseEvent) => { + if (!disabled) { + setHoverValue(null); + setCleanedValue(null); + onHoverChange?.(undefined); + } + if (event) { + onMouseLeave?.(event); + } }; // =========================== Click ============================ @@ -148,12 +176,11 @@ function Rate(props: RateProps, ref: React.Ref) { if (allowClear) { isReset = newValue === value; } - onMouseLeave(); + onMouseLeaveCallback(); changeValue(isReset ? 0 : newValue); setCleanedValue(isReset ? newValue : null); }; - // ========================== Keyboard ========================== const onInternalKeyDown: React.KeyboardEventHandler = (event) => { const { keyCode } = event; const reverse = direction === 'rtl'; @@ -233,13 +260,16 @@ function Rate(props: RateProps, ref: React.Ref) { [`${prefixCls}-rtl`]: direction === 'rtl', })} style={style} - onMouseLeave={disabled ? null : onMouseLeave} + id={id} + onMouseEnter={onMouseEnter} + onMouseLeave={onMouseLeaveCallback} tabIndex={disabled ? -1 : tabIndex} onFocus={disabled ? null : onInternalFocus} onBlur={disabled ? null : onInternalBlur} onKeyDown={disabled ? null : onInternalKeyDown} ref={rateRef} role="radiogroup" + {...dataOrAriaAttributeProps} > {starNodes} diff --git a/tests/simple.spec.js b/tests/simple.spec.js index 36953c0..5601199 100644 --- a/tests/simple.spec.js +++ b/tests/simple.spec.js @@ -289,5 +289,43 @@ describe('rate', () => { wrapper.simulate('keydown'); expect(onKeyDown).toHaveBeenCalled(); }); + + // https://github.com/ant-design/ant-design/issues/30940 + it('range picker should accept onMouseEnter and onMouseLeave event when Rate component is diabled', () => { + const handleMouseEnter = jest.fn(); + const handleMouseLeave = jest.fn(); + const wrapper = mount( + , + ); + wrapper.simulate('mouseenter'); + expect(handleMouseEnter).toHaveBeenCalled(); + wrapper.simulate('mouseleave'); + expect(handleMouseLeave).toHaveBeenCalled(); + }); + + it('range picker should accept onMouseEnter and onMouseLeave event when Rate component is not diabled', () => { + const handleMouseEnter = jest.fn(); + const handleMouseLeave = jest.fn(); + const wrapper = mount( + , + ); + wrapper.simulate('mouseenter'); + expect(handleMouseEnter).toHaveBeenCalled(); + wrapper.simulate('mouseleave'); + expect(handleMouseLeave).toHaveBeenCalled(); + }); + }); + + describe('html attributes', () => { + it('data-* and aria-* and role', () => { + const wrapper = mount(); + expect(wrapper.getDOMNode().getAttribute('data-number')).toBe('1'); + expect(wrapper.getDOMNode().getAttribute('aria-label')).toBe('label'); + expect(wrapper.getDOMNode().getAttribute('role')).toBe('button'); + }); + it('id', () => { + const wrapper = mount(); + expect(wrapper.getDOMNode().getAttribute('id')).toBe('myrate'); + }); }); });