Form Generator

FormGenerator is a client-side orchestrator for multi-step and multi-section forms. It renders questions from a declarative configuration (usually returned by a backend API), manages step navigation, validation, dependent-question visibility, and answer collection.

Source: src/client/components/FormGenerator/index.js

How it works

  1. Instantiate โ€” Pass a DOM container and a config object tonew FormGenerator(container, config). The constructor extendsFormComponent and immediately renders markup into the container.
  2. Render steps โ€” One wrapper (.lta-form-generator__step) is created per step. Questions for each step are placed in.lta-form-generator__question-container placeholders.
  3. Mount question components โ€” Each question's questionType maps to a dedicated component (e.g. RadioComponent, SchoolFinder,DateComponent). The component instance is stored on question.ref.
  4. Collect answers โ€” When a question changes, updateAnswers() merges the answer into formGenerator.answers keyed by [step][questionId].
  5. Navigate โ€” The Next button validates the current step viastepValidator(), then advances currentStep. On the final step, Next becomes Submit and calls submitForm().
  6. Dependent questions โ€” Radio/checkbox options can declaredependentQuestion: [ids]. When the selection changes,beforeDepChangeHandler() hides previous dependents, clears their answers, and shows new ones.

Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Page / Modal (container element)                   โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚  FormGenerator                                โ”‚  โ”‚
โ”‚  โ”‚  โ”œโ”€โ”€ TopBackButton / ExitButton               โ”‚  โ”‚
โ”‚  โ”‚  โ”œโ”€โ”€ Stepper (optional progress bar)          โ”‚  โ”‚
โ”‚  โ”‚  โ”œโ”€โ”€ Step 1..N (.lta-form-generator__step)    โ”‚  โ”‚
โ”‚  โ”‚  โ”‚     โ””โ”€โ”€ Question containers                โ”‚  โ”‚
โ”‚  โ”‚  โ”‚           โ””โ”€โ”€ RadioComponent, Select, โ€ฆ    โ”‚  โ”‚
โ”‚  โ”‚  โ””โ”€โ”€ Next / Submit button                     โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Form types

TypeConstantBehaviour
Step formFormTypes.StepFormOne step visible at a time. Next advances; only the final step shows the submit label.
Section formFormTypes.SectionFormAll steps rendered on one page with section titles. Submit button hidden until the last section unless submitButton.alwaysVisible is set.

Config options

OptionDescription
questionsArray of question objects (required)
totalStepsNumber of steps (default: 3)
formTypestepForm or sectionForm
themeColorCSS variable name for stepper accent (e.g. color-lta-blue)
isHideProgressHide the stepper when true (default: hidden)
isBackButtonEnabledShow top back button
showBottomBackButtonShow bottom back button below Next
isExitButtonEnabledShow exit button with exitButtonHandler
backButtonHandlerFirstStepCustom handler when back is pressed on step 1 (default: history.back())
hasSeparatorAdd separators between questions / sections
stepVisibilityBoolean array controlling stepper visibility per step
conditionalStepsVisiblityHide stepper when a step has no visible questions
mainPageElementElement whose background class updates per step
submitButton{ title, dataTarget, alwaysVisible }
saveAddressPassed to VenueAddressSearch questions (default: true)
radioAnswerFieldField on radio options used as the answer value (default: label)
titlePatternHeading layout pattern passed to question components
wrapperClassesPer-step CSS classes array

Public API

Method / propertyDescription
onChange(callback)Fires when any answer changes
onStepChange(callback)Fires after navigating to a new step
onSubmit(callback)Fires on final-step submit with (answers, value)
formValidation()Async โ€” validates all steps; sets value on success
stepValidator(step)Async โ€” validates a single step
enableSubmitButton()Re-enables submit after async work completes
reRenderForm(updatedStepData)Re-render a question after server-side validation errors
getAnswerByQuestionType(type)Read an answer value by questionType
unload()Remove all question components and clear the container
answersCurrent answers object (by step โ†’ question id)
currentStep1-based current step index
questionsFiltered question array (questions without id are removed)

