Skip to content

Commit

Permalink
NETOBSERV-1816 allow n/a filter (#590)
Browse files Browse the repository at this point in the history
* allow n/a filter

* override empty value by undefined value

* allow empty only when equal or different
  • Loading branch information
jpinsonneau authored Sep 11, 2024
1 parent 583289a commit 96729c2
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 11 deletions.
1 change: 1 addition & 0 deletions web/locales/en/plugin__netobserv-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@
"Last 1 week": "Last 1 week",
"Last 2 weeks": "Last 2 weeks",
"Value is empty": "Value is empty",
"Value is malformed": "Value is malformed",
"Not a valid Kubernetes name": "Not a valid Kubernetes name",
"Incomplete resource name, either kind, namespace or name is missing.": "Incomplete resource name, either kind, namespace or name is missing.",
"Kind is empty": "Kind is empty",
Expand Down
10 changes: 8 additions & 2 deletions web/src/components/toolbar/filters-toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,17 @@ export const FiltersToolbar: React.FC<FiltersToolbarProps> = ({
switch (selectedFilter.component) {
case 'text':
case 'number':
return <TextFilter {...commonProps} regexp={selectedFilter.component === 'number' ? /\D/g : undefined} />;
return (
<TextFilter
{...commonProps}
allowEmpty={selectedCompare !== FilterCompare.moreThanOrEqual}
regexp={selectedFilter.component === 'number' ? /\D/g : undefined}
/>
);
case 'autocomplete':
return <AutocompleteFilter {...commonProps} />;
}
}, [selectedFilter, addFilter, indicator, setIndicator, setMessageWithDelay]);
}, [selectedFilter, addFilter, setMessageWithDelay, indicator, selectedCompare]);

