From d7bc64a1ba08c20cc8498fe7eb9e7a790768cd65 Mon Sep 17 00:00:00 2001 From: Rastislav Wagner Date: Fri, 30 Apr 2021 10:04:21 +0200 Subject: [PATCH] New virtualized table component --- .../src/components/nodes/NodeDetailsPage.tsx | 2 +- .../factory/Table/VirtualizedTable.tsx | 285 +++++++++++++ .../factory/Table/VirtualizedTableBody.tsx | 80 ++++ .../public/components/factory/Table/sort.ts | 29 ++ frontend/public/components/machine.tsx | 29 +- frontend/public/components/pod.tsx | 389 +++++++++++------- .../components/utils/horizontal-nav.tsx | 3 +- 7 files changed, 652 insertions(+), 165 deletions(-) create mode 100644 frontend/public/components/factory/Table/VirtualizedTable.tsx create mode 100644 frontend/public/components/factory/Table/VirtualizedTableBody.tsx create mode 100644 frontend/public/components/factory/Table/sort.ts diff --git a/frontend/packages/console-app/src/components/nodes/NodeDetailsPage.tsx b/frontend/packages/console-app/src/components/nodes/NodeDetailsPage.tsx index 35c5e85b6ad6..87ccc893abe1 100644 --- a/frontend/packages/console-app/src/components/nodes/NodeDetailsPage.tsx +++ b/frontend/packages/console-app/src/components/nodes/NodeDetailsPage.tsx @@ -33,7 +33,7 @@ const NodeDetailsPage: React.FC> = (pro )), events(ResourceEventStream), diff --git a/frontend/public/components/factory/Table/VirtualizedTable.tsx b/frontend/public/components/factory/Table/VirtualizedTable.tsx new file mode 100644 index 000000000000..a9ae38d0dff2 --- /dev/null +++ b/frontend/public/components/factory/Table/VirtualizedTable.tsx @@ -0,0 +1,285 @@ +import * as React from 'react'; +import * as _ from 'lodash'; +import { + Table as PfTable, + TableHeader, + TableGridBreakpoint, + OnSelect, + SortByDirection, + ICell, +} from '@patternfly/react-table'; +import { AutoSizer, WindowScroller } from '@patternfly/react-virtualized-extension'; +import { useNamespace } from '@console/shared/src/hooks/useNamespace'; + +import VirtualizedTableBody from './VirtualizedTableBody'; +import { history, StatusBox, WithScrollContainer } from '../../utils'; + +const BREAKPOINT_SM = 576; +const BREAKPOINT_MD = 768; +const BREAKPOINT_LG = 992; +const BREAKPOINT_XL = 1200; +const BREAKPOINT_XXL = 1400; +const MAX_COL_XS = 2; +const MAX_COL_SM = 4; +const MAX_COL_MD = 4; +const MAX_COL_LG = 6; +const MAX_COL_XL = 8; + +const isColumnVisible = ( + widthInPixels: number, + columnID: string, + columns: Set = new Set(), + showNamespaceOverride: boolean, + namespace: string, +) => { + const showNamespace = columnID !== 'namespace' || !namespace || showNamespaceOverride; + if (_.isEmpty(columns) && showNamespace) { + return true; + } + if (!columns.has(columnID) || !showNamespace) { + return false; + } + const columnIndex = [...columns].indexOf(columnID); + if (widthInPixels < BREAKPOINT_SM) { + return columnIndex < MAX_COL_XS; + } + if (widthInPixels < BREAKPOINT_MD) { + return columnIndex < MAX_COL_SM; + } + if (widthInPixels < BREAKPOINT_LG) { + return columnIndex < MAX_COL_MD; + } + if (widthInPixels < BREAKPOINT_XL) { + return columnIndex < MAX_COL_LG; + } + if (widthInPixels < BREAKPOINT_XXL) { + return columnIndex < MAX_COL_XL; + } + return true; +}; + +const getActiveColumns = ( + windowWidth: number, + allColumns: TableColumn[], + activeColumns: Set, + columnManagementID: string, + showNamespaceOverride: boolean, + namespace: string, +) => { + let columns = [...allColumns]; + if (_.isEmpty(activeColumns)) { + activeColumns = new Set( + columns.map((col) => { + if (col.id && !col.additional) { + return col.id; + } + }), + ); + } + if (columnManagementID) { + columns = columns?.filter( + (col) => + isColumnVisible(windowWidth, col.id, activeColumns, showNamespaceOverride, namespace) || + col.title === '', + ); + } else { + columns = columns?.filter((col) => activeColumns.has(col.id) || col.title === ''); + } + + const showNamespace = !namespace || showNamespaceOverride; + if (!showNamespace) { + columns = columns.filter((column) => column.id !== 'namespace'); + } + return columns; +}; + +export type TableColumn = ICell & { + title: string; + id?: string; + additional?: boolean; + sort?: (data: D[], sortDirection: SortByDirection) => D[]; +}; + +export type RowProps = { + obj: D; + index: number; + columns: TableColumn[]; + isScrolling: boolean; + style: object; +}; + +type VirtualizedTableProps = { + data: D[]; + loaded: boolean; + loadError: any; + columns: TableColumn[]; + Row: React.ComponentType>; + NoDataEmptyMsg?: React.ComponentType<{}>; + EmptyMsg?: React.ComponentType<{}>; + scrollNode?: () => HTMLElement; + onSelect?: OnSelect; + label?: string; + 'aria-label'?: string; + gridBreakPoint?: TableGridBreakpoint; + activeColumns?: Set; + columnManagementID?: string; + showNamespaceOverride?: boolean; +}; + +const VirtualizedTable: React.FC = ({ + data, + loaded, + loadError, + columns: allColumns, + NoDataEmptyMsg, + EmptyMsg, + scrollNode, + label, + 'aria-label': ariaLabel, + gridBreakPoint = TableGridBreakpoint.none, + onSelect, + Row, + activeColumns, + columnManagementID, + showNamespaceOverride, +}) => { + const [sortBy, setSortBy] = React.useState<{ + index: number; + direction: SortByDirection; + }>({ index: 0, direction: SortByDirection.asc }); + + const columnShift = onSelect ? 1 : 0; //shift indexes by 1 if select provided + + const [windowWidth, setWindowWidth] = React.useState(window.innerWidth); + const namespace = useNamespace(); + + const columns = React.useMemo( + () => + getActiveColumns( + windowWidth, + allColumns, + activeColumns, + columnManagementID, + showNamespaceOverride, + namespace, + ), + [windowWidth, allColumns, activeColumns, columnManagementID, showNamespaceOverride, namespace], + ); + + const applySort = React.useCallback( + (index, direction) => { + const url = new URL(window.location.href); + const sp = new URLSearchParams(window.location.search); + + const sortColumn = columns[index - columnShift]; + if (sortColumn) { + sp.set('orderBy', direction); + sp.set('sortBy', sortColumn.title); + history.replace(`${url.pathname}?${sp.toString()}${url.hash}`); + setSortBy({ + index, + direction, + }); + } + }, + [columnShift, columns], + ); + + data = React.useMemo(() => { + const sortColumn = columns[sortBy.index - columnShift]; + return sortColumn.sort ? sortColumn.sort(data, sortBy.direction) : data; + }, [columnShift, columns, data, sortBy.direction, sortBy.index]); + + React.useEffect(() => { + const handleResize = _.debounce(() => setWindowWidth(window.innerWidth), 100); + + const sp = new URLSearchParams(window.location.search); + const columnIndex = _.findIndex(columns, { title: sp.get('sortBy') }); + + if (columnIndex > -1) { + const sortOrder = + sp.get('orderBy') === SortByDirection.desc.valueOf() + ? SortByDirection.desc + : SortByDirection.asc; + setSortBy({ + index: columnIndex + columnShift, + direction: sortOrder, + }); + } + + // re-render after resize + window.addEventListener('resize', handleResize); + return () => { + window.removeEventListener('resize', handleResize); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const onSort = React.useCallback( + (event, index, direction) => { + event.preventDefault(); + applySort(index, direction); + }, + [applySort], + ); + + const renderVirtualizedTable = (scrollContainer) => ( + + {({ height, isScrolling, registerChild, onChildScroll, scrollTop }) => ( + + {({ width }) => ( +
+ +
+ )} +
+ )} +
+ ); + + return ( +
+ } + data={data} + loaded={loaded} + loadError={loadError} + // unfilteredData={propData} TODO + label={label} + NoDataEmptyMsg={NoDataEmptyMsg} + EmptyMsg={EmptyMsg} + > +
+ + + + {scrollNode ? ( + renderVirtualizedTable(scrollNode) + ) : ( + {renderVirtualizedTable} + )} +
+
+
+ ); +}; + +export default VirtualizedTable; diff --git a/frontend/public/components/factory/Table/VirtualizedTableBody.tsx b/frontend/public/components/factory/Table/VirtualizedTableBody.tsx new file mode 100644 index 000000000000..4cacdc3565fa --- /dev/null +++ b/frontend/public/components/factory/Table/VirtualizedTableBody.tsx @@ -0,0 +1,80 @@ +import * as React from 'react'; +import { VirtualTableBody } from '@patternfly/react-virtualized-extension'; +import { CellMeasurerCache, CellMeasurer } from 'react-virtualized'; +import { Scroll } from '@patternfly/react-virtualized-extension/dist/js/components/Virtualized/types'; +import { TableColumn, RowProps } from './VirtualizedTable'; + +type VirtualizedTableBodyProps = { + Row: React.ComponentType>; + data: D[]; + height: number; + isScrolling: boolean; + onChildScroll: (params: Scroll) => void; + columns: TableColumn[]; + scrollTop: number; + width: number; +}; + +const VirtualizedTableBody: React.FC = ({ + Row, + height, + isScrolling, + onChildScroll, + data, + columns, + scrollTop, + width, +}) => { + const cellMeasurementCache = new CellMeasurerCache({ + fixedWidth: true, + minHeight: 44, + keyMapper: (rowIndex) => data?.[rowIndex]?.metadata?.uid || rowIndex, // TODO custom keyMapper ? + }); + + const rowRenderer = ({ index, isScrolling: scrolling, isVisible, key, style, parent }) => { + const rowArgs: RowProps = { + obj: data[index], + index, + columns, + isScrolling: scrolling, + style, + }; + + // do not render non visible elements (this excludes overscan) + if (!isVisible) { + return null; + } + return ( + + + + ); + }; + + return ( + + ); +}; + +export default VirtualizedTableBody; diff --git a/frontend/public/components/factory/Table/sort.ts b/frontend/public/components/factory/Table/sort.ts new file mode 100644 index 000000000000..8c06a991c662 --- /dev/null +++ b/frontend/public/components/factory/Table/sort.ts @@ -0,0 +1,29 @@ +import { SortByDirection } from '@patternfly/react-table'; + +import { K8sResourceCommon } from '../../../module/k8s'; + +const isNumber = (value): value is number => Number.isFinite(value); + +export const sortResourceByValue = ( + a: D, + b: D, + sortDirection: SortByDirection, + aValue: V, + bValue: V, +): number => { + const lang = navigator.languages[0] || navigator.language; + // Use `localCompare` with `numeric: true` for a natural sort order (e.g., pv-1, pv-9, pv-10) + const compareOpts = { numeric: true, ignorePunctuation: true }; + const result: number = + isNumber(aValue) && isNumber(bValue) + ? aValue - bValue + : `${aValue}`.localeCompare(`${bValue}`, lang, compareOpts); + if (result !== 0) { + return sortDirection === SortByDirection.asc ? result : result * -1; + } + + // Use name as a secondary sort for a stable sort. + const aName = a?.metadata?.name || ''; + const bName = b?.metadata?.name || ''; + return aName.localeCompare(bName, lang, compareOpts); +}; diff --git a/frontend/public/components/machine.tsx b/frontend/public/components/machine.tsx index bed3424c62b2..83ec837db6f7 100644 --- a/frontend/public/components/machine.tsx +++ b/frontend/public/components/machine.tsx @@ -16,7 +16,7 @@ import { MachineModel } from '../models'; import { MachineKind, referenceForModel } from '../module/k8s'; import { Conditions } from './conditions'; import NodeIPList from '@console/app/src/components/nodes/NodeIPList'; -import { DetailsPage, Table, TableRow, TableData, RowFunction } from './factory'; +import { DetailsPage, TableRow, TableData } from './factory'; import ListPageFilter from './factory/ListPage/ListPageFilter'; import ListPageHeader from './factory/ListPage/ListPageHeader'; import ListPageBody from './factory/ListPage/ListPageBody'; @@ -34,6 +34,9 @@ import { } from './utils'; import { ResourceEventStream } from './events'; import { useK8sWatchResource } from './utils/k8s-watch-hook'; +import VirtualizedTable, { TableColumn } from './factory/Table/VirtualizedTable'; +import { sortResourceByValue } from './factory/Table/sort'; + const { common } = Kebab.factory; const menuActions = [...Kebab.getExtensionsActionsForKind(MachineModel), ...common]; export const machineReference = referenceForModel(MachineModel); @@ -52,7 +55,7 @@ const tableColumnClasses = [ const getMachineProviderState = (obj: MachineKind): string => obj?.status?.providerStatus?.instanceState; -const MachineTableRow: RowFunction = ({ obj, index, key, style }) => { +const MachineTableRow: React.FC = ({ obj, index, key, style }) => { const nodeName = getMachineNodeName(obj); const region = getMachineRegion(obj); const zone = getMachineZone(obj); @@ -175,11 +178,16 @@ type MachineListProps = { export const MachineList: React.FC = (props) => { const { t } = useTranslation(); - const MachineTableHeader = () => { - return [ + + const machineTableColumn = React.useMemo[]>( + () => [ { title: t('machines~Name'), - sortField: 'metadata.name', + sort: (data, direction) => { + const sortFunc = (a: MachineKind, b: MachineKind) => + sortResourceByValue(a, b, direction, a.metadata.name, b.metadata.name); + return data.sort(sortFunc); + }, transforms: [sortable], props: { className: tableColumnClasses[0] }, }, @@ -224,15 +232,16 @@ export const MachineList: React.FC = (props) => { title: '', props: { className: tableColumnClasses[7] }, }, - ]; - }; + ], + [t], + ); + return ( - ); }; diff --git a/frontend/public/components/pod.tsx b/frontend/public/components/pod.tsx index 1719cf5f7b14..8c82ae2ffde6 100644 --- a/frontend/public/components/pod.tsx +++ b/frontend/public/components/pod.tsx @@ -6,7 +6,6 @@ import { connect } from 'react-redux'; import { Link } from 'react-router-dom'; import { sortable } from '@patternfly/react-table'; import { useTranslation } from 'react-i18next'; -import i18next from 'i18next'; import * as classNames from 'classnames'; import * as _ from 'lodash-es'; import { Button, Popover } from '@patternfly/react-core'; @@ -32,7 +31,7 @@ import { } from '../module/k8s/pods'; import { getContainerState, getContainerStatus } from '../module/k8s/container'; import { ResourceEventStream } from './events'; -import { DetailsPage, Table, TableRow, TableData, RowFunctionArgs } from './factory'; +import { DetailsPage, TableRow, TableData } from './factory'; import ListPageBody from './factory/ListPage/ListPageBody'; import ListPageHeader from './factory/ListPage/ListPageHeader'; import ListPageFilter from './factory/ListPage/ListPageFilter'; @@ -74,6 +73,9 @@ import { PodModel } from '../models'; import { Conditions } from './conditions'; import { useK8sWatchResource } from './utils/k8s-watch-hook'; import { useListPageFilter } from './factory/ListPage/filter-hook'; +import VirtualizedTable, { RowProps, TableColumn } from './factory/Table/VirtualizedTable'; +import { TFunction } from 'i18next'; +import { sortResourceByValue } from './factory/Table/sort'; // Only request metrics if the device's screen width is larger than the // breakpoint where metrics are visible. @@ -199,109 +201,181 @@ const podRowStateToProps = ({ UI }) => ({ metrics: UI.getIn(['metrics', 'pod']), }); -const getHeader = (showNodes) => { - return () => { - return [ - { - title: i18next.t(podColumnInfo.name.title), - id: podColumnInfo.name.id, - sortField: 'metadata.name', - transforms: [sortable], - props: { className: podColumnInfo.name.classes }, - }, - { - title: i18next.t(podColumnInfo.namespace.title), - id: podColumnInfo.namespace.id, - sortField: 'metadata.namespace', - transforms: [sortable], - props: { className: podColumnInfo.namespace.classes }, - }, - { - title: i18next.t(podColumnInfo.status.title), - id: podColumnInfo.status.id, - sortFunc: 'podPhase', - transforms: [sortable], - props: { className: podColumnInfo.status.classes }, - }, - { - title: i18next.t(podColumnInfo.ready.title), - id: podColumnInfo.ready.id, - sortFunc: 'podReadiness', - transforms: [sortable], - props: { className: podColumnInfo.ready.classes }, - }, - { - title: i18next.t(podColumnInfo.restarts.title), - id: podColumnInfo.restarts.id, - sortFunc: 'podRestarts', - transforms: [sortable], - props: { className: podColumnInfo.restarts.classes }, - }, - { - title: showNodes - ? i18next.t(podColumnInfo.node.title) - : i18next.t(podColumnInfo.owner.title), - id: podColumnInfo.owner.id, - sortField: showNodes ? 'spec.nodeName' : 'metadata.ownerReferences[0].name', - transforms: [sortable], - props: { className: podColumnInfo.owner.classes }, - }, - { - title: i18next.t(podColumnInfo.memory.title), - id: podColumnInfo.memory.id, - sortFunc: 'podMemory', - transforms: [sortable], - props: { className: podColumnInfo.memory.classes }, - }, - { - title: i18next.t(podColumnInfo.cpu.title), - id: podColumnInfo.cpu.id, - sortFunc: 'podCPU', - transforms: [sortable], - props: { className: podColumnInfo.cpu.classes }, - }, - { - title: i18next.t(podColumnInfo.created.title), - id: podColumnInfo.created.id, - sortField: 'metadata.creationTimestamp', - transforms: [sortable], - props: { className: podColumnInfo.created.classes }, - }, - { - title: i18next.t(podColumnInfo.node.title), - id: podColumnInfo.node.id, - sortField: 'spec.nodeName', - transforms: [sortable], - props: { className: podColumnInfo.node.classes }, - additional: true, - }, - { - title: i18next.t(podColumnInfo.labels.title), - id: podColumnInfo.labels.id, - sortField: 'metadata.labels', - transforms: [sortable], - props: { className: podColumnInfo.labels.classes }, - additional: true, - }, - { - title: i18next.t(podColumnInfo.ipaddress.title), - id: podColumnInfo.ipaddress.id, - sortField: 'status.hostIP', - transforms: [sortable], - props: { className: podColumnInfo.ipaddress.classes }, - additional: true, - }, - { - title: '', - props: { className: Kebab.columnClass }, - }, - ]; - }; -}; +const getColumns = (showNodes: boolean, t: TFunction): TableColumn[] => [ + { + title: t(podColumnInfo.name.title), + id: podColumnInfo.name.id, + sort: (data, direction) => { + const sortFunc = (a: PodKind, b: PodKind) => + sortResourceByValue(a, b, direction, a.metadata.name, b.metadata.name); + return data.sort(sortFunc); + }, + transforms: [sortable], + props: { className: podColumnInfo.name.classes }, + }, + { + title: t(podColumnInfo.namespace.title), + id: podColumnInfo.namespace.id, + sort: (data, direction) => { + const sortFunc = (a: PodKind, b: PodKind) => + sortResourceByValue(a, b, direction, a.metadata.namespace, b.metadata.namespace); + return data.sort(sortFunc); + }, + transforms: [sortable], + props: { className: podColumnInfo.namespace.classes }, + }, + { + title: t(podColumnInfo.status.title), + id: podColumnInfo.status.id, + sort: (data, direction) => { + const sortFunc = (a: PodKind, b: PodKind) => + sortResourceByValue(a, b, direction, podPhase(a), podPhase(b)); + return data.sort(sortFunc); + }, + transforms: [sortable], + props: { className: podColumnInfo.status.classes }, + }, + { + title: t(podColumnInfo.ready.title), + id: podColumnInfo.ready.id, + sort: (data, direction) => { + const sortFunc = (a: PodKind, b: PodKind) => + sortResourceByValue( + a, + b, + direction, + podReadiness(a).readyCount, + podReadiness(b).readyCount, + ); + return data.sort(sortFunc); + }, + transforms: [sortable], + props: { className: podColumnInfo.ready.classes }, + }, + { + title: t(podColumnInfo.restarts.title), + id: podColumnInfo.restarts.id, + sort: (data, direction) => { + const sortFunc = (a: PodKind, b: PodKind) => + sortResourceByValue(a, b, direction, podRestarts(a), podRestarts(b)); + return data.sort(sortFunc); + }, + transforms: [sortable], + props: { className: podColumnInfo.restarts.classes }, + }, + { + title: showNodes ? t(podColumnInfo.node.title) : t(podColumnInfo.owner.title), + id: podColumnInfo.owner.id, + sort: (data, direction) => { + const sortFunc = (a: PodKind, b: PodKind) => + sortResourceByValue( + a, + b, + direction, + showNodes ? a.spec.nodeName : a.metadata.ownerReferences?.[0]?.name, + showNodes ? b.spec.nodeName : b.metadata.ownerReferences?.[0]?.name, + ); + return data.sort(sortFunc); + }, + transforms: [sortable], + props: { className: podColumnInfo.owner.classes }, + }, + { + title: t(podColumnInfo.memory.title), + id: podColumnInfo.memory.id, + sort: (data, direction) => { + const sortFunc = (a: PodKind, b: PodKind) => + sortResourceByValue( + a, + b, + direction, + UIActions.getPodMetric(a, 'memory'), + UIActions.getPodMetric(b, 'memory'), + ); + return data.sort(sortFunc); + }, + transforms: [sortable], + props: { className: podColumnInfo.memory.classes }, + }, + { + title: t(podColumnInfo.cpu.title), + id: podColumnInfo.cpu.id, + sort: (data, direction) => { + const sortFunc = (a: PodKind, b: PodKind) => + sortResourceByValue( + a, + b, + direction, + UIActions.getPodMetric(a, 'cpu'), + UIActions.getPodMetric(b, 'cpu'), + ); + return data.sort(sortFunc); + }, + transforms: [sortable], + props: { className: podColumnInfo.cpu.classes }, + }, + { + title: t(podColumnInfo.created.title), + id: podColumnInfo.created.id, + sort: (data, direction) => { + const sortFunc = (a: PodKind, b: PodKind) => + sortResourceByValue( + a, + b, + direction, + a.metadata.creationTimestamp, + b.metadata.creationTimestamp, + ); + return data.sort(sortFunc); + }, + transforms: [sortable], + props: { className: podColumnInfo.created.classes }, + }, + { + title: t(podColumnInfo.node.title), + id: podColumnInfo.node.id, + sort: (data, direction) => { + const sortFunc = (a: PodKind, b: PodKind) => + sortResourceByValue(a, b, direction, a.spec.nodeName, b.spec.nodeName); + return data.sort(sortFunc); + }, + transforms: [sortable], + props: { className: podColumnInfo.node.classes }, + additional: true, + }, + { + title: t(podColumnInfo.labels.title), + id: podColumnInfo.labels.id, + sort: (data, direction) => { + const sortFunc = (a: PodKind, b: PodKind) => + sortResourceByValue(a, b, direction, a.metadata.labels, b.metadata.labels); + return data.sort(sortFunc); + }, + transforms: [sortable], + props: { className: podColumnInfo.labels.classes }, + additional: true, + }, + { + title: t(podColumnInfo.ipaddress.title), + id: podColumnInfo.ipaddress.id, + sort: (data, direction) => { + const sortFunc = (a: PodKind, b: PodKind) => + sortResourceByValue(a, b, direction, a.status?.hostIP, b.status?.hostIP); + return data.sort(sortFunc); + }, + transforms: [sortable], + props: { className: podColumnInfo.ipaddress.classes }, + additional: true, + }, + { + title: '', + props: { className: Kebab.columnClass }, + }, +]; -const getSelectedColumns = (showNodes: boolean) => { +const getSelectedColumns = (showNodes: boolean, t: TFunction) => { return new Set( - getHeader(showNodes)().reduce((acc, column) => { + getColumns(showNodes, t).reduce((acc, column) => { if (column.id && !column.additional) { acc.push(column.id); } @@ -314,12 +388,12 @@ const PodTableRow = connect(p ({ obj: pod, index, - rowKey, style, metrics, showNodes, showNamespaceOverride, }: PodTableRowProps & PodTableRowPropsFromState) => { + const { t } = useTranslation(); const [tableColumns, , loaded] = useUserSettingsCompatibility( COLUMN_MANAGEMENT_CONFIGMAP_KEY, COLUMN_MANAGEMENT_LOCAL_STORAGE_KEY, @@ -335,9 +409,9 @@ const PodTableRow = connect(p const columns: Set = loaded && tableColumns?.[columnManagementID]?.length > 0 ? new Set(tableColumns[columnManagementID]) - : getSelectedColumns(showNodes); + : getSelectedColumns(showNodes, t); return ( - + @@ -801,19 +875,6 @@ export const PodsDetailsPage: React.FC = (props) => { }; PodsDetailsPage.displayName = 'PodsDetailsPage'; -const getRow = (showNodes, showNamespaceOverride) => { - return (rowArgs: RowFunctionArgs) => ( - - ); -}; - export const PodList: React.FC = withUserSettingsCompatibility< PodListProps & WithUserSettingsCompatibilityProps, TableColumnsType @@ -822,27 +883,51 @@ export const PodList: React.FC = withUserSettingsCompatibility< COLUMN_MANAGEMENT_LOCAL_STORAGE_KEY, undefined, true, -)(({ userSettingState: tableColumns, ...props }) => { - const showNodes = props?.customData?.showNodes; - const showNamespaceOverride = props?.customData?.showNamespaceOverride; - const { t } = useTranslation(); - const selectedColumns: Set = - tableColumns?.[columnManagementID]?.length > 0 - ? new Set(tableColumns[columnManagementID]) - : null; - return ( -
- ); -}); +)( + ({ + userSettingState: tableColumns, + data, + loaded, + showNodes, + showNamespaceOverride, + loadError, + }) => { + const { t } = useTranslation(); + const selectedColumns = React.useMemo>( + () => + tableColumns?.[columnManagementID]?.length > 0 + ? new Set(tableColumns[columnManagementID]) + : null, + [tableColumns], + ); + + const Row = React.useCallback( + (rowProps: RowProps) => ( + + ), + [showNamespaceOverride, showNodes], + ); + + const columns = React.useMemo(() => getColumns(showNodes, t), [showNodes, t]); + return ( + + ); + }, +); PodList.displayName = 'PodList'; export const filters = [ @@ -890,11 +975,12 @@ export const PodsPage = connect<{}, PodPagePropsFromDispatch, PodPageProps>( canCreate = true, namespace, setPodMetrics, - customData, userSettingState: tableColumns, showTitle = true, selector, fieldSelector, + showNodes, + showNamespaceOverride, } = props; const { t } = useTranslation(); /* eslint-disable react-hooks/exhaustive-deps */ @@ -945,15 +1031,13 @@ export const PodsPage = connect<{}, PodPagePropsFromDispatch, PodPageProps>( rowFilters={filters} onFilterChange={onFilterChange} columnLayout={{ - columns: getHeader(props?.customData?.showNodes)().map((column) => - _.pick(column, ['title', 'additional', 'id']), - ), + columns: getColumns(showNodes, t), id: columnManagementID, selectedColumns: tableColumns?.[columnManagementID]?.length > 0 ? new Set(tableColumns[columnManagementID]) : null, - showNamespaceOverride: props?.customData?.showNamespaceOverride, + showNamespaceOverride, type: t('public~Pod'), }} /> @@ -961,7 +1045,8 @@ export const PodsPage = connect<{}, PodPagePropsFromDispatch, PodPageProps>( data={filteredData} loaded={loaded} loadError={loadError} - customData={customData} + showNamespaceOverride={showNamespaceOverride} + showNodes={showNodes} /> @@ -1013,11 +1098,7 @@ type PodDetailsProps = { obj: PodKind; }; -type PodTableRowProps = { - obj: PodKind; - index: number; - rowKey: string; - style: object; +type PodTableRowProps = RowProps & { showNodes?: boolean; showNamespaceOverride?: boolean; }; @@ -1030,7 +1111,8 @@ type PodListProps = { data: PodKind[]; loaded: boolean; loadError: any; - customData?: any; + showNamespaceOverride?: boolean; + showNodes?: boolean; }; type PodPageProps = { @@ -1039,7 +1121,8 @@ type PodPageProps = { namespace?: string; selector?: any; showTitle?: boolean; - customData?: any; + showNamespaceOverride?: boolean; + showNodes?: boolean; }; type PodPagePropsFromDispatch = { diff --git a/frontend/public/components/utils/horizontal-nav.tsx b/frontend/public/components/utils/horizontal-nav.tsx index c6fcd6d634a1..634484f846a3 100644 --- a/frontend/public/components/utils/horizontal-nav.tsx +++ b/frontend/public/components/utils/horizontal-nav.tsx @@ -43,7 +43,8 @@ export class PodsComponent extends React.PureComponent { namespace={namespace} selector={selector} canCreate={false} - customData={customData} + showNamespaceOverride={customData?.showNamespaceOverride} + showNodes={customData?.showNodes} /> ); }