Skip to content

Commit 96729c2

Browse files
authored
NETOBSERV-1816 allow n/a filter (#590)
* allow n/a filter * override empty value by undefined value * allow empty only when equal or different
1 parent 583289a commit 96729c2

File tree

6 files changed

+47
-11
lines changed

6 files changed

+47
-11
lines changed

web/locales/en/plugin__netobserv-plugin.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@
411411
"Last 1 week": "Last 1 week",
412412
"Last 2 weeks": "Last 2 weeks",
413413
"Value is empty": "Value is empty",
414+
"Value is malformed": "Value is malformed",
414415
"Not a valid Kubernetes name": "Not a valid Kubernetes name",
415416
"Incomplete resource name, either kind, namespace or name is missing.": "Incomplete resource name, either kind, namespace or name is missing.",
416417
"Kind is empty": "Kind is empty",

web/src/components/toolbar/filters-toolbar.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,17 @@ export const FiltersToolbar: React.FC<FiltersToolbarProps> = ({
121121
switch (selectedFilter.component) {
122122
case 'text':
123123
case 'number':
124-
return <TextFilter {...commonProps} regexp={selectedFilter.component === 'number' ? /\D/g : undefined} />;
124+
return (
125+
<TextFilter
126+
{...commonProps}
127+
allowEmpty={selectedCompare !== FilterCompare.moreThanOrEqual}
128+
regexp={selectedFilter.component === 'number' ? /\D/g : undefined}
129+
/>
130+
);
125131
case 'autocomplete':
126132
return <AutocompleteFilter {...commonProps} />;
127133
}
128-
}, [selectedFilter, addFilter, indicator, setIndicator, setMessageWithDelay]);
134+
}, [selectedFilter, addFilter, setMessageWithDelay, indicator, selectedCompare]);
129135

130136
const isForced = !_.isEmpty(forcedFilters);
131137
const filtersOrForced = isForced ? forcedFilters : filters;

