Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/common/src/wizard/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const createSchema = ({ formOptions, fields }) => {
field.substepOf,
index,
primary: !schema[schema.length - 1] || !field.substepOf || field.substepOf !== schema[schema.length - 1].substepOf,
isProgressAfterSubmissionStep: field.isProgressAfterSubmissionStep,
},
];

Expand Down
59 changes: 59 additions & 0 deletions packages/pf4-component-mapper/demo/demo-schemas/wizard-schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -441,3 +441,62 @@ export const wizardSchemaMoreSubsteps = {
}
]
};

export const wizardSchemaProgressAfterSubmission = {
fields: [
{
component: componentTypes.WIZARD,
name: 'progress-wizard',
title: 'Progress after submission',
description: 'This wizard shows a progress step after submission',
fields: [
{
title: 'Step 1',
name: 'step-1',
nextStep: 'step-2',
fields: [
{
component: componentTypes.TEXT_FIELD,
name: 'name',
label: 'Name',
isRequired: true,
validate: [
{
type: validatorTypes.REQUIRED
}
]
}
]
},
{
title: 'Step 2',
name: 'step-2',
nextStep: 'progress-step',
fields: [
{
component: componentTypes.TEXT_FIELD,
name: 'email',
label: 'Email',
isRequired: true,
validate: [
{
type: validatorTypes.REQUIRED
}
]
}
]
},
{
name: 'progress-step',
isProgressAfterSubmissionStep: true,
fields: [
{
name: 'progress-content',
component: 'progress-step-content'
}
]
}
]
}
]
};
73 changes: 71 additions & 2 deletions packages/pf4-component-mapper/demo/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import { FormRenderer } from '@data-driven-forms/react-form-renderer';
import { FormRenderer, WizardContext } from '@data-driven-forms/react-form-renderer';
import { arraySchemaDDF } from './demo-schemas/widget-schema';
import { componentMapper, FormTemplate } from '../src';
import { Title, Button, Toolbar, ToolbarGroup, ToolbarItem } from '@patternfly/react-core';
import {
Title,
Button,
Toolbar,
ToolbarGroup,
ToolbarItem,
EmptyState,
EmptyStateBody,
EmptyStateFooter,
EmptyStateActions,
Progress,
Bullseye
} from '@patternfly/react-core';
import { CogsIcon } from '@patternfly/react-icons';
import {
wizardSchema,
wizardSchemaWithFunction,
wizardSchemaSimple,
wizardSchemaSubsteps,
wizardSchemaMoreSubsteps,
wizardSchemaProgressAfterSubmission,
} from './demo-schemas/wizard-schema';
import sandboxSchema from './demo-schemas/sandbox';
import dualSchema from './demo-schemas/dual-list-schema';
Expand All @@ -18,6 +32,48 @@ import selectSchema from './demo-schemas/select-schema';

const Summary = (props) => <div>Custom summary component.</div>;

const ProgressStepContent = () => {
const { jumpToStep } = React.useContext(WizardContext);
const [percentValidated, setPercentValidated] = React.useState(0);

const tick = React.useCallback(() => {
if (percentValidated < 100) {
setPercentValidated(prevValue => prevValue + 20);
}
}, [percentValidated]);

React.useEffect(() => {
const interval = setInterval(() => tick(), 1000);
return () => clearInterval(interval);
}, [tick]);

return (
<Bullseye>
<EmptyState
headingLevel="h4"
titleText={percentValidated === 100 ? 'Validation complete' : 'Validating credentials'}
icon={CogsIcon}
variant="lg"
>
<EmptyStateBody>
<Progress value={percentValidated} measureLocation="outside" aria-label="Wizard validation progress" />
</EmptyStateBody>
<EmptyStateBody>
Description can be used to further elaborate on the validation step, or give the user a better idea of how
long the process will take.
</EmptyStateBody>
<EmptyStateFooter>
<EmptyStateActions>
<Button isDisabled={percentValidated !== 100} onClick={() => jumpToStep(0)}>
Go to beginning
</Button>
</EmptyStateActions>
</EmptyStateFooter>
</EmptyState>
</Bullseye>
);
};

