0.0.3 • Published 6 years ago

formscript v0.0.3

Weekly downloads
2
License
GPLv3
Repository
github
Last release
6 years ago

Formscript

Version 0.0.4

Build Status FOSSA Status Known Vulnerabilities JavaScript Style Guide lerna Dependabot badge PRs Welcome

This document defines a JSON-based language used to describe form-content declaratively. The forms thus defined may be rendered and executed by software. In this document, such software is referred to as an "app".

Table of Contents

Structure of a Form

A Form is represented by a JSON Object.

Example: Simple Form

The content of a form is specified by configuring one or more widgets, which are represented by JSON objects.

  • In this example, a form is defined that contains two widgets, one that defines a suitable header (with some text and an accompanying image), followed by a second widget for letting the user enter their name.
{
  "widgets": [
    {
      "type": "header",
      "attributes": {
        "heading": "Register!",
        "desc": "Let's get to know each other a bit better...",
        "backgroundImage": "happyPeople.jpg",
        "backgroundImageAltText": "Beautiful people smiling around a laptop"
      }
    },
    {
      "id": "name",
      "type": "text",
      "attributes": {
        "heading": "Name",
        "placeholder": "e.g. Lucy Smith",
        "mandatory": true,
        "minCharacters": 1,
        "maxCharacters": 100,
        "help": "Enter your full name here"
      }
    }
  ]
}
  • The order that objects are defined within widgets is important, representing the order users will encounter them.

Concepts

Formscript is built on a handful of key concepts...

Apps

Forms defined in Formscript may be rendered and executed by software. In this document, such software is referred to an "app".

  • Apps can be implemented in any frontend-framework, language or library.
  • Formscript does not impose any aesthetic or UI constraints onto apps that implement it.
  • Formscript content can be embedded inside apps with GUI, CLI and even Voice-User interfaces.
  • Perhaps as a fallback, Formscript content can even be rendered as a hard-copy paper form.
  • Several utilities to help develop apps that use Formscript (written in Javascript) are published on here on npmjs.com (the accompanying source code and related issues can be found here).

Forms

The purpose of Formscript is to define a user interface, referred to as a "form".

  • Using an app, forms are typically used to collect information from a user: but it's entirely possible to simply convey information using a form definition as well.
  • With Formscript it's possible to configure a form with structure, validation, conditional content, dynamic values and context-sensitive behaviours (e.g. operating differently with an internet connection as opposed to without).
  • Formscript definitions are naturally stored in .json files (typically one-file-per-form).
  • In certain use-cases consider that YAML (itself just a superset of JSON) may offer a compelling alternative to serialising Formscript definitions in .json files.
  • Please note that a JSON Schema is available here, which may be used to validate the basic integrity of Formscript content.
  • For comprehensive Formscript validation, please refer to the formscript-schema package.

Widgets

Forms are constructed from an ordered list of "widgets".

  • To avoid overloading frontend-terms like 'component', Formscript refers to each object in the widgets array as a widget.
  • Consider a widget as an area of a form responsible for a particular task: either collecting a specific piece of information from a user or visualising a certain piece of information.
  • As such, widgets can be interactive (text, number, map etc.) and non-interactive (heading, stickyNote etc.)
  • The order that Widget objects appear within a form definition is important - representing the order users will encounter them.
  • The Formscript specification offers a fixed set of 26 standard widgets. Need another widget-type entirely or an extra configuration options? Pull requests are very welcome!

Ahead of the Reference section, here's a quick summary of the 26 widgets supported in Formscript 0.0.4:

