Skip to content
44 changes: 37 additions & 7 deletions src/Rate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ export interface RateProps
onFocus?: () => void;
onBlur?: () => void;
onKeyDown?: React.KeyboardEventHandler<HTMLUListElement>;
onMouseEnter?: React.MouseEventHandler<HTMLUListElement>;
onMouseLeave?: React.MouseEventHandler<HTMLUListElement>;
id?: string;
autoFocus?: boolean;
direction?: string;
}
Expand All @@ -37,6 +40,7 @@ function Rate(props: RateProps, ref: React.Ref<RateRef>) {
prefixCls = 'rc-rate',
className,
style,
id,

// Value
defaultValue,
Expand All @@ -61,8 +65,27 @@ function Rate(props: RateProps, ref: React.Ref<RateRef>) {
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;
},
{},
);
Comment on lines +74 to +87

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


const [getStarRef, setStarRef] = useRefs<HTMLElement>();
const rateRef = React.useRef<HTMLUListElement>(null);

Expand Down Expand Up @@ -135,10 +158,15 @@ function Rate(props: RateProps, ref: React.Ref<RateRef>) {
onHoverChange?.(nextHoverValue);
};

const onMouseLeave = () => {
setHoverValue(null);
setCleanedValue(null);
onHoverChange?.(undefined);
const onMouseLeaveCallback = (event?: React.MouseEvent<HTMLUListElement>) => {
if (!disabled) {
setHoverValue(null);
setCleanedValue(null);
onHoverChange?.(undefined);
}
if (event) {
onMouseLeave?.(event);
}
};

// =========================== Click ============================
Expand All @@ -148,12 +176,11 @@ function Rate(props: RateProps, ref: React.Ref<RateRef>) {
if (allowClear) {
isReset = newValue === value;
}
onMouseLeave();
onMouseLeaveCallback();
changeValue(isReset ? 0 : newValue);
setCleanedValue(isReset ? newValue : null);
};

// ========================== Keyboard ==========================
const onInternalKeyDown: React.KeyboardEventHandler<HTMLUListElement> = (event) => {
const { keyCode } = event;
const reverse = direction === 'rtl';
Expand Down Expand Up @@ -233,13 +260,16 @@ function Rate(props: RateProps, ref: React.Ref<RateRef>) {
[`${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}
</ul>
Expand Down
38 changes: 38 additions & 0 deletions tests/simple.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<Rate onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} disabled />,
);
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(
<Rate onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} />,
);
wrapper.simulate('mouseenter');
expect(handleMouseEnter).toHaveBeenCalled();
wrapper.simulate('mouseleave');
expect(handleMouseLeave).toHaveBeenCalled();
});
});

describe('html attributes', () => {
it('data-* and aria-* and role', () => {
const wrapper = mount(<Rate data-number="1" aria-label="label" role="button" />);
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(<Rate id="myrate" />);
expect(wrapper.getDOMNode().getAttribute('id')).toBe('myrate');
});
});
});