in src/plugins/discover/public/application/components/sidebar/discover_field_search.tsx [82:280]
export function DiscoverFieldSearch({ onChange, value, types }: Props) {
const searchPlaceholder = i18n.translate('discover.fieldChooser.searchPlaceHolder', {
defaultMessage: 'Search field names',
});
const aggregatableLabel = i18n.translate('discover.fieldChooser.filter.aggregatableLabel', {
defaultMessage: 'Aggregatable',
});
const searchableLabel = i18n.translate('discover.fieldChooser.filter.searchableLabel', {
defaultMessage: 'Searchable',
});
const typeLabel = i18n.translate('discover.fieldChooser.filter.typeLabel', {
defaultMessage: 'Type',
});
const typeOptions = types
? types.map((type) => {
return { value: type, text: type };
})
: [{ value: 'any', text: 'any' }];
const [activeFiltersCount, setActiveFiltersCount] = useState(0);
const [isPopoverOpen, setPopoverOpen] = useState(false);
const [values, setValues] = useState<State>({
searchable: 'any',
aggregatable: 'any',
type: 'any',
missing: true,
});
if (typeof value !== 'string') {
// at initial rendering value is undefined (angular related), this catches the warning
// should be removed once all is react
return null;
}
const filterBtnAriaLabel = isPopoverOpen
? i18n.translate('discover.fieldChooser.toggleFieldFilterButtonHideAriaLabel', {
defaultMessage: 'Hide field filter settings',
})
: i18n.translate('discover.fieldChooser.toggleFieldFilterButtonShowAriaLabel', {
defaultMessage: 'Show field filter settings',
});
const handleFacetButtonClicked = () => {
setPopoverOpen(!isPopoverOpen);
};
const applyFilterValue = (id: string, filterValue: string | boolean) => {
switch (filterValue) {
case 'any':
if (id !== 'type') {
onChange(id, undefined);
} else {
onChange(id, filterValue);
}
break;
case 'true':
onChange(id, true);
break;
case 'false':
onChange(id, false);
break;
default:
onChange(id, filterValue);
}
};
const isFilterActive = (name: string, filterValue: string | boolean) => {
return name !== 'missing' && filterValue !== 'any';
};
const handleValueChange = (name: string, filterValue: string | boolean) => {
const previousValue = values[name];
updateFilterCount(name, previousValue, filterValue);
const updatedValues = { ...values };
updatedValues[name] = filterValue;
setValues(updatedValues);
applyFilterValue(name, filterValue);
};
const updateFilterCount = (
name: string,
previousValue: string | boolean,
currentValue: string | boolean
) => {
const previouslyFilterActive = isFilterActive(name, previousValue);
const filterActive = isFilterActive(name, currentValue);
const diff = Number(filterActive) - Number(previouslyFilterActive);
setActiveFiltersCount(activeFiltersCount + diff);
};
const handleMissingChange = (e: EuiSwitchEvent) => {
const missingValue = e.target.checked;
handleValueChange('missing', missingValue);
};
const buttonContent = (
<EuiFacetButton
aria-label={filterBtnAriaLabel}
data-test-subj="toggleFieldFilterButton"
className="dscFieldSearch__toggleButton"
icon={<EuiIcon type="filter" />}
isSelected={activeFiltersCount > 0}
quantity={activeFiltersCount}
onClick={handleFacetButtonClicked}
>
<FormattedMessage
id="discover.fieldChooser.fieldFilterFacetButtonLabel"
defaultMessage="Filter by type"
/>
</EuiFacetButton>
);
const select = (
id: string,
selectOptions: Array<{ text: ReactNode } & OptionHTMLAttributes<HTMLOptionElement>>,
selectValue: string
) => {
return (
<EuiSelect
id={`${id}-select`}
options={selectOptions}
value={selectValue}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
handleValueChange(id, e.target.value)
}
aria-label={i18n.translate('discover.fieldChooser.filter.fieldSelectorLabel', {
defaultMessage: 'Selection of {id} filter options',
values: { id },
})}
data-test-subj={`${id}Select`}
compressed
/>
);
};
const toggleButtons = (id: string) => {
return [
{
id: `${id}-any`,
label: 'any',
},
{
id: `${id}-true`,
label: 'yes',
},
{
id: `${id}-false`,
label: 'no',
},
];
};
const buttonGroup = (id: string, legend: string) => {
return (
<EuiButtonGroup
legend={legend}
options={toggleButtons(id)}
idSelected={`${id}-${values[id]}`}
onChange={(optionId) => handleValueChange(id, optionId.replace(`${id}-`, ''))}
buttonSize="compressed"
isFullWidth
data-test-subj={`${id}ButtonGroup`}
/>
);
};
const selectionPanel = (
<div className="dscFieldSearch__formWrapper">
<EuiForm data-test-subj="filterSelectionPanel">
<EuiFormRow fullWidth label={aggregatableLabel} display="columnCompressed">
{buttonGroup('aggregatable', aggregatableLabel)}
</EuiFormRow>
<EuiFormRow fullWidth label={searchableLabel} display="columnCompressed">
{buttonGroup('searchable', searchableLabel)}
</EuiFormRow>
<EuiFormRow fullWidth label={typeLabel} display="columnCompressed">
{select('type', typeOptions, values.type)}
</EuiFormRow>
</EuiForm>
</div>
);
return (
<React.Fragment>
<EuiFlexGroup responsive={false} gutterSize={'s'}>
<EuiFlexItem>
<EuiFieldSearch
aria-label={searchPlaceholder}
data-test-subj="fieldFilterSearchInput"
compressed
fullWidth
onChange={(event) => onChange('name', event.currentTarget.value)}
placeholder={searchPlaceholder}
value={value}
/>
</EuiFlexItem>
</EuiFlexGroup>
<div className="dscFieldSearch__filterWrapper">
<EuiOutsideClickDetector onOutsideClick={() => {}} isDisabled={!isPopoverOpen}>