Widget TypeDescription
addressAllows the user to select a particular postal address from a provided list and store a unique reference to that property, such as a UPRN or similar.
apiLookupAllows the user to select a specific value from an API endpoint
checkboxListOffer a related set of checkboxes with accompanying labels for the user to switch on and off.
currencyJust like a number widget, but for specifically collecting a monetary value.
dateAllows the user to provide a specific date - without a time portion.
dateTimeCollects a specific date and time from the user.
endSetMarks the end of a set of related widgets - see the Sets section for more information.
endSubFormMarks the end of a sub-form - see the Sets section for more information.
fileUploadAllows the user to upload a file.
headerDisplays a header for a form (with an optional background image and some text akin to a 'Hero Unit' component).
imageEmbeds a non-interactive image within the form.
mapDisplays a map to the user, and can optionally be configured to collect geo-spatial data (points, lines etc.)
numberLike a text widget, but specifically for collecting numeric content.
questionnaireOffers the user a question with two or more possible responses on an appropriate scale.
radioAllows the user to select a value from a set of related options that are rendered in a Radio Button style.
richtextOffers the user a text editor with functionality to format text.
selectAllows the user to select a value from a set of options, which should be rendered in an HTML Select style.
setMarks the start of a set of related widgets - see the Sets section for more information.
signatureAllow the collection of a handwritten signature
sliderFor capturing a number along a specified range
stickyNoteA panel for putting helpful text or other informative text
subFormAllows the user to enter a number of 'sub forms' (think order-lines or contact details etc.)
switchPresents a on/off style switch to the user.
textA bread-and-butter box for collecting textual information from the user.
textareaCollects simple multi-line text input from the user.
timeAllows the user to provide a specific time (without being tied to a particular date)

Sets

All the widgets that define a form's content are specified in a simple array. This design helps align Formscript with vertical-scrolling interfaces with very little friction. To assist with navigation (especially around larger, more complex forms) it is common for User Interfaces to be split into logical sections.

In Formscript, sets allow widgets to be grouped into related chunks.

  • Each set begins with a set widget and ends with an endSet widget.
  • Nesting of sets is possible and sets are especially powerful when combined with dynamic expressions to conditionally show/hide content.
  • Sets enable apps to offer progress tracking components.
  • Multi-step "wizard" interfaces are also easily achieved via sets.

Expressions

Formscript uses expressions to deliver dynamic content. Expressions are used to:

  • Conditionally show/hide widgets depending on values as they change.
  • Validate form content based on more complex business rules.
  • Affect the contents of enumerated lists.
  • Default dynamic values.
  • Calculate running totals, real-time summaries etc.

Consider an expression to be something that could be evaluated in a Javascript if (...) {} statement.

{
  "widgets": [
    {
      "id": "userWantsToGiveFeedback",
      "type": "switch",
      "attributes": {
        "default": false,
        "heading": "Do you want to leave feedback?"
      }
    },
    {
      "id": "feedback",
      "showWhen": "data.userWantsToGiveFeedback",
      "type": "textarea",
      "attributes": {
        "heading": "Feedback",
        "desc": "Please tell us how you feel!"
      }
    }
  ]
}

In the example above we have two widgets:

  • The first is a simple boolean on/off switch widget (with the id of userWantsToGiveFeedback) which is by default set to false.
  • The second widget is a textarea box (with the id of feedback) for collecting feedback from the user.

The feedback widget should only show if the userWantsToGiveFeedback switch is thrown on (i.e. true).

There are a few new things going on here. Most types of widget (here the switch and textarea types) expect an app to read and write their values to an underlying data object (using their respective id values as keys). It is also expected that any app implementing Formscript should also make this data object available within a safe sandbox while evaluating expressions.

In the previous example we can see the showWhen attribute is being used on the feedback widget. The string value here is an expression, which will control the visibility of the widget (i.e. it should only be shown to the user when the expression evaluates to true).

Expression sandbox

Apps must ensure expressions are evaluated in a safe sandbox context. As such only certain objects may be referred to within an expression:

Sandbox objectDescription
dataThe current form data being stored. Should be kept fresh in real-time using UI binding techniques.
envSome environmental information, e.g. the user's name, if the app has access to an internet connection etc.
env object properties

Apps are expected to provide the following details via an env object when evaluating expressions:

PropertyTypeDescription
usernamestringUsername of the the user currently using the form.
startedOfflinebooleanIndicates if the form was started online, or not.

