Skip to content

Commit

Permalink
Panels & columns popup filters
Browse files Browse the repository at this point in the history
  • Loading branch information
jpinsonneau committed Jan 26, 2024
1 parent f71c56d commit d63c3b0
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 60 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 @@ -188,6 +188,7 @@
"Manage columns": "Manage columns",
"Selected columns will appear in the table.": "Selected columns will appear in the table.",
"Click and drag the items to reorder the columns in the table.": "Click and drag the items to reorder the columns in the table.",
"Clear filters": "Clear filters",
"Unselect all": "Unselect all",
"Select all": "Select all",
"Restore default columns": "Restore default columns",
Expand Down
28 changes: 28 additions & 0 deletions web/src/components/filters/filters-toolbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,31 @@ div#filter-toolbar-search-filters {
#clear-all-filters-button {
padding: 0;
}

.custom-chip.buttonless>p {
margin-right: 1rem;
}

.custom-chip.selected,
.custom-chip.selected>button,
.custom-chip.selected>*,
.pf-theme-dark .custom-chip.selected,
.pf-theme-dark .custom-chip.selected>button,
.pf-theme-dark .custom-chip.selected>* {
color: #fff;
background-color: #06c;
}

.custom-chip.unselected,
.custom-chip.unselected>button,
.custom-chip.unselected>* {
color: #000;
background-color: #fff;
}

.pf-theme-dark .custom-chip.unselected,
.pf-theme-dark .custom-chip.unselected>button,
.pf-theme-dark .custom-chip.unselected>* {
color: #000;
background-color: #D2D2D2;
}
16 changes: 16 additions & 0 deletions web/src/components/modals/columns-modal.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,20 @@ label {
margin-top: auto;
margin-bottom: auto;
padding: inherit;
}

.popup-header-margin {
margin-top: 1rem;
}

.flex-center {
align-self: center;
}

.flex-gap {
gap: 0.5rem;
}

.custom-chip.pointer {
cursor: pointer;
}
133 changes: 104 additions & 29 deletions web/src/components/modals/columns-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
DragDrop,
Draggable,
Droppable,
Flex,
FlexItem,
Text,
TextContent,
TextVariants,
Expand All @@ -24,6 +26,8 @@ import { Column, ColumnSizeMap, getDefaultColumns, getFullColumnName } from '../
import './columns-modal.css';
import Modal from './modal';

export const COLUMN_FILTER_KEYS = ['source', 'destination', 'time', 'host', 'namespace', 'owner', 'ip', 'dns'];