web/src/components/toolbar/filters/autocomplete-filter.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import * as React from 'react';
1414
import { createFilterValue, FilterDefinition, FilterOption, FilterValue } from '../../../model/filters';
1515
import { autoCompleteCache } from '../../../utils/autocomplete-cache';
1616
import { getHTTPErrorDetails } from '../../../utils/errors';
17+
import { undefinedValue } from '../../../utils/filter-definitions';
1718
import { Indicator } from '../../../utils/filters-helper';
1819
import { usePrevious } from '../../../utils/previous-hook';
1920
import './autocomplete-filter.css';
@@ -128,13 +129,19 @@ export const AutocompleteFilter: React.FC<AutocompleteFilterProps> = ({
128129
);
129130

130131
const onEnter = React.useCallback(() => {
132+
// override empty value by undefined value
133+
let v = currentValue;
134+
if (currentValue.length === 0) {
135+
v = undefinedValue;
136+
}
137+
131138
// Only one choice is present, consider this is what is desired
132139
if (options.length === 1) {
133140
onAutoCompleteOptionSelected(options[0]);
134141
return;
135142
}
136143

137-
const validation = filterDefinition.validate(currentValue);
144+
const validation = filterDefinition.validate(v);
138145
//show tooltip and icon when user try to validate filter
139146
if (!_.isEmpty(validation.err)) {
140147
setMessageWithDelay(validation.err);

web/src/components/toolbar/filters/text-filter.tsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { SearchIcon } from '@patternfly/react-icons';
33
import * as _ from 'lodash';
44
import * as React from 'react';
55
import { createFilterValue, FilterDefinition, FilterValue } from '../../../model/filters';
6+
import { doubleQuoteValue, undefinedValue } from '../../../utils/filter-definitions';
67
import { Indicator } from '../../../utils/filters-helper';
78

89
export interface TextFilterProps {
@@ -11,6 +12,7 @@ export interface TextFilterProps {
1112
setMessageWithDelay: (m: string | undefined) => void;
1213
indicator: Indicator;
1314
setIndicator: (ind: Indicator) => void;
15+
allowEmpty?: boolean;
1416
regexp?: RegExp;
1517
}
1618

@@ -20,6 +22,7 @@ export const TextFilter: React.FC<TextFilterProps> = ({
2022
setMessageWithDelay,
2123
indicator,
2224
setIndicator,
25+
allowEmpty,
2326
regexp
2427
}) => {
2528
const searchInputRef = React.useRef<HTMLInputElement | null>(null);
@@ -38,7 +41,7 @@ export const TextFilter: React.FC<TextFilterProps> = ({
3841
const updateValue = React.useCallback(
3942
(v: string) => {
4043
let filteredValue = v;
41-
if (regexp) {
44+
if (![doubleQuoteValue, undefinedValue].includes(filteredValue) && regexp) {
4245
filteredValue = filteredValue.replace(regexp, '');
4346
}
4447
setCurrentValue(filteredValue);
@@ -53,7 +56,17 @@ export const TextFilter: React.FC<TextFilterProps> = ({
5356
}, [setCurrentValue, setMessageWithDelay, setIndicator]);
5457

5558
const onSelect = React.useCallback(() => {
56-
const validation = filterDefinition.validate(String(currentValue));
59+
// override empty value by undefined value
60+
let v = currentValue;
61+
if (allowEmpty) {
62+
if (currentValue.length === 0) {
63+
v = undefinedValue;
64+
}
65+
} else if (v === undefinedValue) {
66+
v = '';
67+
}
68+
69+
const validation = filterDefinition.validate(String(v));
5770
//show tooltip and icon when user try to validate filter
5871
if (!_.isEmpty(validation.err)) {
5972
setMessageWithDelay(validation.err);
@@ -66,7 +79,7 @@ export const TextFilter: React.FC<TextFilterProps> = ({
6679
resetFilterValue();
6780
}
6881
});
69-
}, [filterDefinition, currentValue, setMessageWithDelay, setIndicator, addFilter, resetFilterValue]);
82+
}, [currentValue, allowEmpty, filterDefinition, setMessageWithDelay, setIndicator, addFilter, resetFilterValue]);
7083

7184
return (
7285
<>

web/src/model/filters.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import _ from 'lodash';
22
import { isEqual } from '../utils/base-compare';
3+
import { undefinedValue } from '../utils/filter-definitions';
34

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

@@ -94,7 +95,9 @@ export interface FilterOption {
9495
export const createFilterValue = (def: FilterDefinition, value: string): Promise<FilterValue> => {
9596
return def.getOptions(value).then(opts => {
9697
const option = opts.find(opt => opt.name === value || opt.value === value);
97-
return option ? { v: option.value, display: option.name } : { v: value };
98+
return option
99+
? { v: option.value, display: option.name }
100+
: { v: value, display: value === undefinedValue ? 'n/a' : undefined };
98101
});
99102
};
100103

web/src/utils/filter-definitions.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ import { validateK8SName, validateStrictK8SName } from './label';
3838

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

4244
const matcher = (left: string, right: string[], not: boolean, moreThan: boolean) =>
4345
`${left}${not ? '!=' : moreThan ? '>=' : '='}${right.join(',')}`;
@@ -136,15 +138,19 @@ export const getFilterDefinitions = (
136138
if (_.isEmpty(value)) {
137139
return invalid(t('Value is empty'));
138140
}
141+
if (value === doubleQuoteValue) {
142+
return invalid(t('Value is malformed'));
143+
}
139144
return valid(value);
140145
};
141146

142147
const k8sNameValidation = (value: string) => {
143148
if (_.isEmpty(value)) {
144-
// Replace with exact match
145-
return valid('""');
149+
return invalid(t('Value is empty'));
146150
}
147-
return value === '""' || validateK8SName(value) ? valid(value) : invalid(t('Not a valid Kubernetes name'));
151+
return value === undefinedValue || validateK8SName(value)
152+
? valid(value)
153+
: invalid(t('Not a valid Kubernetes name'));
148154
};
149155

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

0 commit comments

Comments
 (0)