const isForced = !_.isEmpty(forcedFilters);
const filtersOrForced = isForced ? forcedFilters : filters;
Expand Down
9 changes: 8 additions & 1 deletion web/src/components/toolbar/filters/autocomplete-filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import * as React from 'react';
import { createFilterValue, FilterDefinition, FilterOption, FilterValue } from '../../../model/filters';
import { autoCompleteCache } from '../../../utils/autocomplete-cache';
import { getHTTPErrorDetails } from '../../../utils/errors';
import { undefinedValue } from '../../../utils/filter-definitions';
import { Indicator } from '../../../utils/filters-helper';
import { usePrevious } from '../../../utils/previous-hook';
import './autocomplete-filter.css';
Expand Down Expand Up @@ -128,13 +129,19 @@ export const AutocompleteFilter: React.FC<AutocompleteFilterProps> = ({
);

const onEnter = React.useCallback(() => {
// override empty value by undefined value
let v = currentValue;
if (currentValue.length === 0) {
v = undefinedValue;
}

// Only one choice is present, consider this is what is desired
if (options.length === 1) {
onAutoCompleteOptionSelected(options[0]);
return;
}

const validation = filterDefinition.validate(currentValue);
const validation = filterDefinition.validate(v);
//show tooltip and icon when user try to validate filter
if (!_.isEmpty(validation.err)) {
setMessageWithDelay(validation.err);
Expand Down
19 changes: 16 additions & 3 deletions web/src/components/toolbar/filters/text-filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { SearchIcon } from '@patternfly/react-icons';
import * as _ from 'lodash';
import * as React from 'react';
import { createFilterValue, FilterDefinition, FilterValue } from '../../../model/filters';
import { doubleQuoteValue, undefinedValue } from '../../../utils/filter-definitions';
import { Indicator } from '../../../utils/filters-helper';

export interface TextFilterProps {
Expand All @@ -11,6 +12,7 @@ export interface TextFilterProps {
setMessageWithDelay: (m: string | undefined) => void;
indicator: Indicator;
setIndicator: (ind: Indicator) => void;
allowEmpty?: boolean;
regexp?: RegExp;
}

Expand All @@ -20,6 +22,7 @@ export const TextFilter: React.FC<TextFilterProps> = ({
setMessageWithDelay,
indicator,
setIndicator,
allowEmpty,
regexp
}) => {
const searchInputRef = React.useRef<HTMLInputElement | null>(null);
Expand All @@ -38,7 +41,7 @@ export const TextFilter: React.FC<TextFilterProps> = ({
const updateValue = React.useCallback(
(v: string) => {
let filteredValue = v;
if (regexp) {
if (![doubleQuoteValue, undefinedValue].includes(filteredValue) && regexp) {
filteredValue = filteredValue.replace(regexp, '');
}
setCurrentValue(filteredValue);
Expand All @@ -53,7 +56,17 @@ export const TextFilter: React.FC<TextFilterProps> = ({
}, [setCurrentValue, setMessageWithDelay, setIndicator]);

const onSelect = React.useCallback(() => {
const validation = filterDefinition.validate(String(currentValue));
// override empty value by undefined value
let v = currentValue;
if (allowEmpty) {
if (currentValue.length === 0) {
v = undefinedValue;
}
} else if (v === undefinedValue) {
v = '';
}

const validation = filterDefinition.validate(String(v));
//show tooltip and icon when user try to validate filter
if (!_.isEmpty(validation.err)) {
setMessageWithDelay(validation.err);
Expand All @@ -66,7 +79,7 @@ export const TextFilter: React.FC<TextFilterProps> = ({
resetFilterValue();
}
});
}, [filterDefinition, currentValue, setMessageWithDelay, setIndicator, addFilter, resetFilterValue]);
}, [currentValue, allowEmpty, filterDefinition, setMessageWithDelay, setIndicator, addFilter, resetFilterValue]);

return (
<>
Expand Down
5 changes: 4 additions & 1 deletion web/src/model/filters.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import _ from 'lodash';
import { isEqual } from '../utils/base-compare';
import { undefinedValue } from '../utils/filter-definitions';

export type FiltersEncoder = (values: FilterValue[], matchAny: boolean, not: boolean, moreThan: boolean) => string;

Expand Down Expand Up @@ -94,7 +95,9 @@ export interface FilterOption {
export const createFilterValue = (def: FilterDefinition, value: string): Promise<FilterValue> => {
return def.getOptions(value).then(opts => {
const option = opts.find(opt => opt.name === value || opt.value === value);
return option ? { v: option.value, display: option.name } : { v: value };
return option
? { v: option.value, display: option.name }
: { v: value, display: value === undefinedValue ? 'n/a' : undefined };
});
};

Expand Down
14 changes: 10 additions & 4 deletions web/src/utils/filter-definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import { validateK8SName, validateStrictK8SName } from './label';

// Convenience string to filter by undefined field values
export const undefinedValue = '""';
// Unique double are allowed while typing but invalid
export const doubleQuoteValue = '"';

const matcher = (left: string, right: string[], not: boolean, moreThan: boolean) =>
`${left}${not ? '!=' : moreThan ? '>=' : '='}${right.join(',')}`;
Expand Down Expand Up @@ -136,15 +138,19 @@ export const getFilterDefinitions = (
if (_.isEmpty(value)) {
return invalid(t('Value is empty'));
}
if (value === doubleQuoteValue) {
return invalid(t('Value is malformed'));
}
return valid(value);
};

const k8sNameValidation = (value: string) => {
if (_.isEmpty(value)) {
// Replace with exact match
return valid('""');
return invalid(t('Value is empty'));
}
return value === '""' || validateK8SName(value) ? valid(value) : invalid(t('Not a valid Kubernetes name'));
return value === undefinedValue || validateK8SName(value)
? valid(value)
: invalid(t('Not a valid Kubernetes name'));
};

const k8sResourceValidation = (value: string) => {
Expand Down Expand Up @@ -208,7 +214,7 @@ export const getFilterDefinitions = (
if (_.isEmpty(value)) {
return invalid(t('Value is empty'));
}
return /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})/.test(value)
return value == undefinedValue || /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})/.test(value)
? valid(value)
: invalid(t('Not a valid MAC address'));
};
Expand Down

0 comments on commit 96729c2

Please sign in to comment.