Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | 4x 43x 43x 43x 43x 33x 6x 33x 43x 19x 43x 11x 43x 3x 3x 43x 1x 43x 43x | import React, { useEffect, useState } from 'react';
import { TextField, InputAdornment, IconButton } from '@mui/material';
import SearchIcon from '@mui/icons-material/Search';
import ClearIcon from '@mui/icons-material/Clear';
import { useTranslation } from 'react-i18next';
import { usePartnerStore } from '@/stores/partnerStore';
/**
* Search input component with 300ms debouncing for partner name search.
* Implements AC1 requirement for debounced search functionality.
*/
export const PartnerSearch: React.FC = () => {
const { t } = useTranslation('partners');
const { searchQuery, setSearchQuery } = usePartnerStore();
const [localValue, setLocalValue] = useState(searchQuery);
// Debounce search query updates (300ms)
useEffect(() => {
const timer = setTimeout(() => {
setSearchQuery(localValue);
}, 300);
return () => clearTimeout(timer);
}, [localValue, setSearchQuery]);
// Sync local value when store value changes externally (e.g., clear filters)
useEffect(() => {
setLocalValue(searchQuery);
}, [searchQuery]);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setLocalValue(event.target.value);
};
const handleClear = () => {
setLocalValue('');
setSearchQuery('');
};
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
Iif (event.key === 'Escape') {
handleClear();
}
};
const showClearButton = localValue.length > 0;
return (
<TextField
fullWidth
value={localValue}
onChange={handleChange}
onKeyDown={handleKeyDown}
placeholder={t('searchPlaceholder')}
data-testid="partner-search-input"
inputProps={{
'aria-label': t('searchAriaLabel'),
}}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon data-testid="search-icon" />
</InputAdornment>
),
endAdornment: showClearButton ? (
<InputAdornment position="end">
<IconButton
aria-label={t('clearSearch')}
onClick={handleClear}
edge="end"
size="small"
data-testid="clear-search"
>
<ClearIcon />
</IconButton>
</InputAdornment>
) : null,
}}
/>
);
};
|