Skip to content

Commit 65e0abf

Browse files
committed
fixup! core: refacto combo box
1 parent b29bc8b commit 65e0abf

File tree

4 files changed

+41
-27
lines changed

4 files changed

+41
-27
lines changed

ui-core/src/components/inputs/ComboBox/ComboBox.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,7 @@ const ComboBox = <T,>({
7676
// behavior
7777
const handleInputChange: ChangeEventHandler<HTMLInputElement> = (e) => {
7878
onChange?.(e);
79-
const newInputValue = e.currentTarget.value;
80-
setInputValue(newInputValue);
79+
setInputValue(e.currentTarget.value);
8180
};
8281

8382
const selectSuggestion = (index: number) => {
@@ -128,6 +127,7 @@ const ComboBox = <T,>({
128127
const clearInput = () => {
129128
setInputValue('');
130129
onSelectSuggestion(undefined);
130+
resetSuggestions();
131131
focusInput();
132132
};
133133

@@ -136,7 +136,7 @@ const ComboBox = <T,>({
136136
const inputIcons = useMemo(
137137
() => [
138138
// Conditionally include the clear icon only when input is not empty
139-
...(inputValue !== ''
139+
...(value
140140
? [
141141
{
142142
icon: <XCircle variant="fill" />,
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState } from 'react';
1+
import { useEffect, useState } from 'react';
22

33
const normalizeString = (str: string) => str.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
44

@@ -12,29 +12,49 @@ const defaultFilterSuggestions = <T>(
1212
return suggestions;
1313
}
1414

15-
return suggestions.filter((suggestion) => {
16-
const suggestionLabel = normalizeString(getSuggestionLabel(suggestion).toLowerCase());
17-
return suggestionLabel.startsWith(input.toLowerCase());
18-
});
15+
const { startingWithInput, containingInput } = suggestions.reduce<{
16+
startingWithInput: T[];
17+
containingInput: T[];
18+
}>(
19+
(acc, suggestion) => {
20+
const suggestionLabel = normalizeString(getSuggestionLabel(suggestion).toLowerCase());
21+
if (suggestionLabel.startsWith(input)) {
22+
acc.startingWithInput.push(suggestion);
23+
return acc;
24+
}
25+
if (suggestionLabel.includes(input)) {
26+
acc.containingInput.push(suggestion);
27+
}
28+
return acc;
29+
},
30+
{
31+
startingWithInput: [],
32+
containingInput: [],
33+
}
34+
);
35+
36+
return [...startingWithInput, ...containingInput];
1937
};
2038

2139
const useDefaultComboBox = <T>(suggestions: T[], getSuggestionLabel: (suggestion: T) => string) => {
2240
const [filteredSuggestions, setFilteredSuggestions] = useState<T[]>(suggestions);
41+
const [query, setQuery] = useState('');
2342

2443
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
25-
const newSuggestions = defaultFilterSuggestions(
26-
getSuggestionLabel,
27-
suggestions,
28-
e.target.value
29-
);
30-
setFilteredSuggestions(newSuggestions);
44+
setQuery(e.target.value);
3145
};
3246

47+
useEffect(() => {
48+
const newSuggestions = defaultFilterSuggestions(getSuggestionLabel, suggestions, query);
49+
setFilteredSuggestions(newSuggestions);
50+
// eslint-disable-next-line react-hooks/exhaustive-deps
51+
}, [suggestions, query]);
52+
3353
const resetSuggestions = () => {
3454
setFilteredSuggestions(suggestions);
3555
};
3656

37-
return { filteredSuggestions, onChange, resetSuggestions };
57+
return { suggestions: filteredSuggestions, onChange, resetSuggestions };
3858
};
3959

4060
export default useDefaultComboBox;

ui-core/src/stories/ComboBox.stories.tsx

+3-9
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,16 @@ const ComboBoxStory = (props: { small?: boolean; disabled?: boolean; readOnly?:
3232
setValue(suggestion);
3333
};
3434

35-
const { filteredSuggestions, onChange, resetSuggestions } = useDefaultComboBox(
36-
suggestions,
37-
getSuggestionLabel
38-
);
35+
const comboBoxDefaultProps = useDefaultComboBox(suggestions, getSuggestionLabel);
3936

4037
return (
4138
<div style={{ maxWidth: '20rem' }}>
4239
<ComboBox
4340
id="combo-box-custom"
44-
getSuggestionLabel={getSuggestionLabel}
4541
value={value}
46-
suggestions={filteredSuggestions}
47-
onChange={onChange}
42+
getSuggestionLabel={getSuggestionLabel}
4843
onSelectSuggestion={onSelectSuggestion}
49-
resetSuggestions={resetSuggestions}
44+
{...comboBoxDefaultProps}
5045
{...props}
5146
/>
5247
</div>
@@ -72,7 +67,6 @@ export const Default: Story = {
7267
args: {
7368
label: 'Your name',
7469
type: 'text',
75-
defaultValue: '',
7670
},
7771
};
7872

ui-core/src/stories/ComboBoxCustom.stories.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import ComboBox from '../components/inputs/ComboBox';
88

99
type Suggestion = { id: string; firstname: string; lastname: string };
1010

11-
const suggestions = [
11+
const suggestions: Suggestion[] = [
1212
{ id: '1', firstname: 'Manuel', lastname: 'Garcia' },
1313
{ id: '2', firstname: 'Consuela', lastname: 'Rodriguez' },
1414
{ id: '3', firstname: 'Juan', lastname: 'Gonzales' },
@@ -21,7 +21,7 @@ const suggestions = [
2121
{ id: '10', firstname: 'Carlos', lastname: 'Martin' },
2222
{ id: '11', firstname: 'Elena', lastname: 'Jimenez' },
2323
{ id: '12', firstname: 'Miguel', lastname: 'Ruiz' },
24-
] as Suggestion[];
24+
];
2525

2626
const ComboBoxStory = () => {
2727
const [value, setValue] = useState<Suggestion>();
@@ -35,7 +35,7 @@ const ComboBoxStory = () => {
3535
};
3636

3737
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
38-
const inputValue = e.target.value;
38+
const inputValue = e.target.value.toLowerCase();
3939
if (!inputValue) {
4040
setFilteredSuggestions(suggestions);
4141
return;

0 commit comments

Comments
 (0)