Skip to content

Commit

Permalink
Adding custom hook for selection on list component
Browse files Browse the repository at this point in the history
  • Loading branch information
gnehapk committed Jun 22, 2020
1 parent c882a73 commit 32500da
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 169 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
} from '@console/internal/module/k8s';
import { ListPage } from '@console/internal/components/factory';
import { NodeModel } from '@console/internal/models';
import { hasLabel, getName } from '@console/shared';
import { getName } from '@console/shared';
import {
withHandlePromise,
HandlePromiseProps,
Expand All @@ -38,9 +38,13 @@ import { cephStorageLabel } from '../../selectors';
import NodeTable from './node-list';
import { PVsAvailableCapacity } from './pvs-available-capacity';
import { OCS_FLAG, OCS_CONVERGED_FLAG } from '../../features';

import './ocs-install.scss';

const makeLabelNodesRequest = (selectedNodes: NodeKind[]): Promise<NodeKind>[] => {
const hasLabel = (obj: NodeType, label: string): boolean =>
_.has(obj, label);

const makeLabelNodesRequest = (selectedNodes: NodeType[]): Promise<NodeType>[] => {
const patch = [
{
op: 'add',
Expand All @@ -60,7 +64,7 @@ const makeLabelNodesRequest = (selectedNodes: NodeKind[]): Promise<NodeKind>[] =
};

const makeOCSRequest = (
selectedData: NodeKind[],
selectedData: NodeType[],
storageClass: StorageClassResourceKind,
osdSize: string,
): Promise<any> => {
Expand Down Expand Up @@ -96,16 +100,15 @@ export const CreateOCSServiceForm = withHandlePromise<
params: { appName, ns },
},
} = props;
const [selectedNodes, setSelectedNodes] = React.useState<NodeKind[]>(null);
const [visibleRows, setVisibleRows] = React.useState<NodeKind[]>(null);
const [osdSize, setOSDSize] = React.useState(defaultRequestSize.NON_BAREMETAL);
const [storageClass, setStorageClass] = React.useState<StorageClassResourceKind>(null);
const dispatch = useDispatch();

const [nodes, setNodes] = React.useState<NodeType[]>([]);

const submit = (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
// eslint-disable-next-line promise/catch-or-return
handlePromise(makeOCSRequest(selectedNodes, storageClass, osdSize)).then(() => {
handlePromise(makeOCSRequest(nodes, storageClass, osdSize)).then(() => {
dispatch(setFlag(OCS_CONVERGED_FLAG, true));
dispatch(setFlag(OCS_FLAG, true));
history.push(
Expand Down Expand Up @@ -153,7 +156,14 @@ export const CreateOCSServiceForm = withHandlePromise<
kind={NodeModel.kind}
showTitle={false}
ListComponent={NodeTable}
customData={{ selectedNodes, setSelectedNodes, visibleRows, setVisibleRows }}
customData={{
onRowSelected: (selectedNodes :NodeKind[]) => {
const nodes = selectedNodes.map((n) => {
return {name: n.metadata.name, labels: n.metadata.labels}
});
setNodes(nodes);
}
}}
/>
</FormGroup>
<FormGroup
Expand Down Expand Up @@ -200,7 +210,7 @@ export const CreateOCSServiceForm = withHandlePromise<
type="button"
variant="primary"
onClick={submit}
isDisabled={(selectedNodes?.length ?? 0) < minSelectedNode}
isDisabled={(nodes?.length ?? 0) < minSelectedNode}
>
Create
</Button>
Expand All @@ -216,3 +226,8 @@ export const CreateOCSServiceForm = withHandlePromise<
type CreateOCSServiceFormProps = {
match: match<{ appName: string; ns: string }>;
};

type NodeType = {
name: string;
labels: { [key:string]: string }
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import {
import { humanizeCpuCores, ResourceLink, pluralize } from '@console/internal/components/utils/';
import { NodeKind } from '@console/internal/module/k8s';
import { Table } from '@console/internal/components/factory';
import { IRow, OnSelect } from '@patternfly/react-table';
import { IRow } from '@patternfly/react-table';
import { hasOCSTaint, hasTaints, getConvertedUnits } from '../../utils/install';
import { cephStorageLabel } from '../../selectors';
import { useSelectList } from '@console/shared/src/hooks/select-list';

import './ocs-install.scss';

const tableColumnClasses = [
Expand Down Expand Up @@ -50,25 +52,27 @@ const getColumns = () => {
];
};

const getSelected = (selected: NodeKind[], nodeUID: string) =>
selected.map((node) => node.metadata.uid).includes(nodeUID);
const isSelected = (selected: string[], nodeUID: string) => selected.some((uid) => uid === nodeUID);

type GetRows = ({
componentProps,
customData,
}: {
componentProps: { data: NodeKind[] };
customData: {
selectedNodes: NodeKind[];
setSelectedNodes: React.Dispatch<React.SetStateAction<NodeKind[]>>;
visibleRows: NodeKind[];
setVisibleRows: React.Dispatch<React.SetStateAction<NodeKind[]>>;
};
}) => NodeTableRow[];
componentProps,
}: {
componentProps: { data: NodeKind[] };
},
visibleRows: string[],
setVisibleRows: React.Dispatch<React.SetStateAction<string[]>>,
selectedNodes: string[],
setSelectedNodes: React.Dispatch<React.SetStateAction<string[]>>,
) => NodeTableRow[];

const getRows: GetRows = ({ componentProps, customData }) => {
const getRows: GetRows = (
{ componentProps },
visibleRows,
setVisibleRows,
selectedNodes,
setSelectedNodes,
) => {
const { data } = componentProps;
const { selectedNodes, setSelectedNodes, setVisibleRows, visibleRows } = customData;

const filteredData = data.filter((node: NodeKind) => hasOCSTaint(node) || !hasTaints(node));

Expand Down Expand Up @@ -96,58 +100,37 @@ const getRows: GetRows = ({ componentProps, customData }) => {
return {
cells,
selected: _.isArray(selectedNodes)
? getSelected(selectedNodes, node.metadata.uid)
? isSelected(selectedNodes, node.metadata.uid)
: hasLabel(node, cephStorageLabel),
props: {
id: node.metadata.uid,
},
};
});

if (!_.isEqual(filteredData, visibleRows)) {
setVisibleRows(filteredData);
if (!selectedNodes && filteredData.length) {
const preSelected = filteredData.filter((row) => hasLabel(row, cephStorageLabel));
const uids = filteredData.map((n) => n.metadata.uid);

if (!_.isEqual(uids, visibleRows)) {
setVisibleRows(uids);
if (!selectedNodes.length && filteredData.length) {
const preSelected = filteredData
.filter((row) => hasLabel(row, cephStorageLabel))
.map((row) => row.metadata.uid);
setSelectedNodes(preSelected);
}
}

return rows;
};

const NodeTable: React.FC<NodeTableProps> = (props) => {
const { selectedNodes, setSelectedNodes, visibleRows } = props.customData;

const onSelect: OnSelect = (_event, isSelected, rowIndex, rowData) => {
const selectedUIDs = selectedNodes?.map((node) => node.metadata.uid) ?? [];
const visibleUIDs = visibleRows?.map((row) => row.metadata.uid);
if (rowIndex === -1) {
if (isSelected) {
const uniqueUIDs = _.uniq([...visibleUIDs, ...selectedUIDs]);
setSelectedNodes(
_.uniqBy(
[...visibleRows, ...selectedNodes].filter((node) =>
uniqueUIDs.includes(node.metadata.uid),
),
(n) => n.metadata.uid,
),
);
} else {
setSelectedNodes(
_.uniqBy(
selectedNodes.filter((node) => !visibleUIDs.includes(node.metadata.uid)),
(n) => n.metadata.uid,
),
);
}
} else {
const uniqueUIDs = _.xor(selectedUIDs, [rowData.props.id]);
const data = _.uniqBy(
[...visibleRows, ...selectedNodes].filter((node) => uniqueUIDs.includes(node.metadata.uid)),
(n) => n.metadata.uid,
);
setSelectedNodes(data);
}
};
const [visibleRows, setVisibleRows] = React.useState<string[]>();

const {
onSelect,
selectedRows: selectedNodes,
setSelectedRows: setSelectedNodes,
} = useSelectList<NodeKind>(props.data, visibleRows, props.customData.onRowSelected);

return (
<>
Expand All @@ -156,7 +139,9 @@ const NodeTable: React.FC<NodeTableProps> = (props) => {
aria-label="Node Table"
data-test-id="select-nodes-table"
{...props}
Rows={getRows}
Rows={(props) =>
getRows(props, visibleRows, setVisibleRows, selectedNodes, setSelectedNodes)
}
Header={getColumns}
virtualize={false}
onSelect={onSelect}
Expand Down
1 change: 1 addition & 0 deletions frontend/packages/console-shared/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './fullscreen';
export * from './scroll';
export * from './plugins-overview-tab-section';
export * from './debounce';
export * from './select-list';
43 changes: 43 additions & 0 deletions frontend/packages/console-shared/src/hooks/select-list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as React from 'react';
import * as _ from 'lodash';
import { K8sResourceCommon } from '@console/internal/module/k8s';
import { IRowData, OnSelect } from '@patternfly/react-table';

const getAllSelectedRows =<R extends K8sResourceCommon>(data: R[], selectedRows: string[]) => data.filter((row) => selectedRows.includes(row.metadata.uid));

export const useSelectList = <R extends K8sResourceCommon>(
data: R[],
visibleRows: string[],
onRowSelected: (rows: R[]) => void,
): {
onSelect: OnSelect,
selectedRows: string[];
setSelectedRows: React.Dispatch<React.SetStateAction<string[]>>;
} => {
const [selectedRows, setSelectedRows] = React.useState<string[]>([]);

const onSelect = (
_event: React.MouseEvent,
isSelected: boolean,
rowIndex: number,
rowData: IRowData,
) => {
let uniqueUIDs = [];
if (rowIndex === -1) {
if (isSelected) {
uniqueUIDs = _.uniq([...visibleRows, ...selectedRows]);
} else {
uniqueUIDs = _.uniq(selectedRows.filter((uid) => !visibleRows.includes(uid)));
}
} else {
uniqueUIDs = _.xor(selectedRows, [rowData?.props?.id]);
}
setSelectedRows(uniqueUIDs);
onRowSelected(getAllSelectedRows(data, uniqueUIDs));
};
return {
onSelect,
selectedRows,
setSelectedRows,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,14 @@ import {
} from '@console/internal/components/utils';
import { history } from '@console/internal/components/utils/router';
import { ListPage } from '@console/internal/components/factory';
import { k8sCreate, referenceFor } from '@console/internal/module/k8s';
import { k8sCreate, referenceFor, NodeKind } from '@console/internal/module/k8s';
import { NodeModel } from '@console/internal/models';
import { useSelectList } from '@console/shared/src/hooks/select-list';
import { ClusterServiceVersionModel } from '@console/operator-lifecycle-manager';
import { LocalVolumeSetModel } from '../../models';
import { NodesSelectionList } from './nodes-selection-list';
import {
RowUIDMap,
LocalVolumeSetKind,
DeviceType,
DiskType,
DeviceMechanicalProperty,
} from './types';
import { getSelectedNodeUIDs } from './utils';
import { LocalVolumeSetKind, DeviceType, DiskType, DeviceMechanicalProperty } from './types';
import { getName } from '@console/shared';
import './create-local-volume-set.scss';

const volumeModeDropdownItems = {
Expand All @@ -56,11 +51,15 @@ const CreateLocalVolumeSet: React.FC = withHandlePromise<CreateLocalVolumeSetPro
const [volumeType, setVolumeType] = React.useState<DiskType>(DiskType.SSD);
const [volumeMode, setVolumeMode] = React.useState(volumeModeDropdownItems.Block);
const [maxVolumeLimit, setMaxVolumeLimit] = React.useState('');
const [rows, setRows] = React.useState<RowUIDMap>({});
const [allSelected, setAllSelected] = React.useState<boolean>(null);
const [visibleRows, setVisibleRows] = React.useState<NodeKind[]>([]);

const { ns, appName } = match.params;
const modelName = LocalVolumeSetModel.label;
const {
onSelect,
selectedRows: selectedNodes,
setSelectedRows: setSelectedNodes,
} = useSelectList<NodeKind>(visibleRows);

const toggleShowNodesList = () => {
setShowNodesList(!showNodesList);
Expand All @@ -85,13 +84,12 @@ const CreateLocalVolumeSet: React.FC = withHandlePromise<CreateLocalVolumeSetPro
};

if (showNodesList) {
const selectedNodesUID = getSelectedNodeUIDs(rows);
const selectedNodes = selectedNodesUID.map((uid) => rows[uid].props.data.metadata.name);
const selectedNodesNames = selectedNodes.map((node) => getName(node));
requestData.spec.nodeSelector = {
nodeSelectorTerms: [
{
matchExpressions: [
{ key: 'kubernetes.io/hostname', operator: 'In', values: [...selectedNodes] },
{ key: 'kubernetes.io/hostname', operator: 'In', values: [...selectedNodesNames] },
],
},
],
Expand Down Expand Up @@ -170,10 +168,11 @@ const CreateLocalVolumeSet: React.FC = withHandlePromise<CreateLocalVolumeSetPro
</FormGroup>
{showNodesList && (
<ListPage
customData={{ rows, setRows, allSelected, setAllSelected }}
customData={{ selectedNodes, setSelectedNodes, visibleRows, setVisibleRows }}
showTitle={false}
kind={NodeModel.kind}
ListComponent={NodesSelectionList}
onSelect={onSelect}
/>
)}
<FormGroup label="Volume Type" fieldId="create-lvs--volume-type-dropdown">
Expand Down

0 comments on commit 32500da

Please sign in to comment.