From a0e4da283813467a00539a48bc7364383d2fca60 Mon Sep 17 00:00:00 2001 From: Jason Madigan Date: Mon, 18 Nov 2024 11:53:12 +0000 Subject: [PATCH 1/2] custom ResourceList filtering Signed-off-by: Jason Madigan --- src/components/ResourceList.tsx | 87 ++++++++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 12 deletions(-) diff --git a/src/components/ResourceList.tsx b/src/components/ResourceList.tsx index ca9a36a..8779284 100644 --- a/src/components/ResourceList.tsx +++ b/src/components/ResourceList.tsx @@ -17,14 +17,12 @@ import { ResourceLink, useK8sWatchResources, VirtualizedTable, - useListPageFilter, Timestamp, TableData, RowProps, TableColumn, WatchK8sResource, ListPageBody, - ListPageFilter, } from '@openshift-console/dynamic-plugin-sdk'; import { SearchIcon } from '@patternfly/react-icons'; import { @@ -340,8 +338,12 @@ const ResourceList: React.FC = ({ resourceDescriptors, ); - const allData = Object.values(watchedResources).flatMap((res) => - res.loaded && !res.loadError ? (res.data as K8sResourceCommon[]) : [], + const allData = React.useMemo( + () => + Object.values(watchedResources).flatMap((res) => + res.loaded && !res.loadError ? (res.data as K8sResourceCommon[]) : [], + ), + [watchedResources], ); const allLoaded = Object.values(watchedResources).every((res) => res.loaded); @@ -353,7 +355,32 @@ const ResourceList: React.FC = ({ const combinedLoadError = loadErrors.length > 0 ? new Error(loadErrors.map((err) => err.message).join('; ')) : null; - const [data, filteredData, onFilterChange] = useListPageFilter(allData); + // Implement local filter state + const [filters, setFilters] = React.useState<{ [key: string]: string }>({}); + const [filteredData, setFilteredData] = React.useState([]); + + React.useEffect(() => { + let data = allData; + + // Apply filters + Object.keys(filters).forEach((key) => { + const value = filters[key]; + if (value) { + data = data.filter((item) => { + if (key === 'name') { + return item.metadata.name.toLowerCase().includes(value.toLowerCase()); + } else if (key === 'namespace') { + return item.metadata.namespace?.toLowerCase().includes(value.toLowerCase()); + } else if (key === 'type') { + return item.kind.toLowerCase().includes(value.toLowerCase()); + } + return true; + }); + } + }); + + setFilteredData(data); + }, [allData, filters]); const defaultColumns: TableColumn[] = [ { @@ -442,10 +469,14 @@ const ResourceList: React.FC = ({ case 'namespace': return ( - + {obj.metadata.namespace ? ( + + ) : ( + '-' + )} ); case 'Status': @@ -490,7 +521,39 @@ const ResourceList: React.FC = ({ )}
- + {/* Filter UI */} +
+ { + setCurrentPage(1); + setFilters({ ...filters, name: e.target.value }); + }} + style={{ marginRight: '8px' }} + /> + { + setCurrentPage(1); + setFilters({ ...filters, namespace: e.target.value }); + }} + style={{ marginRight: '8px' }} + /> + { + setCurrentPage(1); + setFilters({ ...filters, type: e.target.value }); + }} + /> +
+ {paginatedData.length === 0 && allLoaded ? ( @@ -504,7 +567,7 @@ const ResourceList: React.FC = ({ ) : ( data={paginatedData} - unfilteredData={data} + unfilteredData={filteredData} loaded={allLoaded} loadError={combinedLoadError} columns={usedColumns} @@ -512,7 +575,7 @@ const ResourceList: React.FC = ({ /> )} - {paginatedData.length > 0 && ( + {filteredData.length > 0 && (
Date: Mon, 18 Nov 2024 17:06:50 +0000 Subject: [PATCH 2/2] update filtering into the one input search box for name,ns and type Signed-off-by: R-Lawton --- .../en/plugin__kuadrant-console-plugin.json | 1 + src/components/KuadrantOverviewPage.tsx | 2 +- src/components/ResourceList.tsx | 131 +++++++++++------- src/components/kuadrant.css | 20 +-- 4 files changed, 94 insertions(+), 60 deletions(-) diff --git a/locales/en/plugin__kuadrant-console-plugin.json b/locales/en/plugin__kuadrant-console-plugin.json index 316983e..51b95ae 100644 --- a/locales/en/plugin__kuadrant-console-plugin.json +++ b/locales/en/plugin__kuadrant-console-plugin.json @@ -85,6 +85,7 @@ "Reference to an existing secret resource containing DNS provider credentials and configuration": "Reference to an existing secret resource containing DNS provider credentials and configuration", "Release Notes": "Release Notes", "Save": "Save", + "Search by {{filterValue}}...": "Search by {{filterValue}}...", "Select a gateway": "Select a gateway", "Select a Protocol": "Select a Protocol", "Select an ClusterIssuer": "Select an ClusterIssuer", diff --git a/src/components/KuadrantOverviewPage.tsx b/src/components/KuadrantOverviewPage.tsx index 9103387..bb5719e 100644 --- a/src/components/KuadrantOverviewPage.tsx +++ b/src/components/KuadrantOverviewPage.tsx @@ -171,7 +171,7 @@ const KuadrantOverviewPage: React.FC = () => { {t('Kuadrant')} - + {t('Kuadrant')} Overview
diff --git a/src/components/ResourceList.tsx b/src/components/ResourceList.tsx index 8779284..de655c7 100644 --- a/src/components/ResourceList.tsx +++ b/src/components/ResourceList.tsx @@ -11,6 +11,16 @@ import { EmptyStateBody, Title, Tooltip, + ToolbarItem, + ToolbarGroup, + Select, + MenuToggle, + InputGroup, + TextInput, + MenuToggleElement, + SelectOption, + Toolbar, + ToolbarContent, } from '@patternfly/react-core'; import { K8sResourceCommon, @@ -356,29 +366,39 @@ const ResourceList: React.FC = ({ loadErrors.length > 0 ? new Error(loadErrors.map((err) => err.message).join('; ')) : null; // Implement local filter state - const [filters, setFilters] = React.useState<{ [key: string]: string }>({}); + const [filters, setFilters] = React.useState(''); + const [isOpen, setIsOpen] = React.useState(false); + const [filterSelected, setFilterSelected] = React.useState('Name'); const [filteredData, setFilteredData] = React.useState([]); + const onToggleClick = () => { + setIsOpen(!isOpen); + }; + + const onFilterSelect = ( + _event: React.MouseEvent | undefined, + selection: string, + ) => { + setFilterSelected(selection); + setIsOpen(false); + }; + React.useEffect(() => { let data = allData; - // Apply filters - Object.keys(filters).forEach((key) => { - const value = filters[key]; - if (value) { - data = data.filter((item) => { - if (key === 'name') { - return item.metadata.name.toLowerCase().includes(value.toLowerCase()); - } else if (key === 'namespace') { - return item.metadata.namespace?.toLowerCase().includes(value.toLowerCase()); - } else if (key === 'type') { - return item.kind.toLowerCase().includes(value.toLowerCase()); - } - return true; - }); - } - }); - + if (filters) { + const filterValue = filters.toLowerCase(); + data = data.filter((item) => { + if (filterSelected === 'Name') { + return item.metadata.name.toLowerCase().includes(filterValue); + } else if (filterSelected === 'Namespace') { + return item.metadata.namespace?.toLowerCase().includes(filterValue); + } else if (filterSelected === 'Type') { + return item.kind.toLowerCase().includes(filterValue); + } + return true; + }); + } setFilteredData(data); }, [allData, filters]); @@ -442,6 +462,11 @@ const ResourceList: React.FC = ({ setCurrentPage(1); }; + const handleFilterChange = (value: string) => { + setCurrentPage(1); + setFilters(value); + }; + const ResourceRow: React.FC> = ({ obj, activeColumnIDs }) => { const { apiVersion, kind } = obj; const [group, version] = apiVersion.includes('/') ? apiVersion.split('/') : ['', apiVersion]; @@ -521,39 +546,43 @@ const ResourceList: React.FC = ({ )}
- {/* Filter UI */} -
- { - setCurrentPage(1); - setFilters({ ...filters, name: e.target.value }); - }} - style={{ marginRight: '8px' }} - /> - { - setCurrentPage(1); - setFilters({ ...filters, namespace: e.target.value }); - }} - style={{ marginRight: '8px' }} - /> - { - setCurrentPage(1); - setFilters({ ...filters, type: e.target.value }); - }} - /> -
- + + + + + + + + + + handleFilterChange(value)} + className="pf-v5-c-form-control co-text-filter-with-icon " + /> + + + + + {paginatedData.length === 0 && allLoaded ? ( diff --git a/src/components/kuadrant.css b/src/components/kuadrant.css index d2c7022..c56ea79 100644 --- a/src/components/kuadrant.css +++ b/src/components/kuadrant.css @@ -67,7 +67,9 @@ padding: 1rem; } -.kuadrant-dashboard-learning, .kuadrant-dashboard-feature-highlights, .kuadrant-dashboard-enhance { +.kuadrant-dashboard-learning, +.kuadrant-dashboard-feature-highlights, +.kuadrant-dashboard-enhance { margin-bottom: 0.5em; } @@ -79,7 +81,7 @@ color: var(--pf-global--palette--purple-700); } -.pf-theme-dark .kuadrant-dashboard-learning { +.pf-theme-dark .kuadrant-dashboard-learning { color: var(--pf-v5-global--palette--purple-200); } @@ -107,14 +109,15 @@ font-size: 0.8rem; color: var(--pf-global--palette--black-600); margin-left: 1rem; - margin-right: 1rem + margin-right: 1rem; } .kuadrant-limits-header { margin: 1rem 0 0; } -.kuadrant-limits-button, .pf-v5-c-label-group { +.kuadrant-limits-button, +.pf-v5-c-label-group { margin: 1rem; } @@ -143,11 +146,12 @@ .kuadrant-overview-create-button { position: absolute; - top: 0; - right: 0; + top: 0; + right: 0; } -.kuadrant-overview-create-list, .kuadrant-overview-create-button { +.kuadrant-overview-create-list, +.kuadrant-overview-create-button { font-family: RedHatText, helvetica, arial, sans-serif; - } +}