Skip to content

Commit

Permalink
fix(combobox): workaround Downshift cursor bug (#625)
Browse files Browse the repository at this point in the history
  • Loading branch information
jzempel committed Feb 8, 2024
1 parent b030655 commit 8e9c494
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 11 deletions.
17 changes: 15 additions & 2 deletions packages/combobox/src/ComboboxContainer.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -830,7 +830,7 @@ describe('ComboboxContainer', () => {

describe('controlled', () => {
const handleChange = jest.fn();
let input: HTMLElement;
let input: HTMLInputElement;
let listboxOptions: HTMLElement[];
let rerender: RenderResult['rerender'];

Expand All @@ -853,7 +853,7 @@ describe('ComboboxContainer', () => {
/>
);

input = getByTestId('input');
input = getByTestId('input') as HTMLInputElement;
listboxOptions = getAllByRole('option');
rerender = _rerender;
});
Expand All @@ -869,6 +869,19 @@ describe('ComboboxContainer', () => {
expect(changeTypes).toMatchObject(['input:click', 'input:keyDown:ArrowDown']);
});

it('retains cursor position on input change', async () => {
await user.type(input, 'tet');
await user.keyboard('{ArrowLeft}');

expect(input.selectionStart).toBe(2);

await user.keyboard('s');

expect(input).toHaveValue('test');
expect(input.selectionStart).toBe(3);
expect(handleChange).toHaveBeenCalledTimes(5);
});

it('handles IME input as expected', () => {
fireEvent.change(input, { target: { value: '´' }, nativeEvent: { isComposing: true } });
fireEvent.change(input, { target: { value: 'á' }, nativeEvent: { isComposing: true } });
Expand Down
25 changes: 16 additions & 9 deletions packages/combobox/src/useCombobox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export const useCombobox = <
}

const [triggerContainsInput, setTriggerContainsInput] = useState<boolean>();
const [downshiftInputValue, setDownshiftInputValue] = useState(inputValue);
const [matchValue, setMatchValue] = useState('');
const matchTimeoutRef = useRef<number>();
const previousStateRef = useRef<IPreviousState>();
Expand Down Expand Up @@ -310,7 +311,7 @@ export const useCombobox = <
menuId: idRef.current.listbox,
getItemId: idRef.current.getOptionId,
items: values,
inputValue,
inputValue: downshiftInputValue,
initialInputValue,
itemToString: transformValue as any /* HACK around Downshift's generic type overuse */,
selectedItem: selectionValue,
Expand Down Expand Up @@ -640,14 +641,20 @@ export const useCombobox = <

if (isEditable) {
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
// Override needed to workaround Downshift IME bug.
// https://github.com/downshift-js/downshift/issues/1452
if (inputValue !== undefined && (event.nativeEvent as InputEvent).isComposing) {
/* istanbul ignore next */
handleDownshiftStateChange({
type: useDownshift.stateChangeTypes.InputChange,
inputValue: event.target.value
});
if (inputValue !== undefined) {
// Override needed to workaround Downshift cursor bug.
// https://github.com/downshift-js/downshift/issues/1108
setDownshiftInputValue(event.target.value);

// Override needed to workaround Downshift IME bug.
// https://github.com/downshift-js/downshift/issues/1452
if ((event.nativeEvent as InputEvent).isComposing) {
/* istanbul ignore next */
handleDownshiftStateChange({
type: useDownshift.stateChangeTypes.InputChange,
inputValue: event.target.value
});
}
}
};

Expand Down

0 comments on commit 8e9c494

Please sign in to comment.