Add all steps and write review step

Basic steps with text inputs to test the navigation and formatting, add final review step component to show all values
This commit is contained in:
Karel Hala 2021-05-31 17:39:51 +02:00 committed by Sanne Raymaekers
parent 06d4fd718b
commit c040b004ad
14 changed files with 468 additions and 106 deletions

View file

@ -2,7 +2,10 @@ import React from 'react';
import ImageCreator from './ImageCreator';
import { useHistory } from 'react-router-dom';
import componentTypes from '@data-driven-forms/react-form-renderer/component-types';
import validatorTypes from '@data-driven-forms/react-form-renderer/validator-types';
import { Button } from '@patternfly/react-core';
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
import { review, awsTarget, registration, googleCloudTarger, msAzureTarget, packages, imageOutput } from './steps';
import './CreateImageWizard.scss';
const CreateImage = () => {
const history = useHistory();
@ -19,103 +22,28 @@ const CreateImage = () => {
showTitles: true,
title: 'Create image',
crossroads: [ 'role-type' ],
description: <div>Create a RHEL image and push it to cloud providers.<a>link</a></div>,
description: <div>Create a RHEL image and push it to cloud providers. <Button
component="a"
target="_blank"
variant="link"
icon={ <ExternalLinkAltIcon /> }
iconPosition="right"
isInline
href="
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/uploading_a_customized_rhel_system_image_to_cloud_environments/index
">
Documentation
</Button></div>,
// order in this array does not reflect order in wizard nav, this order is managed inside
// of each step by `nextStep` property!
fields: [
{
title: 'Image output',
name: 'step-1',
nextStep: {
when: 'role-type',
stepMapper: {
a: 'aws-target-env',
ab: 'aws-target-env',
undefined: 'registration'
},
},
fields: [
{
component: componentTypes.TEXT_FIELD,
name: 'role-type',
type: 'text',
label: 'Role name',
isRequired: true,
validate: [
{
type: validatorTypes.REQUIRED
}
],
}
]
},
{
title: 'Amazon Web Services',
name: 'aws-target-env',
substepOf: 'Target environment',
nextStep: {
when: 'role-type',
stepMapper: {
ab: 'ms-azure-target-env',
a: 'registration'
},
},
fields: [
{
component: componentTypes.TEXT_FIELD,
name: 'test-field',
type: 'text',
label: 'Role name',
isRequired: true,
validate: [
{
type: validatorTypes.REQUIRED,
},
],
}
]
},
{
title: 'Microsoft Azure',
label: 'bla bla',
name: 'ms-azure-target-env',
substepOf: 'Target environment',
nextStep: 'registration',
fields: [
{
component: componentTypes.TEXT_FIELD,
name: 'test-field',
type: 'text',
label: 'Role name',
isRequired: true,
validate: [
{
type: validatorTypes.REQUIRED,
},
],
}
]
},
{
title: 'Registration',
name: 'registration',
fields: [
{
component: componentTypes.TEXT_FIELD,
name: 'another-field',
type: 'text',
label: 'Role name',
isRequired: true,
validate: [
{
type: validatorTypes.REQUIRED,
},
{
type: validatorTypes.MAX_LENGTH,
threshold: 150,
},
],
}
]
}
imageOutput,
awsTarget,
msAzureTarget,
googleCloudTarger,
registration,
packages,
review,
]
}
]

View file

@ -1,3 +1,8 @@
.pf-c-wizard__nav-list {
padding-right: 0px;
}
.ins-c-image-builder__in-wizard.pf-c-popover {
visibility: initial;
pointer-events: initial;
}

View file

