Skip to content

Commit

Permalink
Add class to handle form submit
Browse files Browse the repository at this point in the history
  • Loading branch information
hungoptimizely committed Nov 17, 2023
1 parent 752c64e commit fff720e
Show file tree
Hide file tree
Showing 17 changed files with 211 additions and 172 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"jestrunner.jestCommand": "npm run test --",
"jest.rootPath": "src/@optimizely/forms-sdk",
"jest.jestCommandLine": "npm run test --",
}
9 changes: 0 additions & 9 deletions samples/sample-react-app/src/App.test.tsx

This file was deleted.

129 changes: 129 additions & 0 deletions src/@optimizely/forms-react/src/components/FormBody.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import React, { useRef } from "react";
import { useForms } from "../context/store";
import { FormContainer, FormSubmit, StepBuilder, SubmitButtonType, isInArray } from "@optimizely/forms-sdk";
import { RenderElementInStep } from "./RenderElementInStep";

export const FormBody = () => {
const formContext = useForms();
const form = formContext?.formContainer ?? {} as FormContainer;
const formSubmit = new FormSubmit(formContext?.formContainer ?? {} as FormContainer);

const formTitleId = `${form.key}_label`;
const statusDisplay = useRef<string>("hide");
const stepCount = form.steps.length;
const statusMessage = useRef<string>("");

//TODO: these variables should be get from api or sdk
const validateFail = false,
formFinalized = false,
isProgressiveSubmit = false,
isSuccess = false,
submittable = true,
submissionWarning = false,
message = "",
isReadOnlyMode = false,
readOnlyModeMessage = "",
currentStepIndex = 0,
isStepValidToDisplay = true;

if(formFinalized || isProgressiveSubmit)
{
statusDisplay.current = "Form__Success__Message";
statusMessage.current = form.properties.submitSuccessMessage ?? message;
}
else if((submissionWarning || (!submittable && !isSuccess))
&& message)
{
statusDisplay.current = "Form__Warning__Message";
statusMessage.current = message;
}
const validationCssClass = validateFail ? "ValidationFail" : "ValidationSuccess";
const isShowStepNavigation = stepCount > 1 && currentStepIndex > -1 && currentStepIndex < stepCount && !formFinalized;
const prevButtonDisableState = (currentStepIndex == 0) || !submittable;
const nextButtonDisableState = (currentStepIndex == stepCount - 1) || !submittable;
const currentDisplayStepIndex = currentStepIndex + 1;
const progressWidth = (100 * currentDisplayStepIndex / stepCount) + "%";

const handleSubmit = (e: any) => {
e.preventDefault();

let formSubmissions = (formContext?.formSubmissions ?? [])
//only post value of active elements
.filter(fs => !isInArray(fs.elementKey, formContext?.dependencyInactiveElements ?? []));

formSubmit.doSubmit(formSubmissions);
}

return (
<form method="post"
noValidate={true}
aria-labelledby={formTitleId}
encType="multipart/form-data"
className={`EPiServerForms ${validationCssClass}`}
id={form.key}
onSubmit={handleSubmit}
>
{form.properties.title &&
<h2 className="Form__Title" id={formTitleId}>
{form.properties.title}
</h2>
}
{form.properties.description &&
<aside className="Form__Description">
{form.properties.description}
</aside>
}
{isReadOnlyMode && readOnlyModeMessage &&
<div className="Form__Status">
<span className="Form__Readonly__Message" role="alert">
{readOnlyModeMessage}
</span>
</div>
}
{/* area for showing Form's status or validation */}
<div className="Form__Status">
<div role="status" className={`Form__Status__Message ${statusDisplay.current}`}>
<div dangerouslySetInnerHTML={{
__html: statusMessage.current
}}
></div>
</div>
</div>

<div className="Form__MainBody">
{/* render element */}
{form.steps.map((e, i)=>{
let stepDisplaying = (currentStepIndex === i && !formFinalized && isStepValidToDisplay) ? "" : "hide";
return (
<section key={e.formStep.key} id={e.formStep.key} className={`Form__Element__Step ${stepDisplaying}`}>
<RenderElementInStep elements={e.elements} stepIndex={i} />
</section>
);
})}

{/* render step navigation */}
{isShowStepNavigation &&
<nav role="navigation" className="Form__NavigationBar">
<button type="submit" name="submit" value={SubmitButtonType.PreviousStep} className="Form__NavigationBar__Action FormExcludeDataRebind btnPrev"
disabled={prevButtonDisableState}>
{form.localizations["previousButtonLabel"]}
</button>

<div className="Form__NavigationBar__ProgressBar">
<div className="Form__NavigationBar__ProgressBar--Progress" style={{width: progressWidth}}></div>
<div className="Form__NavigationBar__ProgressBar--Text">
<span className="Form__NavigationBar__ProgressBar__ProgressLabel">{form.localizations["pageButtonLabel"]}</span>
<span className="Form__NavigationBar__ProgressBar__CurrentStep">{currentDisplayStepIndex}</span>/
<span className="Form__NavigationBar__ProgressBar__StepsCount">{stepCount}</span>
</div>
</div>
<button type="submit" name="submit" value={SubmitButtonType.NextStep} className="Form__NavigationBar__Action FormExcludeDataRebind btnNext"
disabled={nextButtonDisableState}>
{form.localizations["nextButtonLabel"]}
</button>
</nav>
}
</div>
</form>
)
}
118 changes: 3 additions & 115 deletions src/@optimizely/forms-react/src/components/FormContainerBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React, { useRef } from "react";
import React from "react";
import { FormContainer, StepBuilder } from "@optimizely/forms-sdk";
import { RenderElementInStep } from "./RenderElementInStep";
import { SubmitButtonType } from "../models/SubmitButtonType";
import { FormProvider } from "../context/FormProvider";
import { initState } from "../context/initState";
import { FormBody } from "./FormBody";