Answer structure


// Answers are grouped by step, then by question id:
{
  1: {
    1: { fieldName: 'addVenue', value: 'Yes', dependentQuestion: [2], ... }
  },
  2: {
    2: { fieldName: 'venue', value: { venueName: '...' }, ... }
  }
}
		

Question types

Each questionType maps to a component in renderQuestion(). Search-type questions merge extra API config from src/client/components/FormGenerator/data.js.

questionTypeComponent
radioRadioComponent
checkboxCheckboxComponent
selectSelectComponent
textArea / textInputInputField
dateComponentDateComponent
duetDateComponentDuetDateComponent
fileUploadFileUploadComponent
infoKeyPoints (read-only)
schoolSearchSchoolFinder
universitySearch / nurserySearch / collegeSearchSchoolFinder (variant config)
coachSearchCoachFinder
parkFinder / coachVenueSearch / activatorVenueSearchParkFinder
venueAddressSearchVenueAddressSearch
addressFinderAddressFinder
tournamentSearchTournamentFinder
checkoutCheckoutComponent
playerList / sessionPlayerListPlayerList / SessionPlayersList
contactNumber / childContactNumberContactNumber
formTableFormTable
householdAccountOnboarding / householdMemberListHouseHoldAccountOnboarding / HouseHoldMemberList

Question schema


{
  id: 1,                        // required โ€” unique question id
  step: 1,                      // which step this question belongs to
  fieldName: 'addVenue',        // key used in API payloads
  question: 'Your venues',      // main heading
  stepperTitle: 'Your venues',  // stepper label for this step
  instructionText: 'Are you affiliated with a venue?',
  errorMessage: 'Please select an option',
  questionType: 'radio',        // see QuestionTypes
  visible: true,                // false for dependent questions shown later
  isOptional: false,
  options: [
    {
      id: 'yes',
      label: 'Yes',
      dependentQuestion: [2]     // show question id 2 when selected
    },
    {
      id: 'no',
      label: 'No',
      dependentQuestion: []
    }
  ]
}
		

Basic usage


import FormGenerator, { FormTypes } from '../../components/FormGenerator';

const container = document.querySelector('.js-my-form');

const formGenerator = new FormGenerator(container, {
  questions: stepsData,       // array of question objects (often from an API)
  totalSteps: 3,
  formType: FormTypes.StepForm,
  themeColor: 'color-lta-blue',
  isBackButtonEnabled: true,
  submitButton: {
    title: 'Submit',
    alwaysVisible: false
  }
});

formGenerator.onChange((answers) => {
  console.log('Answers updated:', answers);
});

formGenerator.onStepChange((step) => {
  console.log('Current step:', step);
});

formGenerator.onSubmit((answers, value) => {
  console.log('Form submitted:', answers, value);
});
		

Dependent questions

Dependent questions start with visible: false. When a parent radio/checkbox option is selected, its dependentQuestion array lists question id values to show. Switching options hides previous dependents, recursively clears nested dependents, and removes their answers from the current step.

Steps with no visible questions are skipped automatically during Next/Back navigation whenconditionalStepsVisiblity is enabled.

Validation

stepValidator(step) iterates visible, non-optional questions on that step and calls each component's validate(true). Questions with shouldRender() returningfalse are skipped. Optional questions and answers with value: 'N/A' are also skipped.

The Next button visibility is controlled by nextValidator(), which hides Next while any visible required question's shouldShowNext() returns true (used by components that need inline confirmation before advancing).

Live demo

Step form with radio dependent questions and a select field. Open the browser console to seeonChange and onSubmit output.

Event: none

Used in

Coaches onboarding, school onboarding, checkout, accreditation renewal, officials licence renewal, activators portal, competition eligibility, tennis leader resource form, and other multi-step flows that fetch Steps from an API and mount FormGenerator into a page or modal container.

For modal-based flows, useForm Modaland mount FormGenerator into contentEl.