Skip to content

Commit

Permalink
Create a Wrapper StandardPageWithSelection
Browse files Browse the repository at this point in the history
Signed-off-by: yzamir <[email protected]>
  • Loading branch information
yaacov committed Feb 6, 2024
1 parent 0fa192a commit ab20a60
Show file tree
Hide file tree
Showing 11 changed files with 215 additions and 72 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC, useState } from 'react';
import React, { FC } from 'react';

import {
DefaultHeader,
Expand Down Expand Up @@ -59,6 +59,16 @@ export interface IdBasedSelectionProps<T> {
* @returns true if items can be selected, false otherwise
*/
canSelect: (item: T) => boolean;

/**
* onSelect is called when selection changes
*/
onSelect: (selectedIds: string[]) => void;

/**
* Selected ids
*/
selectedIds: string[];
}

export type GlobalActionWithSelection<T> = GlobalActionToolbarProps<T> & {
Expand All @@ -71,18 +81,28 @@ export type GlobalActionWithSelection<T> = GlobalActionToolbarProps<T> & {
* 1. IDs provided with toId() function are unique and constant in time
* 2. check box status at row level does not depend from other rows and can be calculated from the item via canSelect() function
*/
export function withIdBasedSelection<T>({ toId, canSelect }: IdBasedSelectionProps<T>) {
export function withIdBasedSelection<T>({
toId,
canSelect,
onSelect,
selectedIds,
}: IdBasedSelectionProps<T>) {
const Enhanced = (props: StandardPageProps<T>) => {
const [selectedIds, setSelectedIds]: [string[], (selected: string[]) => void] = useState([]);
const isSelected = (item: T) => selectedIds.includes(toId(item));

const toggleSelectFor = (items: T[]) => {
const ids = items.map(toId);
const allSelected = ids.every((id) => selectedIds.includes(id));
setSelectedIds([
const newSelectedIds = [
...selectedIds.filter((it) => !ids.includes(it)),
...(allSelected ? [] : ids),
]);
];

if (onSelect) {
onSelect(newSelectedIds);
}
};

return (
<StandardPage
{...props}
Expand Down Expand Up @@ -111,3 +131,27 @@ export function withIdBasedSelection<T>({ toId, canSelect }: IdBasedSelectionPro
Enhanced.displayName = 'StandardPageWithSelection';
return Enhanced;
}

export interface StandardPageWithSelectionProps<T> extends StandardPageProps<T> {
toId?: (item: T) => string;
canSelect?: (item: T) => boolean;
onSelect?: (selectedIds: string[]) => void;
selectedIds?: string[];
}

export function StandardPageWithSelection<T>(props: StandardPageWithSelectionProps<T>) {
const { toId, canSelect = () => true, onSelect, selectedIds, ...rest } = props;

if (onSelect && (!toId || !selectedIds)) {
throw new Error('Missing required properties: toId, selectedIds');
}

const EnhancedStandardPage = withIdBasedSelection<T>({
toId,
canSelect,
onSelect,
selectedIds,
});

return onSelect ? <EnhancedStandardPage {...rest} /> : <StandardPage {...props} />;
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
import React, { FC, useState } from 'react';
import StandardPage from 'src/components/page/StandardPage';
import { GlobalActionWithSelection, withIdBasedSelection } from 'src/components/page/withSelection';
import {
GlobalActionWithSelection,
StandardPageWithSelection,
StandardPageWithSelectionProps,
} from 'src/components/page/StandardPageWithSelection';
import { useProviderInventory } from 'src/modules/Providers/hooks';
import { useModal } from 'src/modules/Providers/modals';
import { useForkliftTranslation } from 'src/utils/i18n';

import { loadUserSettings, ResourceFieldFactory } from '@kubev2v/common';
import {
HostModelGroupVersionKind,
V1beta1Host,
V1beta1Provider,
VSphereHost,
} from '@kubev2v/types';
import { HostModelGroupVersionKind, V1beta1Host, VSphereHost } from '@kubev2v/types';
import { useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk';
import { Button, ToolbarItem } from '@patternfly/react-core';

import { VSphereNetworkModal } from './modals/VSphereNetworkModal';
import { InventoryHostPair, matchHostsToInventory } from './utils/helpers';
import { SelectNetworkButton } from './components';
import { ProviderHostsProps } from './ProviderHosts';
import { VSphereHostsCells } from './VSphereHostsRow';

Expand Down Expand Up @@ -60,19 +56,18 @@ export const hostsFieldsMetadataFactory: ResourceFieldFactory = (t) => [
},
];

const PageWithSelection = withIdBasedSelection<InventoryHostPair>({
toId: (item: InventoryHostPair) => item.inventory.id,
canSelect: (item: InventoryHostPair) => item?.inventory?.networkAdapters?.length > 0,
});
const PageWithSelection = StandardPageWithSelection<InventoryHostPair>;
type PageWithSelectionProps = StandardPageWithSelectionProps<InventoryHostPair>;

export const VSphereHostsList: FC<ProviderHostsProps> = ({ obj }) => {
const { t } = useForkliftTranslation();

const [userSettings] = useState(() => loadUserSettings({ pageId: 'ProviderHosts' }));

const { provider, permissions } = obj;
const { namespace } = provider?.metadata || {};

const [selectedIds, setSelectedIds] = useState([]);
const [userSettings] = useState(() => loadUserSettings({ pageId: 'ProviderHosts' }));

const {
inventory: hostsInventory,
loading,
Expand All @@ -91,45 +86,29 @@ export const VSphereHostsList: FC<ProviderHostsProps> = ({ obj }) => {

const hostsData = matchHostsToInventory(hostsInventory, hosts, provider);

const Page = permissions?.canPatch ? PageWithSelection : StandardPage<InventoryHostPair>;
const actions: FC<GlobalActionWithSelection<InventoryHostPair>>[] = permissions?.canPatch
? [({ selectedIds }) => <SelectNetworkBtn {...{ hostsData, provider, selectedIds }} />]
? [({ selectedIds }) => <SelectNetworkButton {...{ hostsData, provider, selectedIds }} />]
: [];

return (
<Page
data-testid="hosts-list"
dataSource={[hostsData || [], !loading, error]}
CellMapper={VSphereHostsCells}
fieldsMetadata={hostsFieldsMetadataFactory(t)}
namespace={namespace}
title={t('Hosts')}
userSettings={userSettings}
GlobalActionToolbarItems={actions}
/>
);
};
const props: PageWithSelectionProps = {
dataSource: [hostsData || [], !loading, error],
CellMapper: VSphereHostsCells,
fieldsMetadata: hostsFieldsMetadataFactory(t),
namespace: namespace,
title: t('Hosts'),
userSettings: userSettings,
GlobalActionToolbarItems: actions,
};

const SelectNetworkBtn: FC<{
selectedIds: string[];
provider: V1beta1Provider;
hostsData: InventoryHostPair[];
}> = ({ selectedIds, provider, hostsData }) => {
const { t } = useForkliftTranslation();
const { showModal } = useModal();
return (
<ToolbarItem>
<Button
variant="secondary"
onClick={() =>
showModal(
<VSphereNetworkModal provider={provider} data={hostsData} selected={selectedIds} />,
)
}
isDisabled={!selectedIds?.length}
>
{t('Select migration network')}
</Button>
</ToolbarItem>
);
const extendedProps: PageWithSelectionProps = permissions?.canPatch
? {
...props,
toId: (item: InventoryHostPair) => item.inventory.id,
canSelect: (item: InventoryHostPair) => item?.inventory?.networkAdapters?.length > 0,
onSelect: setSelectedIds,
selectedIds: selectedIds,
}
: props;

return <PageWithSelection {...extendedProps} />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React, { FC } from 'react';
import { useModal } from 'src/modules/Providers/modals';
import { useForkliftTranslation } from 'src/utils/i18n';

import { V1beta1Provider } from '@kubev2v/types';
import { Button, ToolbarItem } from '@patternfly/react-core';

import { VSphereNetworkModal } from '../modals';
import { InventoryHostPair } from '../utils';

/**
* `SelectNetworkButton` is a functional component that renders a button for selecting a migration network.
* When clicked, it opens a modal dialog for choosing the network.
*
* @component
* @example
* // Usage within a parent component
* <SelectNetworkButton
* selectedIds={['host1', 'host2']}
* provider={myProvider}
* hostsData={myInventoryHostPairs}
* />
*
* @param {Object} props - The properties passed to the component.
* @param {string[]} props.selectedIds - An array of host IDs that are selected for migration.
* @param {V1beta1Provider} props.provider - The provider object containing details about the provider.
* @param {InventoryHostPair[]} props.hostsData - An array of inventory host pairs for displaying in the modal.
*/
export const SelectNetworkButton: FC<{
selectedIds: string[];
provider: V1beta1Provider;
hostsData: InventoryHostPair[];
}> = ({ selectedIds, provider, hostsData }) => {
const { t } = useForkliftTranslation();
const { showModal } = useModal();

return (
<ToolbarItem>
<Button
variant="secondary"
onClick={() =>
showModal(
<VSphereNetworkModal provider={provider} data={hostsData} selected={selectedIds} />,
)
}
isDisabled={!selectedIds?.length}
>
{t('Select migration network')}
</Button>
</ToolbarItem>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export * from './LinkSpeedCellRenderer';
export * from './MTUCellRenderer';
export * from './NameCellRenderer';
export * from './NetworkCellRenderer';
export * from './SelectNetworkButton';
// @endindex
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ export const OVirtVirtualMachinesList: React.FC<ProviderVirtualMachinesProps> =
obj,
loaded,
loadError,
onSelect,
initialSelectedIds,
}) => (
<ProviderVirtualMachinesList
title={title}
Expand All @@ -102,5 +104,7 @@ export const OVirtVirtualMachinesList: React.FC<ProviderVirtualMachinesProps> =
cellMapper={OVirtVirtualMachinesCells}
fieldsMetadataFactory={oVirtVmFieldsMetadataFactory}
pageId="OVirtVirtualMachinesList"
onSelect={onSelect}
initialSelectedIds={initialSelectedIds}
/>
);
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ export const OpenShiftVirtualMachinesList: React.FC<ProviderVirtualMachinesProps
obj,
loaded,
loadError,
onSelect,
initialSelectedIds,
}) => (
<ProviderVirtualMachinesList
title={title}
Expand All @@ -87,5 +89,7 @@ export const OpenShiftVirtualMachinesList: React.FC<ProviderVirtualMachinesProps
cellMapper={OpenShiftVirtualMachinesCells}
fieldsMetadataFactory={openShiftVmFieldsMetadataFactory}
pageId="OpenShiftVirtualMachinesList"
onSelect={onSelect}
initialSelectedIds={initialSelectedIds}
/>
);
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ export const OpenStackVirtualMachinesList: React.FC<ProviderVirtualMachinesProps
obj,
loaded,
loadError,
onSelect,
initialSelectedIds,
}) => (
<ProviderVirtualMachinesList
title={title}
Expand All @@ -118,5 +120,7 @@ export const OpenStackVirtualMachinesList: React.FC<ProviderVirtualMachinesProps
cellMapper={OpenStackVirtualMachinesCells}
fieldsMetadataFactory={openStackVmFieldsMetadataFactory}
pageId="OpenStackVirtualMachinesList"
onSelect={onSelect}
initialSelectedIds={initialSelectedIds}
/>
);
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ export const OvaVirtualMachinesList: React.FC<ProviderVirtualMachinesProps> = ({
obj,
loaded,
loadError,
onSelect,
initialSelectedIds,
}) => (
<ProviderVirtualMachinesList
title={title}
Expand All @@ -56,5 +58,7 @@ export const OvaVirtualMachinesList: React.FC<ProviderVirtualMachinesProps> = ({
cellMapper={OvaVirtualMachinesCells}
fieldsMetadataFactory={ovaVmFieldsMetadataFactory}
pageId="OvaVirtualMachinesList"
onSelect={onSelect}
initialSelectedIds={initialSelectedIds}
/>
);
Loading

0 comments on commit ab20a60

Please sign in to comment.