@ -1,24 +1,21 @@
import React from 'react';
import FormRenderer from '@data-driven-forms/react-form-renderer/form-renderer';
import Pf4FormTemplate from '@data-driven-forms/pf4-component-mapper/form-template';
import componentTypes from '@data-driven-forms/react-form-renderer/component-types';
import Wizard from '@data-driven-forms/pf4-component-mapper/wizard';
import TextField from '@data-driven-forms/pf4-component-mapper/text-field';
import { componentMapper } from '@data-driven-forms/pf4-component-mapper';
import { Spinner } from '@patternfly/react-core';
import Review from './formComponents/ReviewStep';
import PropTypes from 'prop-types';
const CreateImageWizard = ({ schema, onSubmit, onClose }) => {
const CreateImageWizard = ({ schema, onSubmit, onClose, customComponentMapper }) => {
return schema ? <FormRenderer
schema={ schema }
subscription={ { values: true } }
FormTemplate={ (props) => <Pf4FormTemplate { ...props } showFormControls={ false } /> }
onSubmit={ (formValues) => onSubmit(formValues) }
componentMapper={ {
[componentTypes.WIZARD]: {
component: Wizard,
'data-ouia-component-id': 'image-creation-wizard'
},
[componentTypes.TEXT_FIELD]: TextField
...componentMapper,
review: Review,
...customComponentMapper
} }
onCancel={ onClose } /> : <Spinner />;
};
@ -27,6 +24,11 @@ CreateImageWizard.propTypes = {
schema: PropTypes.object,
onSubmit: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
customComponentMapper: PropTypes.shape({
[PropTypes.string]: PropTypes.oneOfType([ PropTypes.node, PropTypes.shape({
component: PropTypes.node
}) ])
})
};
export default CreateImageWizard;

View file

@ -0,0 +1,56 @@
import React from 'react';
import PropTypes from 'prop-types';
import { TextContent, Text, TextVariants, TextList, TextListItem, TextListVariants, TextListItemVariants } from '@patternfly/react-core';
import useFormApi from '@data-driven-forms/react-form-renderer/use-form-api';
import { releaseValues } from '../steps/imageOutput';
import { registerValues } from '../steps/registration';
import { googleAccType } from '../steps/googleCloud';
const ReviewStep = () => {
const { getState } = useFormApi();
return (
<TextContent>
<Text component={ TextVariants.small }>
Review the information and click the Create button
to create the image using the following criteria.
</Text>
<Text component={ TextVariants.h3 }>Image output</Text>
<TextList component={ TextListVariants.dl } data-testid='review-image-output'>
<TextListItem component={ TextListItemVariants.dt }>Release</TextListItem>
<TextListItem component={ TextListItemVariants.dd }>
{releaseValues?.[getState()?.values?.release]}
</TextListItem>
</TextList>
<Text component={ TextVariants.h3 }>Target environment</Text>
{getState()?.values?.['aws-account-id'] && <>
<Text id="destination-header">Amazon Web Services</Text>
<TextList component={ TextListVariants.dl } data-testid='review-image-upload-aws'>
<TextListItem component={ TextListItemVariants.dt }>Account ID</TextListItem>
<TextListItem component={ TextListItemVariants.dd }>{getState()?.values?.['aws-account-id']}</TextListItem>
</TextList>
</>}
{getState()?.values?.['google-account-type'] && <>
<Text id="destination-header">Google Cloud Platform</Text>
<TextList component={ TextListVariants.dl } data-testid='review-image-upload-google'>
<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>
</>}
<Text component={ TextVariants.h3 }>Registration</Text>
<TextList component={ TextListVariants.dl } data-testid='review-image-registration'>
<TextListItem component={ TextListItemVariants.dt }>Subscription</TextListItem>
{registerValues?.[getState()?.values?.['register-system']]}
</TextList>
</TextContent>
);
};
ReviewStep.propTypes = {
formFields: PropTypes.array,
};
export default ReviewStep;

View file

@ -0,0 +1,41 @@
import React from 'react';
import componentTypes from '@data-driven-forms/react-form-renderer/component-types';
import validatorTypes from '@data-driven-forms/react-form-renderer/validator-types';
import nextStepMapper from './stepMapper';
import { Title } from '@patternfly/react-core';
export default {
title: 'Amazon Web Services',
customTitle: <Title headingLevel="h1" size="xl">Target Environment - Amazon Web Service</Title>,
name: 'aws-target-env',
substepOf: 'Target environment',
nextStep: ({ values }) => nextStepMapper(values, true),
fields: [
{
component: componentTypes.PLAIN_TEXT,
name: 'plain-text-component',
label: <p>
Your image will be uploaded to a temporary account on Amazon Web Services. <br />
The image will be shared with the account you provide below. <br />
Within the next 14 days you will need to copy the shared image to your own account.
After 14 days it will be unavailable and will have to be regenerated.
</p>
},
{
component: componentTypes.TEXT_FIELD,
name: 'aws-account-id',
type: 'text',
label: 'AWS account ID',
isRequired: true,
validate: [
{
type: validatorTypes.REQUIRED,
},
{
type: validatorTypes.EXACT_LENGTH,
threshold: 12
}
],
}
]
};

View file

@ -0,0 +1,116 @@
import React from 'react';
import componentTypes from '@data-driven-forms/react-form-renderer/component-types';
import validatorTypes from '@data-driven-forms/react-form-renderer/validator-types';
import { HelpIcon } from '@patternfly/react-icons';
import { Title, Text, Popover, TextContent, TextList, TextListItem, Button } from '@patternfly/react-core';
export const googleAccType = {
googleAccount: 'Google account',
serviceAccount: 'Service account',
googleGroup: 'Google group',
domain: 'Domain'
};
export default {
title: 'Google Cloud Platform',
customTitle: <Title headingLevel="h1" size="xl">Target Environment - Google Cloud Platform</Title>,
name: 'google-cloud-target-env',
substepOf: 'Target environment',
nextStep: 'registration',
fields: [
{
component: componentTypes.PLAIN_TEXT,
name: 'google-cloud-text-component',
label: <Text>
Your image will be uploaded to Google Cloud Platform and shared with the email you provide below. <br />
The image should be copied to your account within 14 days.
</Text>
},
{
component: componentTypes.RADIO,
label: <>Type <Popover
className="ins-c-image-builder__in-wizard"
hasAutoWidth
maxWidth='35rem'
headerContent={ 'Valid account types' }
bodyContent={ <TextContent>
<Text>The following account types can have an image shared with them:</Text>
<TextList>
<TextListItem>
<strong>Google account:</strong> A Google account represents a developer, an administrator,
or any other person who interacts with Google Cloud. e.g., <em>`alice@gmail.com`</em>.
</TextListItem>
<TextListItem>
<strong>Service account:</strong> A service account is an account for an application instead
of an individual end user. e.g., <em>`myapp@appspot.gserviceaccount.com`</em>.
</TextListItem>
<TextListItem>
<strong>Google group:</strong> A Google group is a named collection of Google accounts and
and service accounts. e.g., <em>`admins@example.com`</em>.
</TextListItem>
<TextListItem>
<strong>Google workspace domain/Cloud identity domain:</strong> A Google workspace or cloud identity
domain represents a virtual group of all the Google accounts in an organization. These domains
represent your organization&apos;s internet domain name. e.g., <em>`mycompany.com`</em>.
</TextListItem>
</TextList>
</TextContent> }>
<Button
variant="plain"
aria-label="Account info"
aria-describedby="google-account-type"
className="pf-c-form__group-label-help">
<HelpIcon />
</Button>
</Popover></>,
name: 'google-account-type',
initialValue: 'googleAccount',
options: Object.entries(googleAccType).map(([ value, label ]) => ({
label: value === 'domain' ? 'Google Workspace Domain or Cloud Identity Domain' : label,
value
})),
isRequired: true,
validate: [
{
type: validatorTypes.REQUIRED,
},
],
},
{
component: componentTypes.TEXT_FIELD,
name: 'google-email',
type: 'text',
label: 'Email address',
condition: {
or: [
{ when: 'google-account-type', is: 'googleAccount' },
{ when: 'google-account-type', is: 'serviceAccount' },
{ when: 'google-account-type', is: 'googleGroup' },
{ when: 'google-account-type', is: null },
]
},
isRequired: true,
validate: [
{
type: validatorTypes.REQUIRED,
},
],
},
{
component: componentTypes.TEXT_FIELD,
name: 'google-domain',
type: 'text',
label: 'Domain',
condition: {
when: 'google-account-type',
is: 'domain'
},
isRequired: true,
validate: [
{
type: validatorTypes.REQUIRED,
},
],
}
]
};

View file

@ -0,0 +1,45 @@
import componentTypes from '@data-driven-forms/react-form-renderer/component-types';
import validatorTypes from '@data-driven-forms/react-form-renderer/validator-types';
import nextStepMapper from './stepMapper';
export const releaseValues = {
'rhel-8': 'Red Hat Enterprise Linux (RHEL) 8.3',
'centos-8': 'CentOS Stream 8'
};
export default {
title: 'Image output',
name: 'step-1',
nextStep: ({ values }) => nextStepMapper(values),
fields: [
{
component: componentTypes.SELECT,
label: 'Release',
name: 'release',
simpleValue: true,
initialValue: 'rhel-8',
options: Object.entries(releaseValues).map(([ key, title ]) => ({
label: title,
value: key
})),
isRequired: true,
validate: [
{
type: validatorTypes.REQUIRED
}
],
},
{
component: componentTypes.TEXT_FIELD,
name: 'role-type',
type: 'text',
label: 'Role name',
isRequired: true,
validate: [
{
type: validatorTypes.REQUIRED
}
],
}
]
};

View file

@ -0,0 +1,8 @@
export { default as awsTarget } from './aws';
export { default as googleCloudTarger } from './googleCloud';
export { default as msAzureTarget } from './msAzure';
export { default as packages } from './packages';
export { default as registration } from './registration';
export { default as review } from './review';
export { default as imageOutput } from './imageOutput';
export { default as nextStepMapper } from './stepMapper';

View file

@ -0,0 +1,86 @@
import React from 'react';
import componentTypes from '@data-driven-forms/react-form-renderer/component-types';
import validatorTypes from '@data-driven-forms/react-form-renderer/validator-types';
import nextStepMapper from './stepMapper';
import { Title, Text, Button } from '@patternfly/react-core';
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
export default {
title: 'Microsoft Azure',
customTitle: <Title headingLevel="h1" size="xl">Target Environment - Microsoft Azure</Title>,
name: 'ms-azure-target-env',
substepOf: 'Target environment',
nextStep: ({ values }) => nextStepMapper(values, true, true),
fields: [
{
component: componentTypes.PLAIN_TEXT,
name: 'azure-text-component',
label: <>
<Text>
Image Builder will send an image to an authorized Azure account.
</Text>
<Title headingLevel="h3">OAuth permissions</Title>
<Text>
To authorize Image Builder to push images to Microsoft Azure, the account owner
must configure Image Builder as an authorized application and give it the role of
&quot;Contributor&quot; to at least one resource group.<br />
</Text>
<Button
component="a"
target="_blank"
variant="link"
icon={ <ExternalLinkAltIcon /> }
iconPosition="right"
isInline
href="https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow">
<small>Learn more about OAuth 2.0</small>
</Button>
<a href="https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=b94bb246-b02c-4985-9c22-d44e66f657f4
&scope=openid&response_type=code&response_mode=form_post
&redirect_uri=https%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Fnativeclient" target="_blank" rel="noopener noreferrer">
Authorize Image Builder on Azure <ExternalLinkAltIcon />
</a>
<Title headingLevel="h3">Destination</Title>
<Text>
Your image will be uploaded to the resource group in the subscription you specify.
</Text>
</>
},
{
component: componentTypes.TEXT_FIELD,
name: 'azure-tenant-id',
type: 'text',
label: 'Tenant ID',
isRequired: true,
validate: [
{
type: validatorTypes.REQUIRED,
},
],
},
{
component: componentTypes.TEXT_FIELD,
name: 'azure-subscription-id',
type: 'text',
label: 'Subscription ID',
isRequired: true,
validate: [
{
type: validatorTypes.REQUIRED,
},
],
},
{
component: componentTypes.TEXT_FIELD,
name: 'azure-resource-group',
type: 'text',
label: 'Resource group',
isRequired: true,
validate: [
{
type: validatorTypes.REQUIRED,
},
],
}
]
};

View file

@ -0,0 +1,26 @@
import componentTypes from '@data-driven-forms/react-form-renderer/component-types';
import validatorTypes from '@data-driven-forms/react-form-renderer/validator-types';
export default {
title: 'Packages',
name: 'packages',
nextStep: 'review',
fields: [
{
component: componentTypes.TEXT_FIELD,
name: 'packages-field',
type: 'text',
label: 'Role name',
isRequired: true,
validate: [
{
type: validatorTypes.REQUIRED,
},
{
type: validatorTypes.MAX_LENGTH,
threshold: 150,
},
],
}
]
};

View file

@ -0,0 +1,24 @@
import componentTypes from '@data-driven-forms/react-form-renderer/component-types';
export const registerValues = {
'subscribe-now-radio': 'Embed an activation key and register systems on first boot',
'register-later-radio-button': 'Register the system later'
};
export default {
title: 'Registration',
name: 'registration',
nextStep: 'packages',
fields: [
{
component: componentTypes.RADIO,
label: 'Register the system',
name: 'register-system',
initialValue: 'register-later-radio-button',
options: Object.entries(registerValues).map(([ key, title ]) => ({
label: title,
value: key
}))
}
]
};

View file

@ -0,0 +1,10 @@
export default {
name: 'review',
title: 'Review',
fields: [
{
name: 'review',
component: 'review'
}
]
};

View file

@ -0,0 +1,15 @@
export default (values, skipFirst, skipSecond) => {
if (!skipFirst && values?.['role-type']?.includes('a')) {
return 'aws-target-env';
}
if (!skipSecond && values?.['role-type']?.includes('b')) {
return 'ms-azure-target-env';
}
if (values?.['role-type']?.includes('c')) {
return 'google-cloud-target-env';
}
return 'registration';
};