Reference

Top-Level Properties

The top-level object defining a form comprises of several properties:

PropertyTypeDescriptionRequired?
titlestringA short-as-possible label to associate with the form.false
descstringA quick summary of what the form is hoping to achieve.false
versionstringDenotes the current version of the form definition. This will be assigned by whatever tooling and processes conjure your forms. There is a strong preference that form version strings adhere to Semantic Versioning.false
shasumstringOptionally assigned by tooling, this is a checksum value based on the form-definition. Uses include client-side storage management of form definitions and integrity checking.false
widgetsarrayThe main event, 1 or more widget objects which an app should render to produce a form.true

Widget Properties

Each widget object comprise of some properties:

Attribute NameTypeDescription
idstringA unique string which identifies the widget - often used to bind the value being collected by a widget to an underlying data model. Providing an id value is very often mandatory (depending on the type of widget involved). Regardless, it is good practice to always provide an id because it assists modification (or "patching") of form definitions.
typestringA mandatory value denoting the type of widget being defined (e.g. text, number etc.)
showWhenstringAn expression, that when evaluating to true will cause the widget to appear (so the widget will not be shown if evaluated to be false).
attributesobjectA key/value object for configuring each widget - the content of which is dependent on the widget's type.

Widget Attributes

Formscript 0.0.4 supports a set of 15 common attributes from which widgets can be configured. Not one widget-type requires all these attributes. Attributes are often optional and some widget-types don't need an attributes object at all.

Attribute NameTypeDescription
defaultanyA value to default a widget to if not supplied by other mechanisms.
defaultBooleanbooleanA boolean value to default a widget to if not supplied by other mechanisms.
defaultNumbernumberA numeric value to default a widget to if not supplied by other mechanisms.
defaultStringstringA string value to default a widget to if not supplied by other mechanisms.
descstringSome additional advice (above and beyond the string supplied in label) to help define what data is required from the user.
enabledbooleanIndicates if the user can use the widget to alter the underlying value - default to true.
headingstringSome short, strong, punchy text to identify the widget.
helpstringMore detailed guidance/advice (building on top of description content) to help shape what data is collected from the user.
labelstringA short piece of text to help identify what content is required by the user.
mandatorybooleanIndicates if a value needs to be supplied by the user, or if it's optional.
maxCharactersnumberThe maximum length of number of characters a user can specify.
minCharactersnumberThe minimum length of number of characters a will need to provide.
numericValuevalueExplicitly assert that the widget receive and store numeric values (usually of use with title-map enumerations).
placeholderstringSome example text that can be appear ina widget ahead of collecting use input.
titleMaparrayAn array of objects denoting a set of values that the user can select from.

Widget List

The address widget

Allows the user to select a particular postal address from a provided list and store a unique reference to that property, such as a UPRN or similar.

Example

{
  "id": "patientAddress",
  "type": "address",
  "attributes": {
    "heading": "Where does the patient live?",
    "desc": "If it's not possible to ascertain an accurate address from the patient then please select 'Unknown'",
    "mandatory": true,
    "results": {
      "limit": 20,
      "pagination": true
    },
    "params": {
      "enableUnknownOption": true,
      "enableLocationAssist": false
    }
  }
}

Properties

id: Mandatory

type: Mandatory (address)

showWhen: Optional

The apiLookup widget

Allows the user to select a specific value from an API endpoint

Example

{
  "id": "",
  "type": "apiLookup",
  "attributes": {
    "apiName": "fleet",
    "heading": "Fire Appliance",
    "desc": "Please select the Fire Appliance involved with this event",
    "mandatory": true,
    "results": {
      "limit": 20,
      "pagination": true
    },
    "params": {
      "showCurrentOnly": true,
      "showOperationalOnly": true
    }
  }
}

Properties

id: Mandatory

type: Mandatory (apiLookup)

showWhen: Optional

The checkboxList widget

Offer a related set of checkboxes with accompanying labels for the user to switch on and off.

Example

