Wizard: Update the Review step to have expandable sections

Fixes #919.

This updates the Review step as per proposed mocks.

The tabs were replaced by expandable sections containing all the information relevant to a specific step.
This commit is contained in:
regexowl 2023-03-01 09:56:14 +01:00 committed by Klara Simickova
parent 49b3f04ea2
commit 254ba7afac
6 changed files with 836 additions and 525 deletions

View file

@ -47,7 +47,7 @@
}
.pf-u-min-width {
--pf-u-min-width--MinWidth: 11ch;
--pf-u-min-width--MinWidth: 18ch;
}
.pf-u-max-width {

View file

@ -2,89 +2,36 @@ import React, { useEffect, useState } from 'react';
import useFormApi from '@data-driven-forms/react-form-renderer/use-form-api';
import {
Button,
DescriptionList,
DescriptionListDescription,
DescriptionListGroup,
DescriptionListTerm,
List,
ListItem,
Popover,
Spinner,
Tab,
Tabs,
TabTitleText,
ExpandableSection,
Text,
TextContent,
TextList,
TextListItem,
TextListItemVariants,
TextListVariants,
TextVariants,
} from '@patternfly/react-core';
import { HelpIcon } from '@patternfly/react-icons';
import {
TableComposable,
Tbody,
Td,
Th,
Thead,
Tr,
} from '@patternfly/react-table';
import PropTypes from 'prop-types';
ContentList,
FSCList,
ImageDetailsList,
ImageOutputList,
RegisterLaterList,
RegisterNowList,
TargetEnvAWSList,
TargetEnvAzureList,
TargetEnvGCPList,
TargetEnvOtherList,
} from './ReviewStepTextLists';
import ActivationKeyInformation from './ActivationKeyInformation';
import { RELEASES, UNIT_GIB, UNIT_MIB } from '../../../constants';
import { useGetAWSSourcesQuery } from '../../../store/apiSlice';
import isRhel from '../../../Utilities/isRhel';
import { googleAccType } from '../steps/googleCloud';
const FSReviewTable = ({ ...props }) => {
return (
<TableComposable
aria-label="File system configuration table"
variant="compact"
>
<Thead>
<Tr>
<Th>Mount point</Th>
<Th>File system type</Th>
<Th>Minimum size</Th>
</Tr>
</Thead>
<Tbody data-testid="file-system-configuration-tbody-review">
{props.fsc.map((r, ri) => (
<Tr key={ri}>
<Td className="pf-m-width-30">{r.mountpoint}</Td>
<Td className="pf-m-width-30">xfs</Td>
<Td className="pf-m-width-30">
{r.size}{' '}
{r.unit === UNIT_GIB
? 'GiB'
: r.unit === UNIT_MIB
? 'MiB'
: 'KiB'}
</Td>
</Tr>
))}
</Tbody>
</TableComposable>
);
};
FSReviewTable.propTypes = {
fsc: PropTypes.arrayOf(PropTypes.object).isRequired,
};
const ReviewStep = () => {
const [activeTabKey, setActiveTabKey] = useState(0);
const [minSize, setMinSize] = useState();
const [isExpandedImageOutput, setIsExpandedImageOutput] = useState(false);
const [isExpandedTargetEnvs, setIsExpandedTargetEnvs] = useState(false);
const [isExpandedFSC, setIsExpandedFSC] = useState(false);
const [isExpandedContent, setIsExpandedContent] = useState(false);
const [isExpandedRegistration, setIsExpandedRegistration] = useState(false);
const [isExpandedImageDetail, setIsExpandedImageDetail] = useState(false);
const { change, getState } = useFormApi();
const { data: awsSources, isSuccess: isSuccessAWSSources } =
useGetAWSSourcesQuery();
useEffect(() => {
const registerSystem = getState()?.values?.['register-system'];
if (registerSystem?.startsWith('register-now')) {
@ -94,411 +41,115 @@ const ReviewStep = () => {
change('subscription-organization-id', id);
})();
}
if (
getState()?.values?.['file-system-config-radio'] === 'manual' &&
getState()?.values?.['file-system-configuration']
) {
let size = 0;
for (const fsc of getState().values['file-system-configuration']) {
size += fsc.size * fsc.unit;
}
size = (size / UNIT_GIB).toFixed(1);
if (size < 1) {
setMinSize(`Less than 1 GiB`);
} else {
setMinSize(`${size} GiB`);
}
}
});
const handleTabClick = (event, tabIndex) => {
setActiveTabKey(tabIndex);
};
const onToggleImageOutput = (isExpandedImageOutput) =>
setIsExpandedImageOutput(isExpandedImageOutput);
const onToggleTargetEnvs = (isExpandedTargetEnvs) =>
setIsExpandedTargetEnvs(isExpandedTargetEnvs);
const onToggleFSC = (isExpandedFSC) => setIsExpandedFSC(isExpandedFSC);
const onToggleContent = (isExpandedContent) =>
setIsExpandedContent(isExpandedContent);
const onToggleRegistration = (isExpandedRegistration) =>
setIsExpandedRegistration(isExpandedRegistration);
const onToggleImageDetail = (isExpandedImageDetail) =>
setIsExpandedImageDetail(isExpandedImageDetail);
return (
<>
<Text>
Review the information and click &quot;Create image&quot; to create the
image using the following criteria.
</Text>
<DescriptionList isCompact>
<DescriptionListGroup>
{getState()?.values?.['image-name'] && (
<>
<DescriptionListTerm>Image name</DescriptionListTerm>
<DescriptionListDescription>
{getState()?.values?.['image-name']}
</DescriptionListDescription>
</>
)}
<DescriptionListTerm>Release</DescriptionListTerm>
<DescriptionListDescription>
{RELEASES.get(getState()?.values?.release)}
</DescriptionListDescription>
</DescriptionListGroup>
</DescriptionList>
<Tabs
isFilled
activeKey={activeTabKey}
onSelect={handleTabClick}
className="pf-u-w-75"
<ExpandableSection
toggleContent={'Image output'}
onToggle={onToggleImageOutput}
isExpanded={isExpandedImageOutput}
isIndented
data-testid="image-output-expandable"
>
<Tab
eventKey={0}
title={<TabTitleText>Target environment</TabTitleText>}
data-testid="tab-target"
autoFocus
>
<List isPlain iconSize="large">
{getState()?.values?.['target-environment']?.aws && (
<ListItem
icon={
<img
className="provider-icon"
src="/apps/frontend-assets/partners-icons/aws.svg"
/>
}
>
<TextContent>
<Text component={TextVariants.h3}>Amazon Web Services</Text>
<TextList component={TextListVariants.dl}>
<TextListItem
component={TextListItemVariants.dt}
className="pf-u-min-width"
>
{getState()?.values?.['aws-target-type'] ===
'aws-target-type-source'
? 'Source'
: null}
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{isSuccessAWSSources &&
getState()?.values?.['aws-target-type'] ===
'aws-target-type-source'
? awsSources.find(
(source) =>
source.id ===
getState()?.values?.['aws-sources-select']
)?.name
: null}
</TextListItem>
<TextListItem
component={TextListItemVariants.dt}
className="pf-u-min-width"
>
Account ID
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{isSuccessAWSSources &&
getState()?.values?.['aws-target-type'] ===
'aws-target-type-source'
? awsSources.find(
(source) =>
source.id ===
getState()?.values?.['aws-sources-select']
)?.account_id
: getState()?.values?.['aws-account-id']}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Default Region
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
us-east-1
</TextListItem>
</TextList>
</TextContent>
</ListItem>
)}
{getState()?.values?.['target-environment']?.gcp && (
<ListItem
className="pf-c-list__item pf-u-mt-md"
icon={
<img
className="provider-icon"
src="/apps/frontend-assets/partners-icons/google-cloud-short.svg"
/>
}
>
<TextContent>
<Text component={TextVariants.h3}>Google Cloud Platform</Text>
<TextList component={TextListVariants.dl}>
<TextListItem component={TextListItemVariants.dt}>
{
googleAccType?.[
getState()?.values?.['google-account-type']
]
}
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{getState()?.values?.['google-email'] ||
getState()?.values?.['google-domain']}
</TextListItem>
</TextList>
</TextContent>
</ListItem>
)}
{getState()?.values?.['target-environment']?.azure && (
<ListItem
className="pf-c-list__item pf-u-mt-md"
icon={
<img
className="provider-icon"
src="/apps/frontend-assets/partners-icons/microsoft-azure-short.svg"
/>
}
>
<TextContent>
<Text component={TextVariants.h3}>Microsoft Azure</Text>
<TextList component={TextListVariants.dl}>
<TextListItem component={TextListItemVariants.dt}>
Subscription ID
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{getState()?.values?.['azure-subscription-id']}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Tenant ID
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{getState()?.values?.['azure-tenant-id']}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Resource group
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{getState()?.values?.['azure-resource-group']}
</TextListItem>
</TextList>
</TextContent>
</ListItem>
)}
{getState()?.values?.['target-environment']?.vsphere && (
<ListItem>
<TextContent>
<Text component={TextVariants.h3}>VMWare</Text>
</TextContent>
</ListItem>
)}
{getState()?.values?.['target-environment']?.['guest-image'] && (
<ListItem>
<TextContent>
<Text component={TextVariants.h3}>
Virtualization - Guest image
</Text>
</TextContent>
</ListItem>
)}
{getState()?.values?.['target-environment']?.[
'image-installer'
] && (
<ListItem>
<TextContent>
<Text component={TextVariants.h3}>
Bare metal - Installer
</Text>
</TextContent>
</ListItem>
)}
</List>
</Tab>
{isRhel(getState()?.values?.release) && (
<Tab
eventKey={1}
title={<TabTitleText>Registration</TabTitleText>}
data-testid="tab-registration"
>
{getState()?.values?.['register-system'] === 'register-later' && (
<TextContent>
<TextList component={TextListVariants.dl}>
<TextListItem component={TextListItemVariants.dt}>
Registration type
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
Register the system later
</TextListItem>
</TextList>
</TextContent>
)}
{getState()?.values?.['register-system']?.startsWith(
'register-now'
) && (
<TextContent>
<TextList component={TextListVariants.dl}>
<TextListItem component={TextListItemVariants.dt}>
Registration type
</TextListItem>
<TextListItem
component={TextListItemVariants.dd}
data-testid="review-registration"
>
<TextList isPlain>
{getState()?.values?.['register-system']?.startsWith(
'register-now'
) && (
<TextListItem>
Register with Red Hat Subscription Manager (RHSM)
<br />
</TextListItem>
)}
{(getState()?.values?.['register-system'] ===
'register-now-insights' ||
getState()?.values?.['register-system'] ===
'register-now-rhc') && (
<TextListItem>
Connect to Red Hat Insights
<br />
</TextListItem>
)}
{getState()?.values?.['register-system'] ===
'register-now-rhc' && (
<TextListItem>
Use remote host configuration (RHC) utility
<br />
</TextListItem>
)}
</TextList>
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Activation key
<Popover
bodyContent={
<TextContent>
<Text>
Activation keys enable you to register a system with
appropriate subscriptions, system purpose, and
repositories attached.
<br />
<br />
If using an activation key with command line
registration, you must provide your
organization&apos;s ID. Your organization&apos;s ID
is{' '}
{getState()?.values?.[
'subscription-organization-id'
] !== undefined ? (
getState()?.values?.[
'subscription-organization-id'
]
) : (
<Spinner size="md" />
)}
</Text>
</TextContent>
}
>
<Button
variant="plain"
aria-label="About activation key"
className="pf-u-pl-sm pf-u-pt-0 pf-u-pb-0"
isSmall
>
<HelpIcon />
</Button>
</Popover>
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
<ActivationKeyInformation />
</TextListItem>
</TextList>
</TextContent>
)}
</Tab>
<ImageOutputList />
</ExpandableSection>
<ExpandableSection
toggleContent={'Target environments'}
onToggle={onToggleTargetEnvs}
isExpanded={isExpandedTargetEnvs}
isIndented
data-testid="target-environments-expandable"
>
{getState()?.values?.['target-environment']?.aws && (
<TargetEnvAWSList />
)}
<Tab
eventKey={2}
title={<TabTitleText>System configuration</TabTitleText>}
data-testid="tab-system"
>
{getState()?.values?.['target-environment']?.gcp && (
<TargetEnvGCPList />
)}
{getState()?.values?.['target-environment']?.azure && (
<TargetEnvAzureList />
)}
{getState()?.values?.['target-environment']?.vsphere && (
<TextContent>
<Text component={TextVariants.h3}>File system configuration</Text>
<TextList component={TextListVariants.dl}>
<TextListItem component={TextListItemVariants.dt}>
Partitioning
</TextListItem>
<TextListItem
component={TextListItemVariants.dd}
data-testid="partitioning-auto-manual"
>
{getState()?.values?.['file-system-config-radio'] === 'manual'
? 'Manual'
: 'Automatic'}
{getState()?.values?.['file-system-config-radio'] ===
'manual' && (
<>
{' '}
<Popover
position="bottom"
headerContent="Partitions"
hasAutoWidth
minWidth="30rem"
bodyContent={
<FSReviewTable
fsc={getState().values['file-system-configuration']}
/>
}
>
<Button
data-testid="file-system-configuration-popover"
variant="link"
aria-label="File system configuration info"
aria-describedby="file-system-configuration-info"
className="pf-u-pt-0 pf-u-pb-0"
>
View partitions
</Button>
</Popover>
</>
)}
</TextListItem>
{getState()?.values?.['file-system-config-radio'] ===
'manual' && (
<>
<TextListItem component={TextListItemVariants.dt}>
Image size (minimum)
<Popover
hasAutoWidth
bodyContent={
<TextContent>
<Text>
Image Builder may extend this size based on
requirements, selected packages, and configurations.
</Text>
</TextContent>
}
>
<Button
variant="plain"
aria-label="File system configuration info"
aria-describedby="file-system-configuration-info"
className="pf-c-form__group-label-help"
>
<HelpIcon />
</Button>
</Popover>
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{minSize}
</TextListItem>
</>
)}
</TextList>
<Text component={TextVariants.h3}>Additional packages</Text>
<TextList component={TextListVariants.dl}>
<TextListItem component={TextListItemVariants.dt}>
Chosen
</TextListItem>
<TextListItem
component={TextListItemVariants.dd}
data-testid="chosen-packages-count"
>
{getState()?.values?.['selected-packages']?.length || 0}
</TextListItem>
</TextList>
<Text component={TextVariants.h3}>VMWare (.vmdk)</Text>
<TargetEnvOtherList />
</TextContent>
</Tab>
</Tabs>
)}
{getState()?.values?.['target-environment']?.['guest-image'] && (
<TextContent>
<Text component={TextVariants.h3}>
Virtualization - Guest image (.qcow2)
</Text>
<TargetEnvOtherList />
</TextContent>
)}
{getState()?.values?.['target-environment']?.['image-installer'] && (
<TextContent>
<Text component={TextVariants.h3}>
Bare metal - Installer (.iso)
</Text>
<TargetEnvOtherList />
</TextContent>
)}
</ExpandableSection>
<ExpandableSection
toggleContent={'File system configuration'}
onToggle={onToggleFSC}
isExpanded={isExpandedFSC}
isIndented
data-testid="file-system-configuration-expandable"
>
<FSCList />
</ExpandableSection>
<ExpandableSection
toggleContent={'Content'}
onToggle={onToggleContent}
isExpanded={isExpandedContent}
isIndented
data-testid="content-expandable"
>
<ContentList />
</ExpandableSection>
{isRhel(getState()?.values?.release) && (
<ExpandableSection
toggleContent={'Registration'}
onToggle={onToggleRegistration}
isExpanded={isExpandedRegistration}
isIndented
data-testid="registration-expandable"
>
{getState()?.values?.['register-system'] === 'register-later' && (
<RegisterLaterList />
)}
{getState()?.values?.['register-system']?.startsWith(
'register-now'
) && <RegisterNowList />}
</ExpandableSection>
)}
{getState()?.values?.['image-name'] && (
<ExpandableSection
toggleContent={'Image details'}
onToggle={onToggleImageDetail}
isExpanded={isExpandedImageDetail}
isIndented
data-testid="image-details-expandable"
>
<ImageDetailsList />
</ExpandableSection>
)}
</>
);
};

View file

@ -0,0 +1,119 @@
import React from 'react';
import { useFormApi } from '@data-driven-forms/react-form-renderer';
import { Panel, PanelMain } from '@patternfly/react-core';
import {
TableComposable,
Tbody,
Td,
Th,
Thead,
Tr,
} from '@patternfly/react-table';
import PropTypes from 'prop-types';
import { UNIT_GIB, UNIT_MIB } from '../../../constants';
export const FSReviewTable = () => {
const { getState } = useFormApi();
const fsc = getState().values['file-system-configuration'];
return (
<Panel isScrollable>
<PanelMain maxHeight="30ch">
<TableComposable
aria-label="File system configuration table"
variant="compact"
>
<Thead>
<Tr>
<Th>Mount point</Th>
<Th>File system type</Th>
<Th>Minimum size</Th>
</Tr>
</Thead>
<Tbody data-testid="file-system-configuration-tbody-review">
{fsc.map((partition, partitionIndex) => (
<Tr key={partitionIndex}>
<Td className="pf-m-width-30">{partition.mountpoint}</Td>
<Td className="pf-m-width-30">xfs</Td>
<Td className="pf-m-width-30">
{partition.size}{' '}
{partition.unit === UNIT_GIB
? 'GiB'
: partition.unit === UNIT_MIB
? 'MiB'
: 'KiB'}
</Td>
</Tr>
))}
</Tbody>
</TableComposable>
</PanelMain>
</Panel>
);
};
export const PackagesTable = () => {
const { getState } = useFormApi();
const packages = getState()?.values['selected-packages'];
return (
<Panel isScrollable>
<PanelMain maxHeight="30ch">
<TableComposable aria-label="Packages table" variant="compact">
<Thead>
<Tr>
<Th>Name</Th>
</Tr>
</Thead>
<Tbody data-testid="packages-tbody-review">
{packages.map((pkg, pkgIndex) => (
<Tr key={pkgIndex}>
<Td className="pf-m-width-30">{pkg.name}</Td>
</Tr>
))}
</Tbody>
</TableComposable>
</PanelMain>
</Panel>
);
};
export const RepositoriesTable = () => {
const { getState } = useFormApi();
const repositories = getState()?.values?.['custom-repositories'];
return (
<Panel isScrollable>
<PanelMain maxHeight="30ch">
<TableComposable
aria-label="Custom repositories table"
variant="compact"
>
<Thead>
<Tr>
<Th>Name</Th>
</Tr>
</Thead>
<Tbody data-testid="repositories-tbody-review">
{repositories.map((repo, repoIndex) => (
<Tr key={repoIndex}>
<Td className="pf-m-width-60">{repo.baseurl}</Td>
</Tr>
))}
</Tbody>
</TableComposable>
</PanelMain>
</Panel>
);
};
FSReviewTable.propTypes = {
fsc: PropTypes.arrayOf(PropTypes.object).isRequired,
};
PackagesTable.propTypes = {
packages: PropTypes.arrayOf(PropTypes.object).isRequired,
};
RepositoriesTable.propTypes = {
repositories: PropTypes.arrayOf(PropTypes.object).isRequired,
};

View file

@ -0,0 +1,525 @@
import React, { useEffect, useState } from 'react';
import { useFormApi } from '@data-driven-forms/react-form-renderer';
import {
Button,
Popover,
Spinner,
Text,
TextContent,
TextList,
TextListItem,
TextListVariants,
TextListItemVariants,
TextVariants,
} from '@patternfly/react-core';
import { ExclamationTriangleIcon, HelpIcon } from '@patternfly/react-icons';
import PropTypes from 'prop-types';
import ActivationKeyInformation from './ActivationKeyInformation';
import {
FSReviewTable,
PackagesTable,
RepositoriesTable,
} from './ReviewStepTables';
import { RELEASES, UNIT_GIB } from '../../../constants';
import { useGetAWSSourcesQuery } from '../../../store/apiSlice';
import { googleAccType } from '../steps/googleCloud';
const ExpirationWarning = () => {
return (
<div className="pf-u-mr-sm pf-u-font-size-sm pf-u-warning-color-100">
<ExclamationTriangleIcon /> Expires 14 days after creation
</div>
);
};
export const ImageOutputList = () => {
const { getState } = useFormApi();
return (
<TextContent>
<TextList component={TextListVariants.dl}>
<TextListItem
component={TextListItemVariants.dt}
className="pf-u-min-width"
>
Release
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{RELEASES.get(getState()?.values?.release)}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Architecture
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>x86_64</TextListItem>
</TextList>
<br />
</TextContent>
);
};
export const TargetEnvAWSList = () => {
const { data: awsSources, isSuccess: isSuccessAWSSources } =
useGetAWSSourcesQuery();
const { getState } = useFormApi();
return (
<TextContent>
<Text component={TextVariants.h3}>AWS</Text>
<TextList component={TextListVariants.dl}>
<TextListItem
component={TextListItemVariants.dt}
className="pf-u-min-width"
>
Image type
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
Red Hat hosted image
<br />
<ExpirationWarning />
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Shared to account
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{isSuccessAWSSources &&
getState()?.values?.['aws-target-type'] === 'aws-target-type-source'
? awsSources.find(
(source) =>
source.id === getState()?.values?.['aws-sources-select']
)?.account_id
: getState()?.values?.['aws-account-id']}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
{getState()?.values?.['aws-target-type'] === 'aws-target-type-source'
? 'Source'
: null}
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{isSuccessAWSSources &&
getState()?.values?.['aws-target-type'] === 'aws-target-type-source'
? awsSources.find(
(source) =>
source.id === getState()?.values?.['aws-sources-select']
)?.name
: null}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Default region
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
us-east-1
</TextListItem>
</TextList>
<br />
</TextContent>
);
};
export const TargetEnvGCPList = () => {
const { getState } = useFormApi();
return (
<TextContent>
<Text component={TextVariants.h3}>GCP</Text>
<TextList component={TextListVariants.dl}>
<TextListItem
component={TextListItemVariants.dt}
className="pf-u-min-width"
>
Image type
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
Red Hat hosted image
<br />
<ExpirationWarning />
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Account type
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{googleAccType?.[getState()?.values?.['google-account-type']]}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
{googleAccType?.[getState()?.values?.['google-account-type']] ===
'Domain'
? 'Domain'
: 'Email address'}
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{getState()?.values?.['google-email'] ||
getState()?.values?.['google-domain']}
</TextListItem>
</TextList>
<br />
</TextContent>
);
};
export const TargetEnvAzureList = () => {
const { getState } = useFormApi();
return (
<TextContent>
<Text component={TextVariants.h3}>Microsoft Azure</Text>
<TextList component={TextListVariants.dl}>
<TextListItem
component={TextListItemVariants.dt}
className="pf-u-min-width"
>
Image type
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
Red Hat hosted image
<br />
<ExpirationWarning />
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Azure Tenant ID
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{getState()?.values?.['azure-tenant-id']}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Subscription ID
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{getState()?.values?.['azure-subscription-id']}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Resource group
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{getState()?.values?.['azure-resource-group']}
</TextListItem>
</TextList>
<br />
</TextContent>
);
};
export const TargetEnvOtherList = () => {
return (
<>
<TextList component={TextListVariants.dl}>
<TextListItem
component={TextListItemVariants.dt}
className="pf-u-min-width"
>
Image type
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
Built image will be available for download
</TextListItem>
</TextList>
<br />
</>
);
};
export const FSCList = () => {
const { getState } = useFormApi();
const [minSize, setMinSize] = useState();
useEffect(() => {
if (
getState()?.values?.['file-system-config-radio'] === 'manual' &&
getState()?.values?.['file-system-configuration']
) {
let size = 0;
for (const fsc of getState().values['file-system-configuration']) {
size += fsc.size * fsc.unit;
}
size = (size / UNIT_GIB).toFixed(1);
if (size < 1) {
setMinSize(`Less than 1 GiB`);
} else {
setMinSize(`${size} GiB`);
}
}
});
return (
<TextContent>
<TextList component={TextListVariants.dl}>
<TextListItem
component={TextListItemVariants.dt}
className="pf-u-min-width"
>
Configuration type
</TextListItem>
<TextListItem
component={TextListItemVariants.dd}
data-testid="partitioning-auto-manual"
>
{getState()?.values?.['file-system-config-radio'] === 'manual'
? 'Manual'
: 'Automatic'}
{getState()?.values?.['file-system-config-radio'] === 'manual' && (
<>
{' '}
<Popover
position="bottom"
headerContent="Partitions"
hasAutoWidth
minWidth="30rem"
bodyContent={<FSReviewTable />}
>
<Button
data-testid="file-system-configuration-popover"
variant="link"
aria-label="File system configuration info"
aria-describedby="file-system-configuration-info"
className="pf-u-pt-0 pf-u-pb-0"
>
View partitions
</Button>
</Popover>
</>
)}
</TextListItem>
{getState()?.values?.['file-system-config-radio'] === 'manual' && (
<>
<TextListItem component={TextListItemVariants.dt}>
Image size (minimum)
<Popover
hasAutoWidth
bodyContent={
<TextContent>
<Text>
Image Builder may extend this size based on requirements,
selected packages, and configurations.
</Text>
</TextContent>
}
>
<Button
variant="plain"
aria-label="File system configuration info"
aria-describedby="file-system-configuration-info"
className="pf-c-form__group-label-help"
>
<HelpIcon />
</Button>
</Popover>
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{minSize}
</TextListItem>
</>
)}
</TextList>
<br />
</TextContent>
);
};
export const ContentList = () => {
const { getState } = useFormApi();
return (
<TextContent>
<TextList component={TextListVariants.dl}>
<TextListItem
component={TextListItemVariants.dt}
className="pf-u-min-width"
>
Additional Red Hat
<br />
and 3rd party packages
</TextListItem>
<TextListItem
component={TextListItemVariants.dd}
data-testid="chosen-packages-count"
>
{getState()?.values?.['selected-packages']?.length > 0 ? (
<Popover
position="bottom"
headerContent="Additional packages"
hasAutoWidth
minWidth="30rem"
bodyContent={<PackagesTable />}
>
<Button
variant="link"
aria-label="About packages key"
className="pf-u-p-0"
>
{getState()?.values?.['selected-packages']?.length}
</Button>
</Popover>
) : (
0
)}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Custom repositories
</TextListItem>
<TextListItem
component={TextListItemVariants.dd}
data-testid="custom-repositories-count"
>
{getState()?.values?.['custom-repositories']?.length > 0 ? (
<Popover
position="bottom"
headerContent="Custom repositories"
hasAutoWidth
minWidth="30rem"
bodyContent={<RepositoriesTable />}
>
<Button
variant="link"
aria-label="About custom repositories"
className="pf-u-p-0"
>
{getState()?.values?.['custom-repositories']?.length || 0}
</Button>
</Popover>
) : (
0
)}
</TextListItem>
</TextList>
<br />
</TextContent>
);
};
export const RegisterLaterList = () => {
return (
<TextContent>
<TextList component={TextListVariants.dl}>
<TextListItem
component={TextListItemVariants.dt}
className="pf-u-min-width"
>
Registration type
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
Register the system later
</TextListItem>
</TextList>
<br />
</TextContent>
);
};
export const RegisterNowList = () => {
const { getState } = useFormApi();
return (
<TextContent>
<TextList component={TextListVariants.dl}>
<TextListItem
component={TextListItemVariants.dt}
className="pf-u-min-width"
>
Registration type
</TextListItem>
<TextListItem
component={TextListItemVariants.dd}
data-testid="review-registration"
>
<TextList isPlain>
{getState()?.values?.['register-system']?.startsWith(
'register-now'
) && (
<TextListItem>
Register with Red Hat Subscription Manager (RHSM)
<br />
</TextListItem>
)}
{(getState()?.values?.['register-system'] ===
'register-now-insights' ||
getState()?.values?.['register-system'] ===
'register-now-rhc') && (
<TextListItem>
Connect to Red Hat Insights
<br />
</TextListItem>
)}
{getState()?.values?.['register-system'] === 'register-now-rhc' && (
<TextListItem>
Use remote host configuration (RHC) utility
<br />
</TextListItem>
)}
</TextList>
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Activation key
<Popover
bodyContent={
<TextContent>
<Text>
Activation keys enable you to register a system with
appropriate subscriptions, system purpose, and repositories
attached.
<br />
<br />
If using an activation key with command line registration, you
must provide your organization&apos;s ID. Your
organization&apos;s ID is{' '}
{getState()?.values?.['subscription-organization-id'] !==
undefined ? (
getState()?.values?.['subscription-organization-id']
) : (
<Spinner size="md" />
)}
</Text>
</TextContent>
}
>
<Button
variant="plain"
aria-label="About activation key"
className="pf-u-pl-sm pf-u-pt-0 pf-u-pb-0"
isSmall
>
<HelpIcon />
</Button>
</Popover>
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
<ActivationKeyInformation />
</TextListItem>
</TextList>
<br />
</TextContent>
);
};
export const ImageDetailsList = () => {
const { getState } = useFormApi();
return (
<TextContent>
<TextList component={TextListVariants.dl}>
{getState()?.values?.['image-name'] && (
<>
<TextListItem
component={TextListItemVariants.dt}
className="pf-u-min-width"
>
Name
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{getState()?.values?.['image-name']}
</TextListItem>
</>
)}
</TextList>
<br />
</TextContent>
);
};
TargetEnvAWSList.propTypes = {
awsSources: PropTypes.arrayOf(PropTypes.object),
isSuccessAWSSources: PropTypes.bool,
};
TargetEnvGCPList.propTypes = {
googleAccType: PropTypes.object,
};
FSCList.propTypes = {
minSize: PropTypes.string,
};

View file

@ -1300,21 +1300,31 @@ describe('Click through all steps', () => {
getNextButton().click();
// review
await screen.findByText(
'Review the information and click "Create image" to create the image using the following criteria.'
const targetEnvironmentsExpandable = await screen.findByTestId(
'target-environments-expandable'
);
await screen.findAllByText('Amazon Web Services');
await screen.findAllByText('Google Cloud Platform');
await screen.findByText('VMWare');
await screen.findByText('Virtualization - Guest image');
await screen.findByText('Bare metal - Installer');
targetEnvironmentsExpandable.click();
await screen.findAllByText('AWS');
await screen.findAllByText('GCP');
await screen.findByText('VMWare (.vmdk)');
await screen.findByText('Virtualization - Guest image (.qcow2)');
await screen.findByText('Bare metal - Installer (.iso)');
const registrationExpandable = await screen.findByTestId(
'registration-expandable'
);
registrationExpandable.click();
const review = screen.getByTestId('review-registration');
expect(review).toHaveTextContent(
'Use remote host configuration (RHC) utility'
);
const imageDetailsExpandable = await screen.findByTestId(
'image-details-expandable'
);
imageDetailsExpandable.click();
await screen.findByText('MyImageName');
screen.getByTestId('tab-registration').click();
await screen.findByText('name0');
await screen.findByText('Self-Support');
await screen.findByText('Production');

View file

@ -637,7 +637,7 @@ describe('Step Registration', () => {
screen.getByRole('button', { name: /Next/ }).click();
screen.getByRole('button', { name: /Next/ }).click();
screen.getByRole('button', { name: /Next/ }).click();
screen.getByTestId('tab-registration').click();
screen.getByTestId('registration-expandable').click();
const review = screen.getByTestId('review-registration');
expect(review).toHaveTextContent(
'Register with Red Hat Subscription Manager (RHSM)'
@ -1248,46 +1248,50 @@ describe('Step Review', () => {
verifyCancelButton(cancel, history);
});
test('has three tabs for rhel', async () => {
test('has Registration expandable section for rhel', async () => {
await setUp();
const buttonTarget = screen.getByTestId('tab-target');
const buttonRegistration = screen.getByTestId('tab-registration');
const buttonSystem = screen.getByTestId('tab-system');
const targetExpandable = screen.getByTestId(
'target-environments-expandable'
);
const registrationExpandable = screen.getByTestId(
'registration-expandable'
);
const contentExpandable = screen.getByTestId('content-expandable');
const fscExpandable = screen.getByTestId(
'file-system-configuration-expandable'
);
await user.click(buttonTarget);
screen.getByRole('heading', {
name: 'Amazon Web Services',
});
await user.click(buttonRegistration);
await user.click(targetExpandable);
screen.getByText('AWS');
await user.click(registrationExpandable);
screen.getByText('Register the system later');
await user.click(buttonSystem);
screen.getByRole('heading', {
name: 'Additional packages',
});
screen.getByRole('heading', {
name: 'File system configuration',
});
await user.click(contentExpandable);
screen.getByText('Additional Red Hatand 3rd party packages');
await user.click(fscExpandable);
screen.getByText('Configuration type');
});
test('has two tabs for centos', async () => {
test('has no Registration expandable for centos', async () => {
await setUpCentOS();
const buttonTarget = await screen.findByTestId('tab-target');
const buttonSystem = await screen.findByTestId('tab-system');
expect(screen.queryByTestId('tab-registration')).not.toBeInTheDocument();
const targetExpandable = await screen.findByTestId(
'target-environments-expandable'
);
const contentExpandable = await screen.findByTestId('content-expandable');
const fscExpandable = await screen.findByTestId(
'file-system-configuration-expandable'
);
expect(
screen.queryByTestId('registration-expandable')
).not.toBeInTheDocument();
await user.click(buttonTarget);
screen.getByRole('heading', {
name: 'Amazon Web Services',
});
await user.click(buttonSystem);
screen.getByRole('heading', {
name: 'Additional packages',
});
screen.getByRole('heading', {
name: 'File system configuration',
});
await user.click(targetExpandable);
screen.getByText('AWS');
await user.click(contentExpandable);
screen.getByText('Additional Red Hatand 3rd party packages');
await user.click(fscExpandable);
screen.getByText('Configuration type');
});
test('can pass location to recreate on review step', () => {
@ -1316,10 +1320,7 @@ describe('Step Review', () => {
{},
initialLocation
).history;
screen.getByText(
'Review the information and click "Create image" to create the image using the following criteria.'
);
screen.getByText('Virtualization - Guest image');
screen.getByText('Virtualization - Guest image (.qcow2)');
screen.getByText('Register the system later');
screen.getByText('MyImageName');
});
@ -1502,17 +1503,26 @@ describe('Click through all steps', () => {
getNextButton().click();
// review
await screen.findByText(
'Review the information and click "Create image" to create the image using the following criteria.'
const targetEnvironmentsExpandable = await screen.findByTestId(
'target-environments-expandable'
);
await screen.findAllByText('Amazon Web Services');
await screen.findAllByText('Google Cloud Platform');
await screen.findByText('VMWare');
await screen.findByText('Virtualization - Guest image');
await screen.findByText('Bare metal - Installer');
targetEnvironmentsExpandable.click();
await screen.findAllByText('AWS');
await screen.findAllByText('GCP');
await screen.findByText('VMWare (.vmdk)');
await screen.findByText('Virtualization - Guest image (.qcow2)');
await screen.findByText('Bare metal - Installer (.iso)');
const imageDetailsExpandable = await screen.findByTestId(
'image-details-expandable'
);
imageDetailsExpandable.click();
await screen.findByText('MyImageName');
screen.getByTestId('tab-registration').click();
const registrationExpandable = await screen.findByTestId(
'registration-expandable'
);
registrationExpandable.click();
await screen.findByText('name0');
await screen.findByText('Self-Support');
await screen.findByText('Production');
@ -1921,10 +1931,6 @@ describe('Keyboard accessibility', () => {
const nameInput = screen.getByRole('textbox', { name: /image name/i });
expect(nameInput).toHaveFocus();
clickNext();
// Review
const targetEnvironmentTab = screen.getByTestId('tab-target');
expect(targetEnvironmentTab).toHaveFocus();
});
test('pressing Esc closes the wizard', async () => {