Skip to content

Xperimental form definition syntax

Dave Methvin (USDS) edited this page Aug 27, 2018 · 1 revision

Many early users have pointed out the current schema/uiSchema setup as a high pain point for their use of the library. This page explores an alternative concise syntax that involves less redundancy.

The form chosen for a comparison was the VA Form 1990 Healthcare Benefits form.

Page 1

Concise (~32 lines)

{
  chapter: 'Applicant Information',
  label: '',
  widget: 'PageDisplayGroup',
  children: [
    // Short (array) syntax, when widget defaults are sufficient
    // [ field, widget, label, initialValue ]
    [ 'veteranFullName', 'FullName', 'Full Name' ],
    [ 'veteranSocialSecurityNumber', 'SSN', 'Social Security Number' ],
    [ 'veteranDateOfBirth', 'AdultBirthDate', 'Date of Birth' ],
    [ 'gender', 'Gender', 'Gender' ]
  ]
},

// Person should be at least 18 years old; used below
var MaxAdultBirthDate = new Date();
MaxAdultBirthDate.setFullYear(MaxAdultBirthDate.getFullYear()-17);

// Widgets can use already-defined widgets; this is in long (object) syntax
'AdultBirthDate': {
  widget: 'Date',
  label: 'Date of Birth',
  validation: {
    minDate: [
      '1900-01-01',
      'Date of birth must be after ${minDate}'
    ],
    maxDate: [
      MaxAdultBirthDate, // Converted to ISO8601 string
      'Date of birth must be no later than ${maxDate}'
    ]
  }
},

Current (~75 lines)

applicantInformation: _.merge(applicantInformation(fullSchema1990, {
  isVeteran: true,
  fields: [
    'veteranFullName',
    'veteranSocialSecurityNumber',
    'veteranDateOfBirth',
    'gender'
  ],
  required: [
    'veteranFullName',
    'veteranSocialSecurityNumber',
    'veteranDateOfBirth',
  ]
}), {
  uiSchema: {
    veteranDateOfBirth: {
      'ui:validations': [
        (errors, dob) => {
          // If we have a complete date, check to make sure it’s a valid dob
          if (/\d{4}-\d{2}-\d{2}/.test(dob) && moment(dob).isAfter(moment().endOf('day').subtract(17, 'years'))) {
            errors.addError('You must be at least 17 to apply');
          }
        }
      ]
    }
  }
})

imported applicant information

return {
  path: 'applicant/information',
  title: 'Applicant information',
  initialData: {},
  uiSchema: _.assign({
    'ui:order': fields,
    'ui:description': applicantDescription,
    [`${prefix}FullName`]: fullNameUI,
    [`${prefix}DateOfBirth`]: _.assign(currentOrPastDateUI('Date of birth'),
      {
        'ui:errorMessages': {
          pattern: 'Please provide a valid date',
          futureDate: 'Please provide a valid date'
        }
      }
    ),
    gender: {
      'ui:widget': 'radio',
      'ui:title': 'Gender',
      'ui:options': {
        labels: labels.gender || genderLabels
      }
    },
    relationship: {
      'ui:widget': 'radio',
      'ui:title': 'What’s your relationship to the Servicemember whose benefit is being transferred to you?',
      'ui:options': {
        labels: labels.relationship || relationshipLabels
      }
    }
  }, personId.uiSchema(prefix, 'view:noSSN')),
  schema: {
    type: 'object',
    definitions: _.pick([
      'fullName',
      'relationship',
      'ssn',
      'gender',
      'date',
      'vaFileNumber'
    ], schema.definitions),
    required,
    properties: _.pick(fields, possibleProperties)
  }
};

Page 2

Concise (~30 lines)

{
  chapter: 'Benefits Eligibility',
  label: '',
  widget: 'PageDisplayGroup',
  children: [
    // Long (object) syntax
    {
      widget: 'Fieldset',
      label: 'You may be eligible for more than 1 education benefit ...',
      validation: {
        errorUnless: [
          'chapter33Checkbox|chapter30Checkbox|chapter1606Checkbox|chapter32Checkbox',
          'You must select at least one benefit.'
        ]
      },
      children: [
        [ 'chapter33Checkbox', 'Checkbox', 'Post-9/11 GI Bill (Chapter 33) ...' ],
        {
          widget: 'HTML',
          showIf: 'chapter33Checkbox',
          className: 'expand-under',
          label: 'When you choose to apply for your Post-9/11 benefit, ...'
        },
        [ 'chapter30Checkbox', 'Checkbox','Montgomery GI Bill (MGIB-AD, Chapter 30) ...' ],
        [ 'chapter1606Checkbox', 'Checkbox','Montgomery GI Bill Selected Reserve (MGIB-SR, Chapter 1606) ...' ],
        [ 'chapter32Checkbox', 'Checkbox', 'Post-Vietnam Era Veterans’ Educational Assistance Program ...' ]
      ]
    }
  ]
},

