Skip to content

Commit ba41908

Browse files
committed
fix: input search field issue
Signed-off-by: Amit Amrutiya <[email protected]>
1 parent dbd003f commit ba41908

File tree

1 file changed

+96
-74
lines changed

1 file changed

+96
-74
lines changed

src/custom/InputSearchField/InputSearchField.tsx

Lines changed: 96 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Autocomplete } from '@mui/material';
2-
import React, { useEffect, useRef, useState } from 'react';
3-
import { Box, Chip, Grid, TextField, Tooltip, Typography } from '../../base';
2+
import React, { useCallback, useEffect, useState } from 'react';
3+
import { Box, Chip, CircularProgress, Grid, TextField, Tooltip, Typography } from '../../base';
44
import { iconLarge, iconSmall } from '../../constants/iconsSizes';
55
import { CloseIcon, OrgIcon } from '../../icons';
66

@@ -10,95 +10,112 @@ interface Option {
1010
}
1111

1212
interface InputFieldSearchProps {
13-
defaultData?: Option[];
13+
data: Option[];
14+
setFilterData: (data: Option[]) => void;
1415
label?: string;
1516
fetchSuggestions: (value: string) => void;
16-
setFilterData: (data: Option[]) => void;
1717
isLoading: boolean;
1818
type: string;
1919
disabled?: boolean;
20+
selectedData: Option[];
21+
searchValue: string;
22+
setSearchValue: (value: string) => void;
2023
}
2124

2225
const InputFieldSearch: React.FC<InputFieldSearchProps> = ({
23-
defaultData = [],
26+
data,
2427
label,
2528
fetchSuggestions,
2629
setFilterData,
2730
isLoading,
2831
type,
29-
disabled
32+
disabled,
33+
selectedData,
34+
searchValue,
35+
setSearchValue
3036
}) => {
31-
const [data, setData] = useState<Option[]>([]);
3237
const [error, setError] = useState('');
33-
const [inputValue, setInputValue] = useState('');
3438
const [open, setOpen] = useState(false);
35-
const [showAllUsers, setShowAllUsers] = useState(false);
36-
const [selectedOption, setSelectedOption] = useState<Option | undefined>(undefined);
37-
const isFirstRender = useRef(true);
39+
const [showAllItems, setShowAllItems] = useState(false);
40+
const [localSelectedData, setLocalSelectedData] = useState<Option[]>(selectedData);
3841

42+
// Sync local state with prop changes
3943
useEffect(() => {
40-
if (!isFirstRender.current) {
41-
setFilterData(data);
42-
} else {
43-
isFirstRender.current = false;
44-
}
45-
}, [data, setFilterData]);
44+
setLocalSelectedData(selectedData);
45+
}, [selectedData]);
4646

47-
const handleDelete = (id: string) => {
48-
setData((prevData) => prevData.filter((item) => item.id !== id));
49-
};
47+
const handleDelete = useCallback(
48+
(id: string) => {
49+
const newData = localSelectedData.filter((item) => item.id !== id);
50+
setLocalSelectedData(newData);
51+
setFilterData(newData);
52+
},
53+
[localSelectedData, setFilterData]
54+
);
5055

51-
const handleAdd = (event: React.SyntheticEvent, value: Option | null) => {
52-
if (!value) return;
56+
const handleAdd = useCallback(
57+
(_event: React.SyntheticEvent, value: Option | null) => {
58+
if (!value) return;
5359

54-
setData((prevData) => {
55-
const isDuplicate = prevData.some((item) => item.id === value.id);
60+
// Check for duplicates
61+
const isDuplicate = localSelectedData.some((item) => item.id === value.id);
5662
if (isDuplicate) {
5763
setError(`${type} already selected`);
58-
return prevData;
64+
return;
5965
}
6066

67+
// Update both local and parent state
68+
const newData = [...localSelectedData, value];
69+
setLocalSelectedData(newData);
70+
setFilterData(newData);
6171
setError('');
62-
return [...prevData, value];
63-
});
64-
setSelectedOption(undefined);
65-
setInputValue('');
66-
};
67-
68-
const handleInputChange = (event: React.SyntheticEvent, value: string) => {
69-
setInputValue(value);
70-
if (value === '') {
72+
setSearchValue('');
7173
setOpen(false);
72-
} else {
73-
const encodedValue = encodeURIComponent(value);
74-
fetchSuggestions(encodedValue);
75-
setError('');
76-
setOpen(true);
77-
}
78-
};
74+
},
75+
[localSelectedData, setFilterData, type, setSearchValue]
76+
);
77+
78+
const handleInputChange = useCallback(
79+
(_event: React.SyntheticEvent, value: string) => {
80+
setSearchValue(value);
81+
if (value === '') {
82+
setOpen(false);
83+
} else {
84+
const encodedValue = encodeURIComponent(value);
85+
fetchSuggestions(encodedValue);
86+
setError('');
87+
setOpen(true);
88+
}
89+
},
90+
[fetchSuggestions, setSearchValue]
91+
);
7992