{
  "id": "limbMovement",
  "type": "checkboxList",
  "attributes": {
    "heading": "Which limbs were seen to move?",
    "default": [
      "LEFT_ARM",
      "RIGHT_ARM",
      "LEFT_LEG",
      "RIGHT_LEG"
    ],
    "minLimit": 0,
    "maxLimit": 4,
    "titleMap": [
      {
        "value": "LEFT_ARM",
        "title": "Left arm"
      },
      {
        "value": "RIGHT_ARM",
        "title": "Right arm"
      },
      {
        "value": "LEFT_LEG",
        "title": "Left leg"
      },
      {
        "value": "RIGHT_LEG",
        "title": "Right leg"
      }
    ]
  }
}

Properties

id: Mandatory

type: Mandatory (checkboxList)

showWhen: Optional

The currency widget

Just like a number widget, but for specifically collecting a monetary value.

Example

{
  "id": "price",
  "type": "currency",
  "attributes": {
    "mandatory": true,
    "heading": "Purchase price",
    "desc": "How much did this stock-item cost from the supplier?"
  }
}

Properties

id: Mandatory

type: Mandatory (currency)

showWhen: Optional

The date widget

Allows the user to provide a specific date - without a time portion.

Example

{
  "id": "dateOfBirth",
  "type": "date",
  "attributes": {
    "mandatory": true,
    "heading": "Date of birth",
    "desc": "Date the employee was born",
    "historicByAtLeast": "18 years"
  }
}

Properties

id: Mandatory

type: Mandatory (date)

showWhen: Optional

The dateTime widget

Collects a specific date and time from the user.

Example

{
  "id": "appointment",
  "type": "dateTime",
  "attributes": {
    "mandatory": true,
    "heading": "Appointment",
    "desc": "The date and time this visit is scheduled for",
    "captureHistoric": false,
    "futuristicByAtMost": "3 months"
  }
}

Properties

id: Mandatory

type: Mandatory (dateTime)

showWhen: Optional

The endSet widget

Marks the end of a set of related widgets - see the Sets section for more information.

Example

{
  "widgets": [
    {
      "type": "set",
      "attributes": {
        "heading": "Incident details",
        "desc": "Please provide details of the incident at which casualty care was administered.",
        "showInTOC": true
      }
    },
    {
      "type": "endSet"
    }
  ]
}

Properties

type: Mandatory (endSet)

The endSubForm widget

Marks the end of a sub-form - see the Sets section for more information.

Example

{
  "widgets": [
    {
      "type": "subForm",
      "attributes": {
        "heading": "Explosions",
        "desc": "Please provide details of the explosions which occurred.",
        "minAllowed": 1,
        "maxAllowed": 10,
        "showAtLeastOne": true,
        "singularEntityText": "explosion",
        "pluralEntityText": "explosions"
      }
    },
    {
      "type": "endSubForm"
    }
  ]
}

Properties

type: Mandatory (endSubForm)

The fileUpload widget

Allows the user to upload a file.

Example

{
  "id": "photographicEvidence",
  "type": "fileUpload",
  "attributes": {
    "heading": "Any photographic evidence?",
    "desc": "Upload any digital photographs supporting your observations",
    "enableCaptioning": true,
    "formatRestriction": [
      "jpg",
      "jpeg"
    ],
    "maxFileSize": "15mb",
    "minNumberOfFiles": 0,
    "maxNumberOfFiles": 10
  }
}

Properties

id: Mandatory

type: Mandatory (fileUpload)

showWhen: Optional

The header widget

Displays a header for a form (with an optional background image and some text akin to a 'Hero Unit' component).

Example

{
  "type": "header",
  "attributes": {
    "heading": "Patient Report",
    "desc": "Use this form to provide details of patient care administered at the scene of an incident.",
    "backgroundImage": "wmfs/casualty-care-background.jpg",
    "backgroundImageAltText": "Photograph of activity at a Road Traffic Collision"
  }
}

Properties

type: Mandatory (header)

showWhen: Optional

The image widget

