Skip to content

Commit a41b48d

Browse files
authored
fix(combobox): keep local inputValue state in sync with Downshift (#628)
1 parent 6543d65 commit a41b48d

File tree

3 files changed

+220
-2
lines changed

3 files changed

+220
-2
lines changed

package-lock.json

Lines changed: 190 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/combobox/src/ComboboxContainer.spec.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -894,21 +894,40 @@ describe('ComboboxContainer', () => {
894894
});
895895

896896
it('handles controlled selection as expected', () => {
897+
const _options = [{ value: 'test-1' }, { value: 'test-2' }, { value: 'test-3' }];
898+
897899
expect(listboxOptions[0]).toHaveAttribute('aria-selected', 'false');
898900

899901
rerender(
900902
<TestCombobox
901903
layout={layout}
902-
options={[{ value: 'test-1' }, { value: 'test-2' }, { value: 'test-3' }]}
904+
options={_options}
903905
isExpanded={false}
904-
inputValue=""
906+
inputValue="test-1"
905907
activeIndex={-1}
906908
selectionValue="test-1"
907909
onChange={handleChange}
908910
/>
909911
);
910912

911913
expect(listboxOptions[0]).toHaveAttribute('aria-selected', 'true');
914+
expect(input).toHaveValue('test-1');
915+
916+
// simulate controlled selection change
917+
rerender(
918+
<TestCombobox
919+
layout={layout}
920+
options={_options}
921+
isExpanded={false}
922+
inputValue="test-2"
923+
activeIndex={-1}
924+
selectionValue="test-2"
925+
onChange={handleChange}
926+
/>
927+
);
928+
929+
expect(listboxOptions[1]).toHaveAttribute('aria-selected', 'true');
930+
expect(input).toHaveValue('test-2');
912931
});
913932

914933
it('handles controlled multiple selection as expected', () => {

packages/combobox/src/useCombobox.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export const useCombobox = <
6969
const [triggerContainsInput, setTriggerContainsInput] = useState<boolean>();
7070
const [downshiftInputValue, setDownshiftInputValue] = useState(inputValue);
7171
const [matchValue, setMatchValue] = useState('');
72+
const useInputValueRef = useRef(true);
7273
const matchTimeoutRef = useRef<number>();
7374
const previousStateRef = useRef<IPreviousState>();
7475
const prefix = useId(idPrefix);
@@ -142,6 +143,13 @@ export const useCombobox = <
142143
return defaultActiveIndex;
143144
}, [defaultActiveIndex, isAutocomplete, isEditable]);
144145

146+
if (useInputValueRef.current && inputValue !== downshiftInputValue) {
147+
// Update local state with Downshift `inputValue` for non-buggy cases.
148+
setDownshiftInputValue(inputValue);
149+
} else {
150+
useInputValueRef.current = true;
151+
}
152+
145153
/*
146154
* Validation
147155
*/
@@ -645,6 +653,7 @@ export const useCombobox = <
645653
// Override needed to workaround Downshift cursor bug.
646654
// https://github.com/downshift-js/downshift/issues/1108
647655
setDownshiftInputValue(event.target.value);
656+
useInputValueRef.current = false;
648657

649658
// Override needed to workaround Downshift IME bug.
650659
// https://github.com/downshift-js/downshift/issues/1452

0 commit comments

Comments
 (0)