Skip to content

Commit dd99fb1

Browse files
fix: skip changes for unchanged switch values (#183)
1 parent ac3c285 commit dd99fb1

2 files changed

Lines changed: 38 additions & 1 deletion

File tree

‎src/index.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ const Switch = React.forwardRef<HTMLButtonElement, SwitchProps>(
6161
) {
6262
let mergedChecked = innerChecked;
6363

64-
if (!disabled) {
64+
if (!disabled && newChecked !== innerChecked) {
6565
mergedChecked = newChecked;
6666
setInnerChecked(mergedChecked);
6767
onChange?.(mergedChecked, event);

‎tests/index.spec.js‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,43 @@ describe('rc-switch', () => {
3838
expect(switchNode.getAttribute('aria-checked')).toBe('false');
3939
});
4040

41+
it.each([false, true])('does not emit a change for an arrow that retains %s', (checked) => {
42+
const onChange = jest.fn();
43+
const onKeyDown = jest.fn();
44+
const onClick = jest.fn();
45+
const { getByRole } = createSwitch({ defaultChecked: checked, onChange, onKeyDown, onClick });
46+
const switchNode = getByRole('switch');
47+
48+
keyDownWithWhich(switchNode, checked ? KeyCode.RIGHT : KeyCode.LEFT);
49+
expect(onChange).not.toHaveBeenCalled();
50+
expect(onKeyDown).toHaveBeenCalledTimes(1);
51+
expect(switchNode).toHaveAttribute('aria-checked', String(checked));
52+
53+
keyDownWithWhich(switchNode, checked ? KeyCode.LEFT : KeyCode.RIGHT);
54+
expect(onChange).toHaveBeenCalledTimes(1);
55+
expect(onChange).toHaveBeenLastCalledWith(
56+
!checked,
57+
expect.objectContaining({ type: 'keydown' }),
58+
);
59+
fireEvent.click(switchNode);
60+
expect(onChange).toHaveBeenCalledTimes(2);
61+
expect(onClick).toHaveBeenCalledWith(checked, expect.objectContaining({ type: 'click' }));
62+
});
63+
64+
it('retains controlled change requests while skipping an unchanged checked value', () => {
65+
const onChange = jest.fn();
66+
const { getByRole, rerender } = createSwitch({ checked: false, onChange });
67+
const switchNode = getByRole('switch');
68+
69+
keyDownWithWhich(switchNode, KeyCode.RIGHT);
70+
expect(onChange).toHaveBeenCalledWith(true, expect.objectContaining({ type: 'keydown' }));
71+
expect(switchNode).toHaveAttribute('aria-checked', 'false');
72+
rerender(<Switch checked onChange={onChange} />);
73+
onChange.mockClear();
74+
keyDownWithWhich(switchNode, KeyCode.RIGHT);
75+
expect(onChange).not.toHaveBeenCalled();
76+
});
77+
4178
it('should change from an initial checked state of true to false on click', () => {
4279
const onChange = jest.fn();
4380
const { getByRole } = createSwitch({ defaultChecked: true, onChange });

0 commit comments

Comments
 (0)