Embeds a non-interactive image within the form.

Example

{
  "id": "numberOfFloors",
  "type": "image",
  "attributes": {
    "image": "wmfs/number-of-floors-diagram.png",
    "altText": "Indicates ground-floor is referred to as '0' and one above it is referred to as '1': but the total number of floors is 2."
  }
}

Properties

id: Mandatory

type: Mandatory (image)

showWhen: Optional

The map widget

Displays a map to the user, and can optionally be configured to collect geo-spatial data (points, lines etc.)

Example

{
  "id": "incidentCoordinates",
  "type": "map",
  "attributes": {
    "heading": "Point of ignition",
    "mandatory": true,
    "desc": "Please indicate the exact position of where the fire started.",
    "enableLocationAssist": true,
    "drawingMode": "singlePoint",
    "drawingConfig": {
      "autoCentre": true,
      "iconImage": "wmfs/flame"
    },
    "relatedLayers": [
      {
        "name": "gaz",
        "heading": "Gazetteer",
        "desc": "Buildings and similar",
        "visibleByDefault": false
      }
    ]
  }
}

Properties

id: Mandatory

type: Mandatory (map)

showWhen: Optional

The number widget

Like a text widget, but specifically for collecting numeric content.

Example

{
  "id": "numShocks",
  "type": "number",
  "attributes": {
    "mandatory": true,
    "default": 2,
    "heading": "How many shocks were delivered?"
  }
}

Properties

id: Mandatory

type: Mandatory (number)

showWhen: Optional

The questionnaire widget

Offers the user a question with two or more possible responses on an appropriate scale.

Example

{
  "id": "painArrival",
  "type": "questionnaire",
  "attributes": {
    "mandatory": true,
    "heading": "Pain-score on arrival",
    "desc": "How did the carer assess the patient's pain when they first met?",
    "default": 1,
    "numericValue": true,
    "titleMap": [
      {
        "value": 0,
        "title": "0",
        "desc": "No pain"
      },
      {
        "value": 1,
        "title": "1",
        "desc": "Slight pain"
      },
      {
        "value": 2,
        "title": "2",
        "desc": "Moderate pain"
      },
      {
        "value": 3,
        "title": "3",
        "desc": "Severe pain"
      }
    ]
  }
}

Properties

id: Mandatory

type: Mandatory (questionnaire)

showWhen: Optional

The radio widget

Allows the user to select a value from a set of related options that are rendered in a Radio Button style.

Example

{
  "id": "gender",
  "type": "radio",
  "attributes": {
    "heading": "Patient gender",
    "mandatory": true,
    "titleMap": [
      {
        "value": "MALE",
        "title": "Male"
      },
      {
        "value": "FEMALE",
        "title": "Female"
      },
      {
        "value": "UNKNOWN",
        "title": "Unknown"
      }
    ]
  }
}

Properties

id: Mandatory

type: Mandatory (radio)

showWhen: Optional

The richtext widget

Offers the user a text editor with functionality to format text.

Example

{
  "id": "clinicalNotes",
  "type": "richtext",
  "attributes": {
    "heading": "Clinical Notes?",
    "mandatory": false,
    "desc": "If you have any clinical notes, please enter them here"
  }
}

Properties

id: Mandatory

type: Mandatory (richtext)

showWhen: Optional

The select widget

Allows the user to select a value from a set of options, which should be rendered in an HTML Select style.

Example

{
  "id": "choking",
  "type": "select",
  "attributes": {
    "heading": "Choking?",
    "desc": "Was the patient choking, if so what treatment was administered?",
    "mandatory": true,
    "default": "NOT_APPLICABLE",
    "titleMap": [
      {
        "value": "NOT_APPLICABLE",
        "title": "No choking - not applicable"
      },
      {
        "value": "COUGH",
        "title": "Encourage cough"
      },
      {
        "value": "BACK_SLAPS",
        "title": "Back slaps"
      },
      {
        "value": "ABDOMINAL_THRUSTS",
        "title": "Adbominal/Chest thrusts"
      },
      {
        "value": "COMPRESSIONS",
        "title": "Chest compressions (CPR)"
      },
      {
        "value": "OTHER",
        "title": "Other"
      }
    ]
  }
}

