Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update "Example form.js" document #212

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 88 additions & 63 deletions docs/building-a-form/quick-start-example-formjs-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,22 @@
Use this example `form.js` file to build a basic form. For more information about `form.js`, see "[Creating a form config file](creating-a-form-config-file.md)."

```js
{
// Because pages must be unique across all chapters, it's safer to store them in an object
const formPages = {
pageOne: 'pageOne'
}

// Since field IDs are repeated in the form config and must be unique throughout the form,
// it's safer and more convenient to store them in an object
const formFields = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with the diagnosis that repeating names across all these objects is confusing and error prone. Ideally we should be grouping most of this information in a single object, rather than using the field name as a key to unite far-flung objects. That's related to design changes though, so perhaps this is the best we can do with the current formConfig design.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an interesting idea. I agree with what @dmethvin-gov said, that we might be making further design changes to the form config that would change how some of this works. However, in general this change seems a bit prescriptive. It's not necessary for the library to work to put the field names in a separate object, it just potentially makes it less error prone. But I'm not sure if that recommendation belongs in the scope of this library, or should we just leave it up to individual developers to write their form config in whatever way makes most sense to them? If this is the new way Vets.gov is going to be writing their form configs, should this prescription belong in the Vets.gov codebase instead of here?

fieldOne: 'fieldOne',
viewFieldTwo: 'view:fieldTwo',
viewGroup: 'view:artificialGroup'
}

const formConfig = {
// Prefix string to add to the path for each page.
urlPrefix: '',
urlPrefix: '/',

// The introduction page component. To exclude an introduction page, remove this component.
introduction: IntroductionComponent,
Expand All @@ -16,95 +29,107 @@ Use this example `form.js` file to build a basic form. For more information abou
confirmation: ConfirmationComponent,

// The prefix for Google Analytics events that are sent for different form actions.
trackingPrefix: '',
trackingPrefix: 'unique-ga-id-',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a PR in #196 that moves this out to the app, in which case trackingPrefix wouldn't be used.


// The title of the form, rendered on all pages.
title: '',

// The subtitle of the form, usually the form number. The subtitle is rendered on all pages when there's also a title.
subTitle: '',

// Schema definitions that can be referenced on any page. These are added to each page's schema in the reducer code, so that you don't have to put all of the common fields in the definitions property in each page schema. For more information on definitions, see schema.definitions below.
// Schema definitions that can be referenced on any page. These are added to each page's schema in the reducer code, so that you don't have to put all of the common fields in the definitions property in each page schema. For more information on definitions, see `schema.definitions` below.
defaultDefinitions: {},

// When a user begins completing a pre-filled form, this function is called after data migrations are run for pre-filled data in order to make necessary updates to the data or form schema ahead of time.
prefillTransformer: (pages, formData, metadata ) => { pages, formData, metadata }
prefillTransformer: (pages, formData, metadata ) => { pages, formData, metadata },

// The object that contains the configuration for each chapter. Each property is the key for a chapter.
// The object that contains the configuration for each chapter.
chapters: {
// Each property is the key for a chapter.
chapterOne: {

// The title of the chapter.
title: '',
// The title of the chapter. Rendered near the top of the page, under the segmented controller
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would change segmented controller to segmented progress bar, because that's how we refer to it elsewhere.

title: '',

// The object that contains the pages in each chapter. Each property is the key for a page, and should be unique across chapters.
pages: {
// The object that contains the pages in each chapter.
pages: {
// Each property is the key for a page, and should be unique across chapters.
[formPages.pageOne]: {

// The URL for the page.
path: 'some-path',
// The URL for the page. Must be unique across all pages in all chapters.
path: 'some-path',

// The title of the page that renders on the review page.
title: '',
// This can also be a function that receives the current data as a parameter.
title: formData => `A title for ${formData.thing}`,

// Any initial data that should be set for the form.
initialData: {
field1: 'Default string'
},

// Specifies that a page will turn its schema into a page for each item in an array, such as an array of children with a page
// for each child. The schema/uiSchema for this type of page should be built as usual for an array field.
showPagePerItem: true,
// The path to the array.
arrayPath: 'children',
// Items in the array that should not have a page.
itemFilter: () => true,
// You must specify a path with an :index parameter.
path: 'some-path/:index',

// The JSON schema object for the page, following the JSON Schema format.
schema: {
type: 'object',
// A schema's properties refer to definitions. For example:
// "homePhone": { "$ref": "#/definitions/phone" }
// In the configuration file, the definition for `phone` must be added into definitions in order to be parsed correctly and added to `homePhone`.
definitions: {},
properties: {
field1: {
type: 'string'
},
// Fields of type `string`, `boolean`, `number`, and `array` that begin with `view:` are excluded from data that's sent to
// the server. Instead, their children are merged into the parent object and sent to the server. Use these to exclude fields
// from being sent to the server, such as a question that's only used to reveal other questions, or to group related
// questions together to be conditionally revealed that aren't in an object in the schema.
'view:field2': {
type: 'string'
// The title of the page that renders on the review page when this chapter is expanded.
title: '',
// This can also be a function that receives the current data as a parameter:
// title: formData => `A title for ${formData.thing}`,

// Any initial data that should be set for the form.
initialData: {
[formFields.fieldOne]: 'Default string'
},
'view:artificialGroup'{

// Specifies that a page will turn its schema into a page for each item in an array, such as an array of children with a page
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should add a header comment that clarifies that the following group of things are used for array fields only. The additional path reference is a bit confusing because we already specified the path earlier, so perhaps that header will make it more clear that this is only necessary for arrays.

// for each child. The schema/uiSchema for this type of page should be built as usual for an array field.
showPagePerItem: true,
// The path to the array.
arrayPath: 'children',
// Items in the array that should not have a page.
itemFilter: () => true,
// You must specify a path with an :index parameter.
path: 'some-path/:index',

// The JSON schema object for the page, following the JSON Schema format.
schema: {
type: 'object',
// A schema's properties refer to definitions. For example:
// "homePhone": { "$ref": "#/definitions/phone" }
// In the configuration file, the definition for `phone` must be added into definitions in order to be parsed correctly and added to `homePhone`.
definitions: {},
// Set an array of fields on this page that are required
required: [
[formFields.fieldOne]
]
properties: {
// `view:artificialGroup` is flattened. `subField1` and `subField2` are siblings of `field1` when sent to the API.
subField1: {
[formFields.fieldOne]: {
type: 'string'
},
subField2: {
type: 'boolean'
// Fields of type `string`, `boolean`, `number`, and `array` that begin with `view:` are excluded from data that's sent to
// the server. Instead, their children are merged into the parent object and sent to the server. Use these to exclude fields
// from being sent to the server, such as a question that's only used to reveal other questions, or to group related
// questions together to be conditionally revealed that aren't in an object in the schema.
[formFields.viewFieldTwo]: {
type: 'string'
},
[formFields.viewGroup]: {
type: 'object',
properties: {
// `view:artificialGroup` is flattened. `subField1` and `subField2` are siblings of `[formFields.fieldOne]` when sent to the API.
subField1: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to stick with putting all fields in an object, subField1 and subField2 need to be added.

type: 'string'
},
subField2: {
type: 'boolean'
}
}
}
}
}
}
},
},

// See "About the `schema` and `uiSchema` objects" below.
uiSchema: {
'ui:title': 'My form',
field1: {
'ui:title': 'My field'
// See "About the `schema` and `uiSchema` objects" below.
uiSchema: {
'ui:title': 'My form',
[formFields.fieldOne]: {
'ui:title': 'My field'
}
}
}
}
}
}
}
```

export default formConfig;
```
[About the `schema` and `uiSchema` objects](./about-the-schema-and-uischema-objects.md)
[Back to *Building a Form*](./README.md)