export const ColumnsModal: React.FC<{
isModalOpen: boolean;
setModalOpen: (v: boolean) => void;
Expand All @@ -35,29 +39,22 @@ export const ColumnsModal: React.FC<{
}> = ({ id, config, isModalOpen, setModalOpen, columns, setColumns, setColumnSizes }) => {
const [resetClicked, setResetClicked] = React.useState<boolean>(false);
const [updatedColumns, setUpdatedColumns] = React.useState<Column[]>([]);
const [isSaveDisabled, setSaveDisabled] = React.useState<boolean>(true);
const [isAllSelected, setAllSelected] = React.useState<boolean>(false);
const [filterKeys, setFilterKeys] = React.useState<string[]>([]);
const { t } = useTranslation('plugin__netobserv-plugin');

React.useEffect(() => {
if (isModalOpen) {
setFilterKeys([]);
}
}, [isModalOpen]);

React.useEffect(() => {
if (!isModalOpen || _.isEmpty(updatedColumns)) {
setUpdatedColumns(_.cloneDeep(columns));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [columns, isModalOpen]);

React.useEffect(() => {
let allSelected = true;
_.forEach(updatedColumns, (col: Column) => {
if (!col.isSelected) {
allSelected = false;
return false;
}
});
setAllSelected(allSelected);
setSaveDisabled(_.isEmpty(updatedColumns.filter(col => col.isSelected)));
}, [updatedColumns]);

const onDrop = React.useCallback(
(source, dest) => {
if (dest) {
Expand Down Expand Up @@ -91,13 +88,49 @@ export const ColumnsModal: React.FC<{
setUpdatedColumns(getDefaultColumns(config.columns).filter(c => columns.some(existing => existing.id === c.id)));
}, [columns, config.columns]);

const isSaveDisabled = React.useCallback(() => {
return _.isEmpty(updatedColumns.filter(c => c.isSelected));
}, [updatedColumns]);

const isFilteredColumn = React.useCallback((c: Column, fks: string[]) => {
return (
_.isEmpty(fks) ||
_.reduce(
fks,
(acc, fk) =>
(acc =
acc &&
(c.id.toLowerCase().includes(fk) ||
c.name.toLowerCase().includes(fk) ||
c.group?.toLowerCase().includes(fk) ||
false)),
true
)
);
}, []);

const getColumnFilterKeys = React.useCallback(() => {
return COLUMN_FILTER_KEYS.filter(fk => columns.some(c => isFilteredColumn(c, [fk])));
}, [columns, isFilteredColumn]);

const filteredColumns = React.useCallback(() => {
return updatedColumns.filter(c => isFilteredColumn(c, filterKeys));
}, [filterKeys, isFilteredColumn, updatedColumns]);

const isAllSelected = React.useCallback(() => {
return _.reduce(filteredColumns(), (acc, c) => (acc = acc && c.isSelected), true);
}, [filteredColumns]);

const onSelectAll = React.useCallback(() => {
const allSelected = isAllSelected();
const result = [...updatedColumns];
_.forEach(result, (col: Column) => {
col.isSelected = !isAllSelected;
_.forEach(result, (c: Column) => {
if (isFilteredColumn(c, filterKeys)) {
c.isSelected = !allSelected;
}
});
setUpdatedColumns(result);
}, [updatedColumns, setUpdatedColumns, isAllSelected]);
}, [isAllSelected, updatedColumns, isFilteredColumn, filterKeys]);

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

const draggableItems = updatedColumns.map((column, i) => (
const toggleChip = React.useCallback(
(key: string) => {
if (filterKeys.includes(key)) {
setFilterKeys(filterKeys.filter(k => k !== key));
} else {
setFilterKeys(COLUMN_FILTER_KEYS.filter(f => f === key || filterKeys.includes(f)));
}
},
[filterKeys]
);

const draggableItems = filteredColumns().map((column, i) => (
<Draggable key={i} hasNoWrapper>
<DataListItem
key={'data-list-item-' + i}
Expand Down Expand Up @@ -153,15 +197,46 @@ export const ColumnsModal: React.FC<{
scrollable={true}
onClose={onClose}
description={
<TextContent>
<Text component={TextVariants.p}>
{t('Selected columns will appear in the table.')}&nbsp;
{t('Click and drag the items to reorder the columns in the table.')}
</Text>
<Button isInline onClick={onSelectAll} variant="link">
{isAllSelected ? t('Unselect all') : t('Select all')}
</Button>
</TextContent>
<>
<TextContent>
<Text component={TextVariants.p}>
{t('Selected columns will appear in the table.')}&nbsp;
{t('Click and drag the items to reorder the columns in the table.')}
</Text>
</TextContent>
<Flex className="popup-header-margin">
<FlexItem flex={{ default: 'flex_4' }}>
<Flex className="flex-gap">
{getColumnFilterKeys().map(key => {
return (
<FlexItem
key={key}
onClick={() => toggleChip(key)}
className={`custom-chip ${
filterKeys.includes(key) ? 'selected' : 'unselected'
} buttonless gap pointer`}
>
<Text component={TextVariants.p}>{key}</Text>
</FlexItem>
);
})}
</Flex>
</FlexItem>
<FlexItem flex={{ default: 'flex_1' }} className="flex-center">
{_.isEmpty(filteredColumns()) ? (
<Button isInline onClick={() => setFilterKeys([])} variant="link">
{t('Clear filters')}
</Button>
) : (
<Button isInline onClick={onSelectAll} variant="link">
{`${isAllSelected() ? t('Unselect all') : t('Select all')}${
!_.isEmpty(filterKeys) ? ' ' + filterKeys.join(',') : ''
}`}
</Button>
)}
</FlexItem>
</Flex>
</>
}
footer={
<div className="footer">
Expand All @@ -171,10 +246,10 @@ export const ColumnsModal: React.FC<{
<Button data-test="columns-cancel-button" key="cancel" variant="link" onClick={() => onClose()}>
{t('Cancel')}
</Button>
<Tooltip content={t('At least one column must be selected')} trigger="" isVisible={isSaveDisabled}>
<Tooltip content={t('At least one column must be selected')} trigger="" isVisible={isSaveDisabled()}>
<Button
data-test="columns-save-button"
isDisabled={isSaveDisabled}
isDisabled={isSaveDisabled()}
key="confirm"
variant="primary"
onClick={() => onSave()}
Expand Down
8 changes: 5 additions & 3 deletions web/src/components/modals/export-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,11 @@ export const ExportModal: React.FC<ExportModalProps> = ({
<ChipGroup isClosable={false} categoryName={t('Time Range')}>
<Chip isReadOnly={true}>{rangeText()}</Chip>
</ChipGroup>
<ChipGroup isClosable={false} categoryName={t('Deduplicate')}>
<Chip isReadOnly={true}>{flowQuery.dedup}</Chip>
</ChipGroup>
{flowQuery.dedup && (
<ChipGroup isClosable={false} categoryName={t('Deduplicate')}>
<Chip isReadOnly={true}>true</Chip>
</ChipGroup>
)}
<ChipGroup isClosable={false} categoryName={t('Limit')}>
<Chip isReadOnly={true}>{flowQuery.limit}</Chip>
</ChipGroup>
Expand Down
12 changes: 12 additions & 0 deletions web/src/components/modals/overview-panels-modal.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,16 @@ label {
margin-top: auto;
margin-bottom: auto;
padding: inherit;
}

.popup-header-margin {
margin-top: 1rem;
}

.flex-center {
align-self: center;
}

.flex-gap {
gap: 0.5rem;
}
Loading

0 comments on commit d63c3b0

Please sign in to comment.