Current (58 lines)

benefitsEligibility: {
  title: 'Benefits eligibility',
  path: 'benefits-eligibility/benefits-selection',
  uiSchema: {
    'ui:description': benefitsEligibilityBox,
    'view:selectedBenefits': {
      'ui:title': 'Select the benefit that is the best match for you.',
      'ui:validations': [
        validateBooleanGroup
      ],
      'ui:errorMessages': {
        atLeastOne: 'Please select at least one benefit'
      },
      'ui:options': {
        showFieldLabel: true
      },
      chapter33: {
        'ui:title': benefitsLabels.chapter33,
        'ui:options': {
          expandUnderClassNames: 'schemaform-expandUnder-indent',
        }
      },
      'view:chapter33ExpandedContent': {
        'ui:description': 'When you choose to apply for your Post-9/11 benefit, you have to relinquish (give up) 1 other benefit you may be eligible for. You’ll make this decision on the next page.',
        'ui:options': {
          expandUnder: 'chapter33',
        }
      },
      chapter30: {
        'ui:title': benefitsLabels.chapter30
      },
      chapter1606: {
        'ui:title': benefitsLabels.chapter1606
      },
      chapter32: {
        'ui:title': benefitsLabels.chapter32
      }
    }
  },
  schema: {
    type: 'object',
    required: ['view:selectedBenefits'],
    properties: {
      'view:selectedBenefits': {
        type: 'object',
        properties: {
          chapter33,
          'view:chapter33ExpandedContent': {
            type: 'object',
            properties: {}
          },
          chapter30,
          chapter1606,
          chapter32
        }
      }
    }
  }
},

Page 3 (Optional depending on answers in page 2)

Concise (~25 lines)

{
  chapter: 'Benefits Eligibility',
  label: 'Benefits Relinquishment',
  widget: 'PageDisplayGroup',
  showIf: 'chapter33Checkbox',
  required: [ 'benefitsRelinquished' ],
  children: [
    // Hidden field containing today's date (LOCAL TIME), see function above
    [ 'benefitsRelinquishedDate', 'Hidden', '', () => {
      const now = Date.now();
      return now.getFullYear()+'-'+(now.getMonth()+1)+'-'+now.getDate();
    }],
    {
      field: 'benefitsRelinquished',
      widget: 'RadioGroup',
      label: 'Because you chose to apply for your Post-9/11 benefit, ...',
      children: [
        { value: 'chapter32', label: 'I\'m only eligible for the post-9/11 GI Bill' },
        { value: 'chapter30', label: 'Montgomery GI Bill (MGIB-AD, Chapter 30)' },
        { value: 'chapter1606', label: 'Montgomery GI Bill Selected Reserve (MGIB-SR, Chapter 1606)' },
        { value: 'chapter1607', label: 'Reserve Educational Assistance Program (REAP, Chapter 1607)' }
      ]
    }
  ]
},

Current (~54 lines)

benefitsRelinquishment: {
  title: 'Benefits relinquishment',
  path: 'benefits-eligibility/benefits-relinquishment',
  depends: (formData) => formData['view:selectedBenefits'].chapter33,
  initialData: {
    'view:benefitsRelinquishedContainer': {
      benefitsRelinquishedDate: moment().format('YYYY-MM-DD')
    }
  },
  uiSchema: {
    'ui:title': 'Benefits relinquishment',
    'ui:description': benefitsRelinquishmentWarning,
    'view:benefitsRelinquishedContainer': {
      'ui:field': BenefitsRelinquishmentField,
      benefitsRelinquished: {
        'ui:title': 'I choose to give up:',
        'ui:widget': 'radio',
        'ui:options': {
          labels: benefitsRelinquishmentLabels,
        }
      },
      benefitsRelinquishedDate: _.merge(dateUI('Effective date'), {
        'ui:required': (formData) => _.get('view:benefitsRelinquishedContainer.benefitsRelinquished', formData) !== 'unknown'
      })
    },
    'view:questionText': {
      'ui:description': benefitsRelinquishedDescription
    }
  },
  schema: {
    type: 'object',
    properties: {
      'view:benefitsRelinquishedContainer': {
        type: 'object',
        required: ['benefitsRelinquished'],
        properties: {
          benefitsRelinquished,
          benefitsRelinquishedDate
        }
      },
      'view:questionText': {
        type: 'object',
        properties: {}
      }
    }
  }
}
export const benefitsRelinquishmentLabels = {
  unknown: 'I’m only eligible for the Post-9/11 GI Bill',
  chapter30: 'Montgomery GI Bill (MGIB-AD, Chapter 30)',
  chapter1606: 'Montgomery GI Bill Selected Reserve (MGIB-SR, Chapter 1606)',
  chapter1607: 'Reserve Educational Assistance Program (REAP, Chapter 1607)'
};
Clone this wiki locally