Skip to content

Commit d447fd3

Browse files
fix: holding left click breaks mask (#959)
* fix: holding left click breaks mask * fix: align practice * fix: add test * fix: disable click * fix: lint * fix: update * fix: update * fix: codecov * fix: update * fix: update * fix: test
1 parent 3afa103 commit d447fd3

File tree

2 files changed

+68
-3
lines changed

2 files changed

+68
-3
lines changed

src/PickerInput/Selector/Input.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ const Input = React.forwardRef<InputRef, InputProps>((props, ref) => {
9797
// ========================= Refs =========================
9898
const holderRef = React.useRef<HTMLDivElement>(null);
9999
const inputRef = React.useRef<HTMLInputElement>(null);
100+
// When mousedown get focus, defer selection to mouseUp so click position is used
101+
const mouseDownRef = React.useRef(false);
100102

101103
React.useImperativeHandle(ref, () => ({
102104
nativeElement: holderRef.current,
@@ -153,6 +155,12 @@ const Input = React.forwardRef<InputRef, InputProps>((props, ref) => {
153155
};
154156

155157
const onFormatPaste: React.ClipboardEventHandler<HTMLInputElement> = (event) => {
158+
// Block paste until selection is set (after mouseUp when focus was by mousedown)
159+
if (mouseDownRef.current) {
160+
event.preventDefault();
161+
return;
162+
}
163+
156164
// Get paste text
157165
const pasteText = event.clipboardData.getData('text');
158166

@@ -164,8 +172,6 @@ const Input = React.forwardRef<InputRef, InputProps>((props, ref) => {
164172
// ======================== Mouse =========================
165173
// When `mouseDown` get focus, it's better to not to change the selection
166174
// Since the up position maybe not is the first cell
167-
const mouseDownRef = React.useRef(false);
168-
169175
const onFormatMouseDown: React.MouseEventHandler<HTMLInputElement> = () => {
170176
mouseDownRef.current = true;
171177
};
@@ -221,6 +227,12 @@ const Input = React.forwardRef<InputRef, InputProps>((props, ref) => {
221227
};
222228

223229
const onFormatKeyDown: React.KeyboardEventHandler<HTMLInputElement> = (event) => {
230+
// Block key input until selection is set (after mouseUp when focus was by mousedown)
231+
if (mouseDownRef.current) {
232+
event.preventDefault();
233+
return;
234+
}
235+
224236
onSharedKeyDown(event);
225237

226238
const { key } = event;

tests/new-range.spec.tsx

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// In theory, all RangePicker test cases should be paired with SinglePicker
2-
import { act, fireEvent, render } from '@testing-library/react';
2+
import { act, createEvent, fireEvent, render } from '@testing-library/react';
33
import dayjs, { type Dayjs } from 'dayjs';
44
import 'dayjs/locale/ar';
55
import { spyElementPrototype } from '@rc-component/util/lib/test/domHook';
@@ -995,6 +995,27 @@ describe('NewPicker.Range', () => {
995995
expect(onChange).toHaveBeenCalledWith(expect.anything(), ['20200903', '20200905']);
996996
});
997997

998+
it('blocks paste while mouse is down', () => {
999+
const { container } = render(<Demo />);
1000+
1001+
const startInput = container.querySelectorAll<HTMLInputElement>('input')[0];
1002+
1003+
// Simulate focus gained by mousedown, then paste before mouse up.
1004+
fireEvent.mouseDown(startInput);
1005+
fireEvent.focus(startInput);
1006+
1007+
const pasteEvent = createEvent.paste(startInput, {
1008+
clipboardData: {
1009+
getData: () => '20200903',
1010+
},
1011+
});
1012+
pasteEvent.preventDefault = jest.fn();
1013+
fireEvent(startInput, pasteEvent);
1014+
1015+
// Guard in Input should prevent default while mouse is down
1016+
expect(pasteEvent.preventDefault).toHaveBeenCalled();
1017+
});
1018+
9981019
it('click to change selection cell', () => {
9991020
const { container } = render(<Demo />);
10001021

@@ -1010,6 +1031,38 @@ describe('NewPicker.Range', () => {
10101031
expect(startInput.selectionEnd).toEqual(6);
10111032
});
10121033

1034+
it('blocks key input while mouse is down', () => {
1035+
const { container } = render(<Demo />);
1036+
1037+
const startInput = container.querySelectorAll<HTMLInputElement>('input')[0];
1038+
1039+
// Simulate focus gained by mousedown, then key input before mouse up.
1040+
fireEvent.mouseDown(startInput);
1041+
fireEvent.focus(startInput);
1042+
1043+
const keyDownEvent = createEvent.keyDown(startInput, {
1044+
key: '1',
1045+
});
1046+
keyDownEvent.preventDefault = jest.fn();
1047+
fireEvent(startInput, keyDownEvent);
1048+
1049+
// Guard in Input should prevent default while mouse is down
1050+
expect(keyDownEvent.preventDefault).toHaveBeenCalled();
1051+
});
1052+
1053+
it('focus by mousedown defers selection sync to mouseUp', () => {
1054+
const { container } = render(<Demo />);
1055+
1056+
const startInput = container.querySelectorAll<HTMLInputElement>('input')[0];
1057+
1058+
fireEvent.mouseDown(startInput);
1059+
fireEvent.focus(startInput);
1060+
1061+
fireEvent.mouseUp(startInput);
1062+
expect(startInput.selectionStart).toBeDefined();
1063+
expect(startInput.selectionEnd).toBeDefined();
1064+
});
1065+
10131066
it('blur to reset back text', async () => {
10141067
const { container } = render(<Demo />);
10151068

0 commit comments

Comments
 (0)