export interface FormContainerProps {
form: FormContainer
Expand All @@ -12,123 +11,12 @@ export interface FormContainerProps {
export function FormContainerBlock(props: FormContainerProps){
const stepBuilder = new StepBuilder(props.form);
const form = stepBuilder.buildForm();
const formTitleId = `${form.key}_label`;
const statusDisplay = useRef<string>("hide");
const stepCount = form.steps.length;
const statusMessage = useRef<string>("");

//TODO: these variables should be get from api or sdk
const validateFail = false,
formFinalized = false,
isProgressiveSubmit = false,
isSuccess = false,
submittable = true,
submissionWarning = false,
message = "",
isReadOnlyMode = false,
readOnlyModeMessage = "",
currentStepIndex = 0,
isStepValidToDisplay = true;

if(formFinalized || isProgressiveSubmit)
{
statusDisplay.current = "Form__Success__Message";
statusMessage.current = form.properties.submitSuccessMessage ?? message;
}
else if((submissionWarning || (!submittable && !isSuccess))
&& message)
{
statusDisplay.current = "Form__Warning__Message";
statusMessage.current = message;
}
const validationCssClass = validateFail ? "ValidationFail" : "ValidationSuccess";
const isShowStepNavigation = stepCount > 1 && currentStepIndex > -1 && currentStepIndex < stepCount && !formFinalized;
const prevButtonDisableState = (currentStepIndex == 0) || !submittable;
const nextButtonDisableState = (currentStepIndex == stepCount - 1) || !submittable;
const currentDisplayStepIndex = currentStepIndex + 1;
const progressWidth = (100 * currentDisplayStepIndex / stepCount) + "%";

const FormBody = () => {
return (
<>
{form.properties.title &&
<h2 className="Form__Title" id={formTitleId}>
{form.properties.title}
</h2>
}
{form.properties.description &&
<aside className="Form__Description">
{form.properties.description}
</aside>
}
{isReadOnlyMode && readOnlyModeMessage &&
<div className="Form__Status">
<span className="Form__Readonly__Message" role="alert">
{readOnlyModeMessage}
</span>
</div>
}
{/* area for showing Form's status or validation */}
<div className="Form__Status">
<div role="status" className={`Form__Status__Message ${statusDisplay.current}`}>
<div dangerouslySetInnerHTML={{
__html: statusMessage.current
}}
></div>
</div>
</div>

<div className="Form__MainBody">
{/* render element */}
{form.steps.map((e, i)=>{
let stepDisplaying = (currentStepIndex === i && !formFinalized && isStepValidToDisplay) ? "" : "hide";
return (
<section key={e.formStep.key} id={e.formStep.key} className={`Form__Element__Step ${stepDisplaying}`}>
<RenderElementInStep elements={e.elements} stepIndex={i} />
</section>
);
})}

{/* render step navigation */}
{isShowStepNavigation &&
<nav role="navigation" className="Form__NavigationBar">
<button type="submit" name="submit" value={SubmitButtonType.PreviousStep} className="Form__NavigationBar__Action FormExcludeDataRebind btnPrev"
disabled={prevButtonDisableState}>
{form.localizations["previousButtonLabel"]}
</button>

<div className="Form__NavigationBar__ProgressBar">
<div className="Form__NavigationBar__ProgressBar--Progress" style={{width: progressWidth}}></div>
<div className="Form__NavigationBar__ProgressBar--Text">
<span className="Form__NavigationBar__ProgressBar__ProgressLabel">{form.localizations["pageButtonLabel"]}</span>
<span className="Form__NavigationBar__ProgressBar__CurrentStep">{currentDisplayStepIndex}</span>/
<span className="Form__NavigationBar__ProgressBar__StepsCount">{stepCount}</span>
</div>
</div>
<button type="submit" name="submit" value={SubmitButtonType.NextStep} className="Form__NavigationBar__Action FormExcludeDataRebind btnNext"
disabled={nextButtonDisableState}>
{form.localizations["nextButtonLabel"]}
</button>
</nav>
}
</div>
</>
)
}

const state = initState({formContainer: form});

{/* finally return the form */}
return (
<FormProvider initialState={state}>
<form method="post"
noValidate={true}
aria-labelledby={formTitleId}
encType="multipart/form-data"
className={`EPiServerForms ${validationCssClass}`}
id={form.key}>
<FormBody />
</form>
<FormBody />
</FormProvider>
)
}
14 changes: 1 addition & 13 deletions src/@optimizely/forms-react/src/context/initState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { ConditionProperties,
FormSubmission,
FormValidation,
FormValidationResult,
FormDependencies,
StepDependencies } from "@optimizely/forms-sdk";

interface InitStateProps{
Expand All @@ -19,7 +18,6 @@ export function initState(props: InitStateProps): FormState{

let formSubmissions = [] as FormSubmission[];
let formValidations = [] as FormValidation[];
let formDependencies = [] as FormDependencies[];
let stepDependencies = [] as StepDependencies[];

formContainer?.steps.forEach(s => {
Expand All @@ -40,23 +38,13 @@ export function initState(props: InitStateProps): FormState{
}

formValidations = formValidations.concat({elementKey: e.key, results: formValidationResults});

//init form dependencies
let conditionProps = (e.properties as unknown) as ConditionProperties;

//Captcha, ResetButton don't have condition
if(!isNull(conditionProps.conditions)){
conditionProps.conditions.forEach(c => {
formDependencies = formDependencies.concat({ elementKey: e.key, isSatisfied: false }); //default isSatisfied = false to do reverse action
});
}
});
stepDependencies = stepDependencies.concat({elementKey: s.formStep.key, isSatisfied: false });
});



return {
isReset: false, formSubmissions, formDependencies, formValidations, stepDependencies, formContainer
isReset: false, formSubmissions, formValidations, stepDependencies, formContainer, dependencyInactiveElements: []
} as FormState;
}
6 changes: 1 addition & 5 deletions src/@optimizely/forms-react/src/context/reducer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { equals,
FormState,
FormDependencies,
FormSubmission,
FormValidation } from "@optimizely/forms-sdk";

Expand Down Expand Up @@ -35,10 +34,7 @@ export function formReducer(formState: FormState, action: any) {
case ActionType.UpdateDependencies: {
return {
...formState,
formDependencies: formState.formDependencies.map(fd => equals(fd.elementKey, action.elementKey) ? {
elementKey: action.elementKey,
isSatisfied: action.isSatisfied
} as FormDependencies : fd)
dependencyInactiveElements: action.dependencyInactiveElements
} as FormState;
}
case ActionType.ResetForm: {
Expand Down
Loading

0 comments on commit fff720e

Please sign in to comment.