Skip to content

Commit

Permalink
Restore code save currentStepIndex in cache
Browse files Browse the repository at this point in the history
  • Loading branch information
hungoptimizely committed Dec 20, 2023
1 parent 10595e0 commit 2e58c93
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 35 deletions.
45 changes: 25 additions & 20 deletions src/@episerver/forms-react/src/components/FormBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,15 @@ export const FormBody = (props: FormBodyProps) => {
const dispatchFunctions = new DispatchFunctions();
const stepDependCondition = new StepDependCondition(form, inactiveElements);
const stepHelper = new StepHelper(form);
const currentPageUrl = props.currentPageUrl ?? window.location.pathname;
const currentPageUrl = props.currentPageUrl ?? window.location.href;

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

const formCache = new FormCache();
const localFormCache = new FormCache(window.localStorage);
const currentStepIndex = useMemo(()=>{
return stepHelper.getCurrentStepIndex(currentPageUrl);
},[currentPageUrl]);
const currentStepIndex = formContext?.currentStepIndex ?? 0;

//TODO: these variables should be get from api or sdk
const validateFail = useRef<boolean>(false),
Expand Down Expand Up @@ -68,11 +66,11 @@ export const FormBody = (props: FormBodyProps) => {
message.current = error;
}

const handleSubmit = async (e: any):Promise<boolean> => {
const handleSubmit = (e: any) => {
e.preventDefault();

if (!form.properties.allowAnonymousSubmission && isNullOrEmpty(formContext?.identityInfo?.accessToken)) {
return false;
return;
}

//Find submit button, if found then check property 'finalizeForm' of submit button. Otherwise, button Next/Previous was clicked.
Expand All @@ -94,7 +92,7 @@ export const FormBody = (props: FormBodyProps) => {
let invalid = stepHelper.getFirstInvalidElement(formValidationResults, currentStepIndex);
if(!isNullOrEmpty(invalid)){
dispatchFunctions.updateFocusOn(invalid);
return false;
return;
}

let isLastStep = currentStepIndex === form.steps.length - 1;
Expand All @@ -110,14 +108,10 @@ export const FormBody = (props: FormBodyProps) => {
}

//submit data to API
isSuccess.current = false;
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>");
}
else {
if(!response.success) {
//ignore validation message
showError(response.messages.filter(m => isNullOrEmpty(m.identifier)).map(m => m.message).join("<br>"));
}
Expand Down Expand Up @@ -147,20 +141,26 @@ export const FormBody = (props: FormBodyProps) => {
localFormCache.set(submissionStorageKey, response.submissionKey);

if (isFormFinalized.current) {
formCache.remove(FormConstants.FormCurrentStep + form.key);
localFormCache.remove(submissionStorageKey);
message.current = response.messages.map(m => m.message).join("<br>");
}
}).catch((e: ProblemDetail) => {
if(e.status === 401) {
//clear access token to ask login again
dispatchFunctions.updateIdentity({} as IdentityInfo);
formCache.remove(FormConstants.FormAccessToken);
showError(e.detail);
switch(e.status){
case 401:
//clear access token to ask login again
dispatchFunctions.updateIdentity({} as IdentityInfo);
formCache.remove(FormConstants.FormAccessToken);
break;
case 400:
//validate fail
break;
}

showError(e.detail);
}).finally(()=>{
dispatchFunctions.updateIsSubmitting(false);
});

return isSuccess.current;
}

useEffect(() => {
Expand All @@ -173,6 +173,11 @@ export const FormBody = (props: FormBodyProps) => {
}
}, [props.identityInfo?.accessToken]);

//reset when change page
useEffect(()=>{
isSuccess.current = false;
},[currentStepIndex]);

isMalFormSteps && showError("Improperly formed FormStep configuration. Some steps are attached to pages, while some steps are not attached, or attached to content with no public URL.");

return (
Expand Down Expand Up @@ -229,7 +234,7 @@ export const FormBody = (props: FormBodyProps) => {
handleSubmit = {handleSubmit}
isMalFormSteps = {isMalFormSteps}
isStepValidToDisplay = {isStepValidToDisplay}
currentStepIndex={currentStepIndex}
isSuccess = {isSuccess.current}
/>
</div>
</form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export interface FormContainerProps {
export function FormContainerBlock(props: FormContainerProps){
const stepBuilder = new StepBuilder(props.form);
const form = stepBuilder.buildForm();
const state = initFormState(form);
const state = initFormState(form, props.currentPageUrl);

{/* finally return the form */}
return (
Expand Down
34 changes: 24 additions & 10 deletions src/@episerver/forms-react/src/components/FormStepNavigation.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
import React, { useRef } from "react";
import React, { useEffect, useRef } from "react";
import { useForms } from "../context/store";
import { FormContainer, FormStep, StepDependCondition, SubmitButtonType, isNull } from "@episerver/forms-sdk";
import { FormCache, FormConstants, FormContainer, StepDependCondition, SubmitButtonType, isNull, isNullOrEmpty } from "@episerver/forms-sdk";
import { DispatchFunctions } from "../context/dispatchFunctions";

interface FormStepNavigationProps {
isFormFinalized: boolean;
history?: any;
handleSubmit: (e: any) => Promise<boolean>;
handleSubmit: (e: any) => void;
isMalFormSteps: boolean;
isStepValidToDisplay: boolean;
currentStepIndex: number;
isSuccess: boolean;
}

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

const submittable = true
const stepCount = form.steps.length;

const currentStepIndex = formContext?.currentStepIndex ?? 0;
const currentDisplayStepIndex = currentStepIndex + 1;
const prevButtonDisableState = (currentStepIndex == 0) || !submittable;
const nextButtonDisableState = (currentStepIndex == stepCount - 1) || !submittable;
Expand All @@ -35,19 +40,28 @@ export const FormStepNavigation = (props: FormStepNavigationProps) => {

const handleNextStep = async (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault()
let isSuccess = await handleSubmit(event);
isSuccess && goToStep(depend.findNextStep(currentStepIndex) ?? 0);
handleSubmit(event);
isNextStep.current = true;
}

const goToStep = (stepIndex: number) => {
var step = form.steps[stepIndex].formStep as FormStep
var attachedContentLink = form.steps[stepIndex]?.formStep?.properties?.attachedContentLink;

if (!isNull(step) && !isNull(step.properties.attachedContentLink)) {
let url = new URL(step.properties.attachedContentLink)
formCache.set<number>(FormConstants.FormCurrentStep + form.key, stepIndex);
dispatchFuncs.updateCurrentStepIndex(stepIndex);
if (!isNullOrEmpty(attachedContentLink)) {
let url = new URL(attachedContentLink);
history && history.push(url.pathname);
}
}

useEffect(()=>{
if(isSuccess && isNextStep.current) {
goToStep(depend.findNextStep(currentStepIndex) ?? 0);
isNextStep.current = false;
}
},[isSuccess]);

return (
<>
{isShowStepNavigation &&
Expand Down
7 changes: 7 additions & 0 deletions src/@episerver/forms-react/src/context/dispatchFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ export class DispatchFunctions {
});
}

updateCurrentStepIndex = (currentStepIndex?: number) => {
this._dispatch({
type: ActionType.UpdateCurrentStepIndex,
currentStepIndex
});
}

updateIsSubmitting = (isSubmitting?: boolean) => {
this._dispatch({
type: ActionType.UpdateIsSubmitting,
Expand Down
7 changes: 7 additions & 0 deletions src/@episerver/forms-react/src/context/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export enum ActionType {
UpdateFocusOn = "UpdateFocusOn",
UpdateIdentityInfo = "UpdateIdentityInfo",
UpdateSubmissionKey = "UpdateSubmissionKey",
UpdateCurrentStepIndex = "UpdateCurrentStepIndex",
UpdateIsSubmitting = "UpdateIsSubmitting"
}

Expand Down Expand Up @@ -78,6 +79,12 @@ export function formReducer(formState: FormState, action: any) {
submissionKey: action.submissionKey
} as FormState
}
case ActionType.UpdateCurrentStepIndex: {
return {
...formState,
currentStepIndex: action.currentStepIndex
} as FormState
}
case ActionType.UpdateIsSubmitting: {
return {
...formState,
Expand Down
4 changes: 2 additions & 2 deletions src/@episerver/forms-sdk/src/form-step/stepHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export class StepHelper {
* @param currentPageUrl
* @returns
*/
getCurrentStepIndex (currentPageUrl: string): number {
getCurrentStepIndex (currentPageUrl?: string): number {
let currentStepIndex = 0;

if(this.isAllStepsAreNotLinked()){
Expand All @@ -48,7 +48,7 @@ export class StepHelper {
else {
this._form.steps.every((s, i)=>{
let url = new URL(s.formStep.properties?.attachedContentLink, this._tempBaseUrl);
let pageUrl = new URL(currentPageUrl, this._tempBaseUrl)
let pageUrl = new URL(currentPageUrl ?? "/", this._tempBaseUrl)
if(equals(pageUrl.pathname, url.pathname)){
currentStepIndex = i;
return false;
Expand Down
14 changes: 12 additions & 2 deletions src/@episerver/forms-sdk/src/helpers/initFormState.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import { FormCache } from "../form-cache";
import { StepHelper } from "../form-step";
import { FormStorage } from "../form-storage";
import { ElementValidationResult, FormConstants, FormContainer, FormState, FormSubmission, FormValidationResult, StepDependencies, ValidatableElementBaseProperties } from "../models";
import { getDefaultValue } from "./elementHelper";
import { equals, isNull } from "./utils";
import { equals, isNull, isNullOrEmpty } from "./utils";

/**
* Function to initialize FormState object
* @param formContainer A form container
* @returns An object of FormState
*/
export function initFormState(formContainer: FormContainer): FormState {
export function initFormState(formContainer: FormContainer, currentPageUrl?: string): FormState {
const formStorage = new FormStorage(formContainer);
const formData = formStorage.loadFormDataFromStorage();
const formCache = new FormCache();
const stepHelper = new StepHelper(formContainer);

let formSubmissions = [] as FormSubmission[];
let formValidationResults = [] as FormValidationResult[];
Expand Down Expand Up @@ -45,10 +49,16 @@ export function initFormState(formContainer: FormContainer): FormState {
});
}

let stepIndexCached = formCache.get(FormConstants.FormCurrentStep + formContainer.key) as string;
let currentStepIndex = isNullOrEmpty(stepIndexCached)
? stepHelper.getCurrentStepIndex(currentPageUrl)
: parseInt(stepIndexCached);

return {
isReset: false,
focusOn: "",
dependencyInactiveElements: [],
currentStepIndex,
formSubmissions,
formValidationResults,
stepDependencies,
Expand Down
1 change: 1 addition & 0 deletions src/@episerver/forms-sdk/src/models/enums/FormConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
export enum FormConstants {
FormAccessToken = "form_access_token",
FormFieldPrefix = "__field_",
FormCurrentStep = "form_current_step_",
FormSubmissionId = "form_submission_id_"
}
1 change: 1 addition & 0 deletions src/@episerver/forms-sdk/src/models/states/FormState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ export interface FormState {
focusOn: string,
identityInfo?: IdentityInfo
submissionKey?: string
currentStepIndex: number
isSubmitting?: boolean
}

0 comments on commit 2e58c93

Please sign in to comment.