Skip to content

Commit df3ec9d

Browse files
authored
Panels & columns popup filters (#424)
1 parent b4a80ab commit df3ec9d

File tree

7 files changed

+254
-60
lines changed

7 files changed

+254
-60
lines changed

web/locales/en/plugin__netobserv-plugin.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@
188188
"Manage columns": "Manage columns",
189189
"Selected columns will appear in the table.": "Selected columns will appear in the table.",
190190
"Click and drag the items to reorder the columns in the table.": "Click and drag the items to reorder the columns in the table.",
191+
"Clear filters": "Clear filters",
191192
"Unselect all": "Unselect all",
192193
"Select all": "Select all",
193194
"Restore default columns": "Restore default columns",

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,31 @@ div#filter-toolbar-search-filters {
189189
#clear-all-filters-button {
190190
padding: 0;
191191
}
192+
193+
.custom-chip.buttonless>p {
194+
margin-right: 1rem;
195+
}
196+
197+
.custom-chip.selected,
198+
.custom-chip.selected>button,
199+
.custom-chip.selected>*,
200+
.pf-theme-dark .custom-chip.selected,
201+
.pf-theme-dark .custom-chip.selected>button,
202+
.pf-theme-dark .custom-chip.selected>* {
203+
color: #fff;
204+
background-color: #06c;
205+
}
206+
207+
.custom-chip.unselected,
208+
.custom-chip.unselected>button,
209+
.custom-chip.unselected>* {
210+
color: #000;
211+
background-color: #fff;
212+
}
213+
214+
.pf-theme-dark .custom-chip.unselected,
215+
.pf-theme-dark .custom-chip.unselected>button,
216+
.pf-theme-dark .custom-chip.unselected>* {
217+
color: #000;
218+
background-color: #D2D2D2;
219+
}

web/src/components/modals/columns-modal.css

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,20 @@ label {
2424
margin-top: auto;
2525
margin-bottom: auto;
2626
padding: inherit;
27+
}
28+
29+
.popup-header-margin {
30+
margin-top: 1rem;
31+
}
32+
33+
.flex-center {
34+
align-self: center;
35+
}
36+
37+
.flex-gap {
38+
gap: 0.5rem;
39+
}
40+
41+
.custom-chip.pointer {
42+
cursor: pointer;
2743
}

web/src/components/modals/columns-modal.tsx