Properties

id: Mandatory

type: Mandatory (select)

showWhen: Optional

The set widget

Marks the start of a set of related widgets - see the Sets section for more information.

Example

{
  "widgets": [
    {
      "type": "set",
      "attributes": {
        "heading": "Incident details",
        "desc": "Please provide details of the incident at which casualty care was administered.",
        "showInTOC": true
      }
    },
    {
      "type": "endSet"
    }
  ]
}

Properties

id: Mandatory

type: Mandatory (set)

showWhen: Optional

The signature widget

Allow the collection of a handwritten signature

Example

{
  "id": "confirmation",
  "type": "signature",
  "attributes": {
    "heading": "Customer acknowledgement",
    "desc": "Please sign here to confirm receipt of some service",
    "help": "Hand the device over to the customer",
    "mandatory": true
  }
}

Properties

id: Mandatory

type: Mandatory (signature)

showWhen: Optional

The slider widget

For capturing a number along a specified range

Example

{
  "id": "burnArea",
  "type": "slider",
  "attributes": {
    "mandatory": true,
    "heading": "Estimated body surface area burnt (%)",
    "default": 0,
    "minimum": 0,
    "maximum": 100,
    "step": 5
  }
}

Properties

id: Mandatory

type: Mandatory (slider)

showWhen: Optional

The stickyNote widget

A panel for putting helpful text or other informative text

Example

{
  "id": "info",
  "type": "stickyNote",
  "attributes": {
    "style": "informative",
    "heading": "Remember!",
    "desc": "Floor numbering starts with 0 (ground floor)."
  }
}

Properties

id: Mandatory

type: Mandatory (stickyNote)

showWhen: Optional

The subForm widget

Allows the user to enter a number of 'sub forms' (think order-lines or contact details etc.)

Example

{
  "widgets": [
    {
      "type": "subForm",
      "attributes": {
        "heading": "Explosions",
        "desc": "Please provide details of the explosions which occurred.",
        "minAllowed": 1,
        "maxAllowed": 10,
        "showAtLeastOne": true,
        "singularEntityText": "explosion",
        "pluralEntityText": "explosions"
      }
    },
    {
      "type": "endSubForm"
    }
  ]
}

Properties

id: Mandatory

type: Mandatory (subForm)

showWhen: Optional

The switch widget

Presents a on/off style switch to the user.

Example

{
  "id": "burns",
  "type": "switch",
  "attributes": {
    "heading": "Did the patient suffer burns?",
    "default": false
  }
}

Properties

id: Mandatory

type: Mandatory (switch)

showWhen: Optional

The text widget

A bread-and-butter box for collecting textual information from the user.

Example

{
  "id": "handover",
  "type": "text",
  "attributes": {
    "heading": "Who was the patient handed over to?",
    "desc": "Please provide Emergency service and name of person.",
    "placeholder": "Service/name",
    "mandatory": false,
    "minCharacters": 10
  }
}

Properties

id: Mandatory

type: Mandatory (text)

showWhen: Optional

The textarea widget

Collects simple multi-line text input from the user.

Example

{
  "id": "clinicalNotes",
  "type": "richtext",
  "attributes": {
    "heading": "Clinical Notes?",
    "mandatory": false,
    "desc": "If you have any clinical notes, please enter them here"
  }
}

Properties

id: Mandatory

type: Mandatory (textarea)

showWhen: Optional

The time widget

Allows the user to provide a specific time (without being tied to a particular date)

Example

{
  "id": "openingTime",
  "type": "time",
  "attributes": {
    "mandatory": true,
    "heading": "Opening Time",
    "desc": "What time does the business usually open?"
  }
}

Properties

id: Mandatory

type: Mandatory (time)

showWhen: Optional

FOSSA Status

0.0.3

6 years ago