diff --git a/packages/common/src/wizard/reducer.js b/packages/common/src/wizard/reducer.js index ff673c026..53f0aeac5 100644 --- a/packages/common/src/wizard/reducer.js +++ b/packages/common/src/wizard/reducer.js @@ -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, }, ]; diff --git a/packages/pf4-component-mapper/demo/demo-schemas/wizard-schema.js b/packages/pf4-component-mapper/demo/demo-schemas/wizard-schema.js index 9029b03e2..e108d86d8 100644 --- a/packages/pf4-component-mapper/demo/demo-schemas/wizard-schema.js +++ b/packages/pf4-component-mapper/demo/demo-schemas/wizard-schema.js @@ -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' + } + ] + } + ] + } + ] +}; diff --git a/packages/pf4-component-mapper/demo/index.js b/packages/pf4-component-mapper/demo/index.js index ab384d64d..b23969783 100644 --- a/packages/pf4-component-mapper/demo/index.js +++ b/packages/pf4-component-mapper/demo/index.js @@ -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'; @@ -18,6 +32,48 @@ import selectSchema from './demo-schemas/select-schema'; const Summary = (props) =>
Custom summary component.
; +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 ( + + + + + + + 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. + + + + + + + + + ); +}; + const fieldArrayState = { schema: arraySchemaDDF, additionalOptions: { @@ -135,6 +191,19 @@ class App extends React.Component { FormTemplate={(props) => } {...this.state.additionalOptions} /> +
Progress after submission
+ console.log('Cancel action')} + schema={wizardSchemaProgressAfterSubmission} + FormTemplate={(props) => } + {...this.state.additionalOptions} + /> )} diff --git a/packages/pf4-component-mapper/src/wizard/wizard-components/wizard-nav.js b/packages/pf4-component-mapper/src/wizard/wizard-components/wizard-nav.js index f7dc19956..9a4bdd981 100644 --- a/packages/pf4-component-mapper/src/wizard/wizard-components/wizard-nav.js +++ b/packages/pf4-component-mapper/src/wizard/wizard-components/wizard-nav.js @@ -22,7 +22,7 @@ const WizardNavigationInternal = React.memo( ({ navSchema, activeStepIndex, maxStepIndex, jumpToStep, valid, validating }) => ( {navSchema - .filter((field) => field.primary) + .filter((field) => field.primary && !field.isProgressAfterSubmissionStep) .map((step) => { const substeps = step.substepOf && navSchema.filter((field) => field.substepOf === step.substepOf); diff --git a/packages/pf4-component-mapper/src/wizard/wizard.js b/packages/pf4-component-mapper/src/wizard/wizard.js index f8294235f..7ce588484 100644 --- a/packages/pf4-component-mapper/src/wizard/wizard.js +++ b/packages/pf4-component-mapper/src/wizard/wizard.js @@ -85,6 +85,8 @@ const WizardInternal = ({ return null; } + const isProgressAfterSubmissionStep = currentStep.isProgressAfterSubmissionStep; + return ( )} - -
-
- - - {({ values, valid, validating }) => ( - { - state.openNav && dispatch({ type: 'closeNav' }); - return jumpToStep(...args); - }} - crossroads={crossroads} - isDynamic={isDynamic} - values={values} - setPrevSteps={setPrevSteps} - validating={validating} - /> - )} - - - handleNext(nextStep)} - handlePrev={handlePrev} - disableBack={activeStepIndex === 0} + {isProgressAfterSubmissionStep ? ( + currentStep.fields.map((item) => formOptions.renderForm([item], formOptions)) + ) : ( + <> + -
-
+
+
+ + + {({ values, valid, validating }) => ( + { + state.openNav && dispatch({ type: 'closeNav' }); + return jumpToStep(...args); + }} + crossroads={crossroads} + isDynamic={isDynamic} + values={values} + setPrevSteps={setPrevSteps} + validating={validating} + /> + )} + + + handleNext(nextStep)} + handlePrev={handlePrev} + disableBack={activeStepIndex === 0} + /> +
+
+ + )}
); diff --git a/packages/react-form-renderer/src/tests/form-renderer/condition.test.js b/packages/react-form-renderer/src/tests/form-renderer/condition.test.js index a119b0d21..a8c5f4547 100644 --- a/packages/react-form-renderer/src/tests/form-renderer/condition.test.js +++ b/packages/react-form-renderer/src/tests/form-renderer/condition.test.js @@ -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: '); }); });