diff --git a/frontend/src/components/SearchableValueSelect.tsx b/frontend/src/components/SearchableValueSelect.tsx index 95df7cc..858f40a 100644 --- a/frontend/src/components/SearchableValueSelect.tsx +++ b/frontend/src/components/SearchableValueSelect.tsx @@ -16,6 +16,11 @@ interface SearchableValueSelectProps { allowEmpty?: boolean /** Label for the empty option and closed-state display when value is empty. */ emptyLabel?: string + /** + * Commit every keystroke via onChange (list is suggestions only). + * Used by domain filter: type to filter the current page without requiring a pick. + */ + commitOnType?: boolean loading?: boolean disabled?: boolean className?: string @@ -33,6 +38,7 @@ export function SearchableValueSelect({ placeholder, allowEmpty = false, emptyLabel, + commitOnType = false, loading = false, disabled = false, className, @@ -46,9 +52,9 @@ export function SearchableValueSelect({ const id = useId() const emptyText = emptyLabel ?? t('select.any') const choices = values.filter(item => item.toLowerCase().includes(filter.toLowerCase())) - // active index accounts for optional empty row at top const optionCount = (allowEmpty ? 1 : 0) + choices.length const closedDisplay = value || (allowEmpty ? emptyText : '') + const inputValue = open ? filter : closedDisplay useEffect(() => { const close = (event: MouseEvent) => { @@ -60,7 +66,7 @@ export function SearchableValueSelect({ function choose(next: string) { onChange(next) - setFilter('') + setFilter(commitOnType ? next : '') setActive(0) setOpen(false) } @@ -81,6 +87,7 @@ export function SearchableValueSelect({ else { const item = choices[allowEmpty ? active - 1 : active] if (item) choose(item) + else if (commitOnType) setOpen(false) } } } @@ -98,11 +105,20 @@ export function SearchableValueSelect({ aria-expanded={open} aria-controls={`${id}-options`} disabled={disabled} - value={open ? filter : closedDisplay} + value={inputValue} placeholder={placeholder ?? t('select.placeholder')} className={cn('h-9', triggerClassName)} - onFocus={() => { setFilter(''); setOpen(true) }} - onChange={event => { setFilter(event.target.value); setActive(0); setOpen(true) }} + onFocus={() => { + setFilter(commitOnType ? value : '') + setOpen(true) + }} + onChange={event => { + const next = event.target.value + setFilter(next) + setActive(0) + setOpen(true) + if (commitOnType) onChange(next) + }} onKeyDown={onKeyDown} /> {open && ( @@ -112,7 +128,8 @@ export function SearchableValueSelect({ type="button" role="option" aria-selected={!value} - className={cn('block w-full rounded px-2 py-1.5 text-left text-sm hover:bg-muted', active === 0 && 'bg-primary/10 text-primary')} + className={cn('block w-full truncate rounded px-2 py-1.5 text-left text-sm hover:bg-muted', active === 0 && 'bg-primary/10 text-primary')} + title={emptyText} onMouseDown={event => event.preventDefault()} onClick={() => choose('')} > @@ -132,8 +149,9 @@ export function SearchableValueSelect({ type="button" role="option" aria-selected={item === value} + title={item} className={cn( - 'block w-full rounded px-2 py-1.5 text-left text-sm', + 'block w-full truncate rounded px-2 py-1.5 text-left text-sm', optionIndex === active ? 'bg-primary/10 text-primary' : 'hover:bg-muted', )} onMouseDown={event => event.preventDefault()} diff --git a/frontend/src/pages/collectors/components/CollectorFormDialog.tsx b/frontend/src/pages/collectors/components/CollectorFormDialog.tsx index ae710bc..3342b93 100644 --- a/frontend/src/pages/collectors/components/CollectorFormDialog.tsx +++ b/frontend/src/pages/collectors/components/CollectorFormDialog.tsx @@ -22,6 +22,7 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select' +import { SearchableValueSelect } from '@/components/SearchableValueSelect' import { Form, FormControl, @@ -367,15 +368,19 @@ export const CollectorFormDialog = memo(function CollectorFormDialog({ {t('form.namespace')}{isSidecarType ? ' *' : ''} {isSidecarType && !manualNamespace ? ( - + { + field.onChange(value) + form.setValue('targetDeployment', '') + }} + loading={namespacesLoading} + disabled={!clusterID || namespacesLoading} + placeholder={!clusterID ? t('form.nsPickCluster') : namespacesLoading ? t('form.nsLoading') : t('form.nsPick')} + /> ) : ( { field.onChange(event) @@ -405,14 +410,16 @@ export const CollectorFormDialog = memo(function CollectorFormDialog({ {manualDeployment ? ( ) : ( - + item.name)} + value={field.value} + onChange={field.onChange} + loading={workloadsLoading} + disabled={!namespace || workloadsLoading} + placeholder={!namespace ? t('form.wlPickNamespace') : workloadsLoading ? t('form.wlLoading') : t('form.wlPick')} + /> )} diff --git a/frontend/src/pages/logs/components/LogResultsPanel.tsx b/frontend/src/pages/logs/components/LogResultsPanel.tsx index 8306f81..bd45ea1 100644 --- a/frontend/src/pages/logs/components/LogResultsPanel.tsx +++ b/frontend/src/pages/logs/components/LogResultsPanel.tsx @@ -59,6 +59,7 @@ export function LogResultsPanel({ entries, searched, hasSource, pageIndex, hasNe onChange={setDomainFilter} allowEmpty emptyLabel={t('results.domainAll')} + commitOnType placeholder={t('results.domainPlaceholder')} className="min-w-60" triggerClassName="h-8"