Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ dist/
# dumi
.dumi/tmp
.dumi/tmp-production
.node
47 changes: 26 additions & 21 deletions src/utils/commonUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,30 @@ export function hasPrefixSuffix(props: BaseInputProps | InputProps) {
return !!(props.prefix || props.suffix || props.allowClear);
}

// TODO: It's better to use `Proxy` replace the `element.value`. But we still need support IE11.
function cloneEvent<
EventType extends React.SyntheticEvent<any, any>,
Element extends HTMLInputElement | HTMLTextAreaElement,
>(event: EventType, target: Element, value: any): EventType {
// A bug report filed on WebKit's Bugzilla tracker, dating back to 2009, specifically addresses the issue of cloneNode() not copying files of <input type="file"> elements.
// As of the last update, this bug was still marked as "NEW," indicating that it might not have been resolved yet​​.
// https://bugs.webkit.org/show_bug.cgi?id=28123
const currentTarget = target.cloneNode(true) as Element;

// click clear icon
const newEvent = Object.create(event, {
target: { value: currentTarget },
currentTarget: { value: currentTarget },
});

// Fill data
currentTarget.value = value;
currentTarget.selectionStart = target.selectionStart;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This line causes error for Input component when it's type is email.

<Input type="email />

InvalidStateError: Failed to set the 'selectionStart' property on 'HTMLInputElement': The input element's type ('email') does not support selection.

currentTarget.selectionEnd = target.selectionEnd;

return newEvent;
}

export function resolveOnChange<
E extends HTMLInputElement | HTMLTextAreaElement,
>(
Expand Down Expand Up @@ -38,18 +62,8 @@ export function resolveOnChange<
// }}
// />

// A bug report filed on WebKit's Bugzilla tracker, dating back to 2009, specifically addresses the issue of cloneNode() not copying files of <input type="file"> elements.
// As of the last update, this bug was still marked as "NEW," indicating that it might not have been resolved yet​​.
// https://bugs.webkit.org/show_bug.cgi?id=28123
const currentTarget = target.cloneNode(true) as E;

// click clear icon
event = Object.create(e, {
target: { value: currentTarget },
currentTarget: { value: currentTarget },
});
event = cloneEvent(e, target, '');

currentTarget.value = '';
onChange(event as React.ChangeEvent<E>);
return;
}
Expand All @@ -58,16 +72,7 @@ export function resolveOnChange<
// https://github.com/ant-design/ant-design/issues/45737
// https://github.com/ant-design/ant-design/issues/46598
if (target.type !== 'file' && targetValue !== undefined) {
// A bug report filed on WebKit's Bugzilla tracker, dating back to 2009, specifically addresses the issue of cloneNode() not copying files of <input type="file"> elements.
// As of the last update, this bug was still marked as "NEW," indicating that it might not have been resolved yet​​.
// https://bugs.webkit.org/show_bug.cgi?id=28123
const currentTarget = target.cloneNode(true) as E;

event = Object.create(e, {
target: { value: currentTarget },
currentTarget: { value: currentTarget },
});
currentTarget.value = targetValue;
event = cloneEvent(e, target, targetValue);
onChange(event as React.ChangeEvent<E>);
return;
}
Expand Down
13 changes: 13 additions & 0 deletions tests/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,19 @@ describe('Input ref', () => {
inputSpy.mockRestore();
});

it('selectionXXX should pass', () => {
const onChange = jest.fn();
const { container } = render(<Input onChange={onChange} />);

const inputEl = container.querySelector('input')!;
fireEvent.change(inputEl, { target: { value: 'test' } });

expect(onChange).toHaveBeenCalled();
const event = onChange.mock.calls[0][0];
expect(event.target.selectionStart).toBe(4);
expect(event.target.selectionEnd).toBe(4);
});

it('input should work', () => {
const ref = React.createRef<InputRef>();
const { container } = render(<Input ref={ref} defaultValue="light" />);
Expand Down