8093
return (
81-
<>
82-
<Autocomplete<Option, false, true, false>
94+
<Box sx={{ width: '100%' }}>
95+
<Autocomplete
8396
id={`${type}-search-field`}
84-
sx={{ width: 'auto' }}
85-
options={defaultData}
86-
getOptionLabel={(option: Option) => option.name}
97+
style={{ width: '100%' }}
98+
options={data}
99+
getOptionLabel={() => searchValue}
87100
isOptionEqualToValue={(option: Option, value: Option) => option.id === value.id}
88101
noOptionsText={isLoading ? 'Loading...' : `No ${type} found`}
89102
loading={isLoading}
90103
open={open}
104+
onClose={() => setOpen(false)}
91105
disabled={disabled}
92-
value={selectedOption}
93-
inputValue={inputValue}
106+
value={undefined}
107+
inputValue={searchValue}
94108
onChange={handleAdd}
95109
onInputChange={handleInputChange}
96-
ffilterOptions={(options) => options}
110+
ffilterOptions={(x) => x}
97111
disableClearable
98112
includeInputInList
99113
filterSelectedOptions
100114
disableListWrap
101115
clearOnBlur
116+
popupIcon={null}
117+
blurOnSelect
118+
forcePopupIcon={false}
102119
renderInput={(params) => (
103120
<TextField
104121
{...params}
@@ -109,50 +126,55 @@ const InputFieldSearch: React.FC<InputFieldSearchProps> = ({
109126
InputProps={{
110127
...params.InputProps,
111128
endAdornment: (
112-
<React.Fragment>{isLoading ? <div color="inherit" /> : null}</React.Fragment>
129+
<React.Fragment>
130+
{isLoading ? <CircularProgress color="inherit" size={20} /> : null}
131+
</React.Fragment>
113132
)
114133
}}
115134
/>
116135
)}
117136
renderOption={(props, option: Option) => (
118-
<li {...props}>
119-
<Grid container alignItems="center">
120-
<Grid item>
121-
<Box sx={{ color: 'text.secondary', mr: 2 }}>
122-
<OrgIcon {...iconLarge} />
123-
</Box>
137+
<li {...props} key={option.id}>
138+
<Box component="li" sx={{ '& > img': { mr: 2, flexShrink: 0 } }}>
139+
<Grid container alignItems="center">
140+
<Grid item>
141+
<Box sx={{ color: 'text.secondary', mr: 2 }}>
142+
<OrgIcon {...iconLarge} />
143+
</Box>
144+
</Grid>
145+
<Grid item xs>
146+
<Typography variant="body2">{option.name}</Typography>
147+
</Grid>
124148
</Grid>
125-
<Grid item xs>
126-
<Typography variant="body2">{option.name}</Typography>
127-
</Grid>
128-
</Grid>
149+
</Box>
129150
</li>
130151
)}
131152
/>
153+
132154
<Box
133155
sx={{
134156
display: 'flex',
135157
flexWrap: 'wrap',
136158
gap: 0.5,
137-
mt: data?.length > 0 ? '0.5rem' : ''
159+
mt: localSelectedData?.length > 0 ? '0.5rem' : ''
138160
}}
139161
>
140-
{!showAllUsers && data?.length > 0 && (
162+
{!showAllItems && localSelectedData?.length > 0 && (
141163
<Chip
142-
key={data[data.length - 1]?.id}
164+
key={localSelectedData[localSelectedData.length - 1]?.id}
143165
avatar={<OrgIcon {...iconSmall} />}
144-
label={data[data.length - 1]?.name}
166+
label={localSelectedData[localSelectedData.length - 1]?.name}
145167
size="small"
146-
onDelete={() => handleDelete(data[data.length - 1]?.id)}
168+
onDelete={() => handleDelete(localSelectedData[localSelectedData.length - 1]?.id)}
147169
deleteIcon={
148-
<Tooltip title="Remove member">
170+
<Tooltip title={`Remove ${type}`}>
149171
<CloseIcon style={iconSmall} />
150172
</Tooltip>
151173
}
152174
/>
153175
)}
154-
{showAllUsers &&
155-
data?.map((obj) => (
176+
{showAllItems &&
177+
localSelectedData?.map((obj) => (
156178
<Chip
157179
key={obj.id}
158180
avatar={<OrgIcon {...iconSmall} />}
@@ -166,23 +188,23 @@ const InputFieldSearch: React.FC<InputFieldSearchProps> = ({
166188
}
167189
/>
168190
))}
169-
{data?.length > 1 && (
191+
{localSelectedData?.length > 1 && (
170192
<Typography
171-
onClick={() => setShowAllUsers(!showAllUsers)}
193+
onClick={() => setShowAllItems(!showAllItems)}
172194
sx={{
173195
cursor: 'pointer',
174-
color: 'white',
196+
color: 'primary.main',
175197
fontWeight: '600',
176198
'&:hover': {
177-
color: 'black'
199+
color: 'primary.dark'
178200
}
179201
}}
180202
>
181-
{showAllUsers ? '(hide)' : `(+${data?.length - 1})`}
203+
{showAllItems ? '(hide)' : `(+${localSelectedData?.length - 1})`}
182204
</Typography>
183205
)}
184206
</Box>
185-
</>
207+
</Box>
186208
);
187209
};
188210

0 commit comments

Comments
 (0)