const fieldArrayState = {
schema: arraySchemaDDF,
additionalOptions: {
Expand Down Expand Up @@ -135,6 +191,19 @@ class App extends React.Component {
FormTemplate={(props) => <FormTemplate {...props} showFormControls={this.state.additionalOptions.showFormControls} />}
{...this.state.additionalOptions}
/>
<div>Progress after submission</div>
<FormRenderer
onSubmit={console.log}
componentMapper={{
...componentMapper,
summary: Summary,
'progress-step-content': ProgressStepContent,
}}
onCancel={() => console.log('Cancel action')}
schema={wizardSchemaProgressAfterSubmission}
FormTemplate={(props) => <FormTemplate {...props} showFormControls={this.state.additionalOptions.showFormControls} />}
{...this.state.additionalOptions}
/>
</>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const WizardNavigationInternal = React.memo(
({ navSchema, activeStepIndex, maxStepIndex, jumpToStep, valid, validating }) => (
<Fragment>
{navSchema
.filter((field) => field.primary)
.filter((field) => field.primary && !field.isProgressAfterSubmissionStep)
.map((step) => {
const substeps = step.substepOf && navSchema.filter((field) => field.substepOf === step.substepOf);

Expand Down
90 changes: 52 additions & 38 deletions packages/pf4-component-mapper/src/wizard/wizard.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ const WizardInternal = ({
return null;
}

const isProgressAfterSubmissionStep = currentStep.isProgressAfterSubmissionStep;

return (
<Modal
inModal={inModal}
Expand Down Expand Up @@ -117,45 +119,57 @@ const WizardInternal = ({
closeButtonAriaLabel={closeButtonAriaLabel}
/>
)}
<WizardToggle activeStepIndex={activeStepIndex} currentStep={currentStep} navSchema={navSchema} isOpen={state.openNav} dispatch={dispatch} />
<div className="pf-v6-c-wizard__outer-wrap">
<div className="pf-v6-c-wizard__inner-wrap">
<WizardNav aria-label={navAriaLabel} isExpanded={state.openNav}>
<FormSpy subscription={{ values: true, valid: true, validating: true }}>
{({ values, valid, validating }) => (
<WizardNavigation
navSchema={navSchema}
activeStepIndex={activeStepIndex}
valid={valid}
maxStepIndex={maxStepIndex}
jumpToStep={(...args) => {
state.openNav && dispatch({ type: 'closeNav' });
return jumpToStep(...args);
}}
crossroads={crossroads}
isDynamic={isDynamic}
values={values}
setPrevSteps={setPrevSteps}
validating={validating}
/>
)}
</FormSpy>
</WizardNav>
<WizardStep
conditionalSubmitFlag={conditionalSubmitFlag}
buttonLabels={buttonLabels}
buttonsClassName={buttonsClassName}
showTitles={showTitles}
hasNoBodyPadding={hasNoBodyPadding}
StepTemplate={StepTemplate}
{...currentStep}
formOptions={formOptions}
handleNext={(nextStep) => handleNext(nextStep)}
handlePrev={handlePrev}
disableBack={activeStepIndex === 0}
{isProgressAfterSubmissionStep ? (
currentStep.fields.map((item) => formOptions.renderForm([item], formOptions))
) : (
<>
<WizardToggle
activeStepIndex={activeStepIndex}
currentStep={currentStep}
navSchema={navSchema}
isOpen={state.openNav}
dispatch={dispatch}
/>
</div>
</div>
<div className="pf-v6-c-wizard__outer-wrap">
<div className="pf-v6-c-wizard__inner-wrap">
<WizardNav aria-label={navAriaLabel} isExpanded={state.openNav}>
<FormSpy subscription={{ values: true, valid: true, validating: true }}>
{({ values, valid, validating }) => (
<WizardNavigation
navSchema={navSchema}
activeStepIndex={activeStepIndex}
valid={valid}
maxStepIndex={maxStepIndex}
jumpToStep={(...args) => {
state.openNav && dispatch({ type: 'closeNav' });
return jumpToStep(...args);
}}
crossroads={crossroads}
isDynamic={isDynamic}
values={values}
setPrevSteps={setPrevSteps}
validating={validating}
/>
)}
</FormSpy>
</WizardNav>
<WizardStep
conditionalSubmitFlag={conditionalSubmitFlag}
buttonLabels={buttonLabels}
buttonsClassName={buttonsClassName}
showTitles={showTitles}
hasNoBodyPadding={hasNoBodyPadding}
StepTemplate={StepTemplate}
{...currentStep}
formOptions={formOptions}
handleNext={(nextStep) => handleNext(nextStep)}
handlePrev={handlePrev}
disableBack={activeStepIndex === 0}
/>
</div>
</div>
</>
)}
</div>
</Modal>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ describe('condition test', () => {
await waitFor(() => {
expect(errorSpy).toHaveBeenCalled();
// eslint-disable-next-line no-console
const errorMessage = console.error.mock.calls.map(call => call[0]).join(' ');
const errorMessage = console.error.mock.calls.map((call) => call[0]).join(' ');
expect(errorMessage).toContain('Received invalid setterValue. Expected object, received: ');
});
});
Expand Down