Lines changed: 104 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import {
1111
DragDrop,
1212
Draggable,
1313
Droppable,
14+
Flex,
15+
FlexItem,
1416
Text,
1517
TextContent,
1618
TextVariants,
@@ -24,6 +26,8 @@ import { Column, ColumnSizeMap, getDefaultColumns, getFullColumnName } from '../
2426
import './columns-modal.css';
2527
import Modal from './modal';
2628

29+
export const COLUMN_FILTER_KEYS = ['source', 'destination', 'time', 'host', 'namespace', 'owner', 'ip', 'dns'];
30+
2731
export const ColumnsModal: React.FC<{
2832
isModalOpen: boolean;
2933
setModalOpen: (v: boolean) => void;
@@ -35,29 +39,22 @@ export const ColumnsModal: React.FC<{
3539
}> = ({ id, config, isModalOpen, setModalOpen, columns, setColumns, setColumnSizes }) => {
3640
const [resetClicked, setResetClicked] = React.useState<boolean>(false);
3741
const [updatedColumns, setUpdatedColumns] = React.useState<Column[]>([]);
38-
const [isSaveDisabled, setSaveDisabled] = React.useState<boolean>(true);
39-
const [isAllSelected, setAllSelected] = React.useState<boolean>(false);
42+
const [filterKeys, setFilterKeys] = React.useState<string[]>([]);
4043
const { t } = useTranslation('plugin__netobserv-plugin');
4144

45+
React.useEffect(() => {
46+
if (isModalOpen) {
47+
setFilterKeys([]);
48+
}
49+
}, [isModalOpen]);
50+
4251
React.useEffect(() => {
4352
if (!isModalOpen || _.isEmpty(updatedColumns)) {
4453
setUpdatedColumns(_.cloneDeep(columns));
4554
}
4655
// eslint-disable-next-line react-hooks/exhaustive-deps
4756
}, [columns, isModalOpen]);
4857

49-
React.useEffect(() => {
50-
let allSelected = true;
51-
_.forEach(updatedColumns, (col: Column) => {
52-
if (!col.isSelected) {
53-
allSelected = false;
54-
return false;
55-
}
56-
});
57-
setAllSelected(allSelected);
58-
setSaveDisabled(_.isEmpty(updatedColumns.filter(col => col.isSelected)));
59-
}, [updatedColumns]);
60-
6158
const onDrop = React.useCallback(
6259
(source, dest) => {
6360
if (dest) {
@@ -91,13 +88,49 @@ export const ColumnsModal: React.FC<{
9188
setUpdatedColumns(getDefaultColumns(config.columns).filter(c => columns.some(existing => existing.id === c.id)));
9289
}, [columns, config.columns]);
9390

91+
const isSaveDisabled = React.useCallback(() => {
92+
return _.isEmpty(updatedColumns.filter(c => c.isSelected));
93+
}, [updatedColumns]);
94+
95+
const isFilteredColumn = React.useCallback((c: Column, fks: string[]) => {
96+
return (
97+
_.isEmpty(fks) ||
98+
_.reduce(
99+
fks,
100+
(acc, fk) =>
101+
(acc =
102+
acc &&
103+
(c.id.toLowerCase().includes(fk) ||
104+
c.name.toLowerCase().includes(fk) ||
105+
c.group?.toLowerCase().includes(fk) ||
106+
false)),
107+
true
108+
)
109+
);
110+
}, []);
111+
112+
const getColumnFilterKeys = React.useCallback(() => {
113+
return COLUMN_FILTER_KEYS.filter(fk => columns.some(c => isFilteredColumn(c, [fk])));
114+
}, [columns, isFilteredColumn]);
115+
116+
const filteredColumns = React.useCallback(() => {
117+
return updatedColumns.filter(c => isFilteredColumn(c, filterKeys));
118+
}, [filterKeys, isFilteredColumn, updatedColumns]);
119+
120+
const isAllSelected = React.useCallback(() => {
121+
return _.reduce(filteredColumns(), (acc, c) => (acc = acc && c.isSelected), true);
122+
}, [filteredColumns]);
123+
94124
const onSelectAll = React.useCallback(() => {
125+
const allSelected = isAllSelected();
95126
const result = [...updatedColumns];
96-
_.forEach(result, (col: Column) => {
97-
col.isSelected = !isAllSelected;
127+
_.forEach(result, (c: Column) => {
128+
if (isFilteredColumn(c, filterKeys)) {
129+
c.isSelected = !allSelected;
130+
}
98131
});
99132
setUpdatedColumns(result);
100-
}, [updatedColumns, setUpdatedColumns, isAllSelected]);
133+
}, [isAllSelected, updatedColumns, isFilteredColumn, filterKeys]);
101134

102135
const onClose = React.useCallback(() => {
103136
setResetClicked(false);
@@ -113,7 +146,18 @@ export const ColumnsModal: React.FC<{
113146
onClose();
114147
}, [resetClicked, setColumns, updatedColumns, onClose, setColumnSizes]);
115148

116-
const draggableItems = updatedColumns.map((column, i) => (
149+
const toggleChip = React.useCallback(
150+
(key: string) => {
151+
if (filterKeys.includes(key)) {
152+
setFilterKeys(filterKeys.filter(k => k !== key));
153+
} else {
154+
setFilterKeys(COLUMN_FILTER_KEYS.filter(f => f === key || filterKeys.includes(f)));
155+
}
156+
},
157+
[filterKeys]
158+
);
159+
160+
const draggableItems = filteredColumns().map((column, i) => (
117161
<Draggable key={i} hasNoWrapper>
118162
<DataListItem
119163
key={'data-list-item-' + i}
@@ -153,15 +197,46 @@ export const ColumnsModal: React.FC<{
153197
scrollable={true}
154198
onClose={onClose}
155199
description={
156-
<TextContent>
157-
<Text component={TextVariants.p}>
158-
{t('Selected columns will appear in the table.')}&nbsp;
159-
{t('Click and drag the items to reorder the columns in the table.')}
160-
</Text>
161-
<Button isInline onClick={onSelectAll} variant="link">
162-
{isAllSelected ? t('Unselect all') : t('Select all')}
163-
</Button>
164-
</TextContent>
200+
<>
201+
<TextContent>
202+
<Text component={TextVariants.p}>
203+
{t('Selected columns will appear in the table.')}&nbsp;
204+
{t('Click and drag the items to reorder the columns in the table.')}
205+
</Text>
206+
</TextContent>
207+
<Flex className="popup-header-margin">
208+
<FlexItem flex={{ default: 'flex_4' }}>
209+
<Flex className="flex-gap">
210+
{getColumnFilterKeys().map(key => {
211+
return (
212+
<FlexItem
213+
key={key}
214+
onClick={() => toggleChip(key)}
215+
className={`custom-chip ${
216+
filterKeys.includes(key) ? 'selected' : 'unselected'
217+
} buttonless gap pointer`}
218+
>
219+
<Text component={TextVariants.p}>{key}</Text>
220+
</FlexItem>
221+
);
222+
})}
223+
</Flex>
224+
</FlexItem>
225+
<FlexItem flex={{ default: 'flex_1' }} className="flex-center">
226+
{_.isEmpty(filteredColumns()) ? (
227+
<Button isInline onClick={() => setFilterKeys([])} variant="link">
228+
{t('Clear filters')}
229+
</Button>
230+
) : (
231+
<Button isInline onClick={onSelectAll} variant="link">
232+
{`${isAllSelected() ? t('Unselect all') : t('Select all')}${
233+
!_.isEmpty(filterKeys) ? ' ' + filterKeys.join(',') : ''
234+
}`}
235+
</Button>
236+
)}
237+
</FlexItem>
238+
</Flex>
239+
</>
165240
}
166241
footer={
167242
<div className="footer">
@@ -171,10 +246,10 @@ export const ColumnsModal: React.FC<{
171246
<Button data-test="columns-cancel-button" key="cancel" variant="link" onClick={() => onClose()}>
172247
{t('Cancel')}
173248
</Button>
174-
<Tooltip content={t('At least one column must be selected')} trigger="" isVisible={isSaveDisabled}>
249+
<Tooltip content={t('At least one column must be selected')} trigger="" isVisible={isSaveDisabled()}>
175250
<Button
176251
data-test="columns-save-button"
177-
isDisabled={isSaveDisabled}
252+
isDisabled={isSaveDisabled()}
178253
key="confirm"
179254
variant="primary"
180255
onClick={() => onSave()}

web/src/components/modals/export-modal.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,11 @@ export const ExportModal: React.FC<ExportModalProps> = ({
139139
<ChipGroup isClosable={false} categoryName={t('Time Range')}>
140140
<Chip isReadOnly={true}>{rangeText()}</Chip>
141141
</ChipGroup>
142-
<ChipGroup isClosable={false} categoryName={t('Deduplicate')}>
143-
<Chip isReadOnly={true}>{flowQuery.dedup}</Chip>
144-
</ChipGroup>
142+
{flowQuery.dedup && (
143+
<ChipGroup isClosable={false} categoryName={t('Deduplicate')}>
144+
<Chip isReadOnly={true}>true</Chip>
145+
</ChipGroup>
146+
)}
145147
<ChipGroup isClosable={false} categoryName={t('Limit')}>
146148
<Chip isReadOnly={true}>{flowQuery.limit}</Chip>
147149
</ChipGroup>

web/src/components/modals/overview-panels-modal.css

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,16 @@ label {
2424
margin-top: auto;
2525
margin-bottom: auto;
2626
padding: inherit;
27+
}
28+
29+
.popup-header-margin {
30+
margin-top: 1rem;
31+
}
32+
33+
.flex-center {
34+
align-self: center;
35+
}
36+
37+
.flex-gap {
38+
gap: 0.5rem;
2739
}

0 commit comments

Comments
 (0)