Skip to content

Commit

Permalink
Show error from server and adjust some code
Browse files Browse the repository at this point in the history
Fixes: AFORM-3781
  • Loading branch information
hungoptimizely committed Dec 15, 2023
1 parent 686f224 commit ed84cb5
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 82 deletions.
46 changes: 2 additions & 44 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions samples/ManagementSite/Alloy.ManagementSite.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
<PackageReference Include="EPiServer.CMS.UI.VisitorGroups" Version="12.23.0" />
<PackageReference Include="EPiServer.CMS.UI.AspNetIdentity" Version="12.23.0" />
<PackageReference Include="EPiServer.ImageLibrary.ImageSharp" Version="2.0.1" />
<PackageReference Include="Optimizely.Headless.Form.Service" Version="0.1.0--ci-165" />
<PackageReference Include="Optimizely.Headless.Form.Service" Version="0.1.0--inte-173" />
<PackageReference Include="Optimizely.Cms.Content.EPiServer" Version="0.3.1" />
<PackageReference Include="EPiServer.Forms" Version="5.8.0-ci-021200" />
<PackageReference Include="EPiServer.Forms" Version="5.8.0-ci-021201" />
<PackageReference Include="EPiServer.OpenIDConnect" Version="3.2.0"/>
</ItemGroup>
<ItemGroup>
Expand Down
42 changes: 28 additions & 14 deletions src/@episerver/forms-react/src/components/FormBody.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useEffect, useRef } from "react";
import { useForms, useFormsDispatch } from "../context/store";
import { FormContainer, FormSubmitter, IdentityInfo, SubmitButtonType, equals, isInArray, isNull, isNullOrEmpty, FormSubmitModel, FormSubmitResult, SubmitButton } from "@episerver/forms-sdk";
import { FormContainer, FormSubmitter, IdentityInfo, SubmitButtonType, equals, isInArray, isNull, isNullOrEmpty, FormSubmitModel, FormSubmitResult, SubmitButton, ElementValidationResult } from "@episerver/forms-sdk";
import { RenderElementInStep } from "./RenderElementInStep";
import { DispatchFunctions } from "../context/dispatchFunctions";
import { FormStepNavigation } from "./FormStepNavigation";
Expand All @@ -15,13 +15,11 @@ export const FormBody = (props: FormBodyProps) => {
const formContext = useForms();
const form = formContext?.formContainer ?? {} as FormContainer;
const formSubmitter = new FormSubmitter(formContext?.formContainer ?? {} as FormContainer, props.baseUrl);
const dispatch = useFormsDispatch();
const dispatchFunctions = new DispatchFunctions(dispatch);
const dispatchFunctions = new DispatchFunctions();

const formTitleId = `${form.key}_label`;
const statusMessage = useRef<string>("");
const statusDisplay = useRef<string>("hide");
const stepLocalizations = useRef<Record<string, string>>(form.steps?.filter(s => !isNull(s.formStep.localizations))[0]?.formStep.localizations);

//TODO: these variables should be get from api or sdk
const validateFail = useRef<boolean>(false),
Expand Down Expand Up @@ -49,6 +47,14 @@ export const FormBody = (props: FormBodyProps) => {

const validationCssClass = validateFail.current ? "ValidationFail" : "ValidationSuccess";

const isInCurrentStep = (elementKey: string): boolean => {
let currentStep = form.steps[currentStepIndex];
if(currentStep){
return currentStep.elements.some(e => equals(e.key, elementKey));
}
return true;
}

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

Expand All @@ -58,15 +64,14 @@ export const FormBody = (props: FormBodyProps) => {

//Find submit button, if found then check property 'finalizeForm' of submit button. Otherwise, button Next/Previous was clicked.
let buttonId = e.nativeEvent.submitter.id;
let submitButton = form.formElements.filter(fe => fe.key === buttonId)[0] as SubmitButton;
let submitButton = form.formElements.find(fe => fe.key === buttonId) as SubmitButton;
if (!isNull(submitButton)) {
//when submitting by SubmitButton, isProgressiveSubmit default is true
isProgressiveSubmit.current = true;
}
//remove submissions of inactive elements and submissions with undefined value
//filter submissions by active elements and current step
let formSubmissions = (formContext?.formSubmissions ?? [])
//only post value of active elements
.filter(fs => !isInArray(fs.elementKey, formContext?.dependencyInactiveElements ?? []) && !isNull(fs.value));
.filter(fs => !isInArray(fs.elementKey, formContext?.dependencyInactiveElements ?? []) && isInCurrentStep(fs.elementKey));

//validate all submission data before submit
let formValidationResults = formSubmitter.doValidate(formSubmissions);
Expand All @@ -79,14 +84,14 @@ export const FormBody = (props: FormBodyProps) => {
)[0]?.elementKey;
if(!isNullOrEmpty(invalid)){
dispatchFunctions.updateFocusOn(invalid);
isFormFinalized.current = false;
return;
}

let isLastStep = formContext?.currentStepIndex === form.steps.length - 1;
let model: FormSubmitModel = {
formKey: form.key,
locale: form.locale,
isFinalized: submitButton?.properties?.finalizeForm || formContext?.currentStepIndex === form.steps.length - 1,
isFinalized: submitButton?.properties?.finalizeForm || isLastStep,
partialSubmissionKey: formContext?.submissionKey ?? "",
hostedPageUrl: window.location.pathname,
submissionData: formSubmissions,
Expand All @@ -95,6 +100,7 @@ export const FormBody = (props: FormBodyProps) => {

dispatchFunctions.updateIsSubmitting(true);
formSubmitter.doSubmit(model).then((response: FormSubmitResult)=>{
//get error or success message
if(response.success){
message.current = response.messages.map(m => m.message).join("<br>");
}
Expand All @@ -103,9 +109,19 @@ export const FormBody = (props: FormBodyProps) => {
//ignore validation message
message.current = response.messages.filter(m => isNullOrEmpty(m.identifier)).map(m => m.message).join("<br>");
}
//update validation message
if(response.validationFail){
response.messages.forEach(m => {
let elementValidationResults = formContext?.formValidationResults?.find(r => equals(r.elementKey, m.identifier))?.results;
if(elementValidationResults){
elementValidationResults = elementValidationResults.map(r => equals(r.type, m.section) ? {...r, valid: false} as ElementValidationResult : r);
dispatchFunctions.updateValidation(m.identifier, elementValidationResults);
}
})
}
validateFail.current = response.validationFail;
isSuccess.current = response.success;
isFormFinalized.current = formContext?.currentStepIndex === form.steps.length - 1 && response.success;
isFormFinalized.current = isLastStep && response.success;
dispatchFunctions.updateSubmissionKey(response.submissionKey);
dispatchFunctions.updateIsSubmitting(false);
});
Expand Down Expand Up @@ -172,9 +188,7 @@ export const FormBody = (props: FormBodyProps) => {

{/* render step navigation*/}
<FormStepNavigation
stepLocalizations={stepLocalizations}
form={form}
isFormFinalized={isFormFinalized}
isFormFinalized={isFormFinalized.current}
history = {props.history}
/>
</div>
Expand Down
24 changes: 11 additions & 13 deletions src/@episerver/forms-react/src/components/FormStepNavigation.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
import React from "react";
import React, { useRef } from "react";
import { useForms, useFormsDispatch } from "../context/store";
import { FormCache, FormConstants, FormContainer, FormStep, StepDependCondition, SubmitButtonType, isNull } from "@episerver/forms-sdk";
import { DispatchFunctions } from "../context/dispatchFunctions";
import { useHistory } from "react-router-dom";

interface FormStepNavigationProps {
stepLocalizations: React.MutableRefObject<Record<string, string>>
form: FormContainer
isFormFinalized: React.MutableRefObject<boolean>
isFormFinalized: boolean
history?: any
}

export const FormStepNavigation = (props: FormStepNavigationProps) => {
const formContext = useForms()
const formCache = new FormCache()
const dispatch = useFormsDispatch()
const history = props.history
const depend = new StepDependCondition(props.form, formContext?.dependencyInactiveElements ?? [])
const { stepLocalizations, form, isFormFinalized } = props;
const dispatchFuncs = new DispatchFunctions(dispatch);
const formContext = useForms();
const formCache = new FormCache();
const form = formContext?.formContainer ?? {} as FormContainer;
const depend = new StepDependCondition(form, formContext?.dependencyInactiveElements ?? []);
const { isFormFinalized, history } = props;
const dispatchFuncs = new DispatchFunctions();
const stepLocalizations = useRef<Record<string, string>>(form.steps?.filter(s => !isNull(s.formStep.localizations))[0]?.formStep.localizations);

const submittable = true
const stepCount = form.steps.length;
Expand All @@ -29,7 +27,7 @@ export const FormStepNavigation = (props: FormStepNavigationProps) => {
const nextButtonDisableState = (currentStepIndex == stepCount - 1) || !submittable;
const progressWidth = (100 * currentDisplayStepIndex / stepCount) + "%";

const isShowStepNavigation = stepCount > 1 && currentStepIndex > -1 && currentStepIndex < stepCount && !isFormFinalized.current;
const isShowStepNavigation = stepCount > 1 && currentStepIndex > -1 && currentStepIndex < stepCount && !isFormFinalized;

const handlePrevStep = (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault()
Expand All @@ -44,7 +42,7 @@ export const FormStepNavigation = (props: FormStepNavigationProps) => {
const goToStep = (stepIndex: number) => {
var step = form.steps[stepIndex].formStep as FormStep

formCache.set<number>(FormConstants.FormCurrentStep + props.form.key, stepIndex)
formCache.set<number>(FormConstants.FormCurrentStep + form.key, stepIndex)
dispatchFuncs.updateCurrentStepIndex(stepIndex)

if (!isNull(step) && !isNull(step.properties.attachedContentLink)) {
Expand Down
5 changes: 3 additions & 2 deletions src/@episerver/forms-react/src/context/dispatchFunctions.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { FormContainer, ElementValidationResult, FormValidationResult, initFormState, IdentityInfo } from "@episerver/forms-sdk";
import { ActionType } from "./reducer";
import { useFormsDispatch } from "./store";

export class DispatchFunctions {
readonly _dispatch: any
constructor(dispatch: any){
this._dispatch = dispatch;
constructor(){
this._dispatch = useFormsDispatch();
}

updateValidation = (elementKey: string, validationResults: ElementValidationResult[]) => {
Expand Down
3 changes: 1 addition & 2 deletions src/@episerver/forms-react/src/hooks/useElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,12 @@ export interface ElementContext {

export const useElement = (element: FormElementBase) => {
const formContext = useForms();
const dispatch = useFormsDispatch();
const extraAttr = useRef<any>({});
const formValidation = new FormValidator(element);
const formCondition = new FormDependConditions(element)
const defaultValue = getDefaultValue(element);
const isVisible = useRef<boolean>(true);
const dispatchFuncs = new DispatchFunctions(dispatch);
const dispatchFuncs = new DispatchFunctions();
const elementRef = useRef<any>(null);

//build element state
Expand Down
7 changes: 6 additions & 1 deletion src/@episerver/forms-sdk/src/form-submit/formSubmit.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FormStorage } from "../form-storage";
import { FormValidator } from "../form-validator";
import { equals } from "../helpers";
import { equals, isNull } from "../helpers";
import { FormConstants, FormContainer, FormSubmission, FormValidationResult } from "../models";
import { ApiConstant } from "../form-loader/apiConstant";

Expand Down Expand Up @@ -104,6 +104,11 @@ export class FormSubmitter {
model.submissionData.forEach(data => {
let ovalue = data.value;
let key = `${FormConstants.FormFieldPrefix}${data.elementKey}`;

if(isNull(ovalue)) {
return;
}

// checking file upload elements, item must be File if any,
// for using Object.getPrototypeOf(variable) variable must be object type
if(Object.getPrototypeOf(ovalue) === FileList.prototype && (ovalue as FileList).length > 0) {
Expand Down
11 changes: 7 additions & 4 deletions src/@episerver/forms-sdk/src/helpers/initFormState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { FormCache } from "../form-cache";
import { FormStorage } from "../form-storage";
import { ElementValidationResult, FormConstants, FormContainer, FormState, FormSubmission, FormValidationResult, StepDependencies, ValidatableElementBaseProperties } from "../models";
import { getDefaultValue } from "./elementHelper";
import { isNull } from "./utils";
import { equals, isNull } from "./utils";

/**
* Function to initialize FormState object
Expand Down Expand Up @@ -39,9 +39,12 @@ export function initFormState(formContainer: FormContainer): FormState {
stepDependencies = stepDependencies.concat({ elementKey: s.formStep.key, isSatisfied: false });
});

//binding the elements with stored input value between Next/Prev navigation
if (formData.length > 0) {
formSubmissions = formData;
//binding the elements with saved data between Next/Prev navigation
if(formData.length > 0){
formSubmissions = formSubmissions.map(s => {
let savedData = formData.find(d => equals(d.elementKey, s.elementKey));
return savedData ? savedData : s;
});
}

return {
Expand Down

0 comments on commit ed84cb5

Please sign in to comment.