Skip to content

akvo/akvo-react-form

Repository files navigation

Akvo React Form

Simple react component for building webforms. View Demo

Build Status Repo Size GitHub release NPM Npm package total downloads JavaScript Style Guide GitHub license

Feature set

Feature Description
Initial values
Question Group Description
Translations
Multiple Question Dependency
Rule based response validation
Save Datapoint
Computed field value
Clear response
Custom style
Tooltip
Extra component on Question
HTML Support on Question
Field Suffix / Prefix
Print
Download response to tabular format

Install

Using NPM

npm install --save akvo-react-form

Using Yarn

yarn add akvo-react-form

Supported Field Type

Type Description
input Input
number InputNumber
cascade Cascade Select
text TextArea
date Date
option Option
multiple_option Multiple Select
tree Tree Select
table Table (Multiple Question)
autofield Autofieled
image Image
entity Entity cascade select

Example Usage

import React from 'react';
import 'akvo-react-form/dist/index.css'; /* REQUIRED */
import { Webform } from 'akvo-react-form';
import * as forms from './example.json';

const App = () => {
  const onChange = ({ current, values, progress }) => {
    console.log(progress);
  };
  const onFinish = (values, refreshForm) => {
    console.log(values);
  };
  return (
    <div className="full-width">
      <Webform
        forms={forms.default}
        onChange={onChange}
        onFinish={onFinish}
      />
    </div>
  );
};

export default App;

Refresh Form

If using the autosave parameter, a form refresh is required to remove traces of the previous fields. onFinish includes a function to do this, you also need to change the autosave parameter if the submission is successful. If not, the form refresh will clean up the current datapoint.

Example:

const onFinish = (values, refreshForm) => {
console.info(values);
const newId = generateRandomId();
setDataPointName(newId);
refreshForm();
};

API

Webform

Props Description Type Default
sidebar Option to show / hide sidebar Boolean true
sticky Sticky header and sidebar (Not support for IE9) Boolean false
onFinish Trigger after submitting the form and verifying data successfully function(values) -
onChange Trigger after field value changed function({current,values,progress}) -
onCompleteFailed Trigger when submit is clicked with blank required question function(values, errorFields) -
submitButtonSetting Submit Button Setting Object{loading: Boolean, disabled: Boolean} | undefined {}
extraButton Extra Button Next to Submit Button ReactComponent | undefined -
initialValue Set value by Form initialization Array[Initial Value] | undefined Array[]
printConfig Support survey print functionality Object{showButton: Boolean, filename: String, hideInputType: Array["field type"], header: ReactComponent} | undefined -
downloadSubmissionConfig Support download submission to Excel Object{visible: Boolean, filename: String, horizontal: Boolean} | undefined -
leftDrawerConfig Show left drawer with custom component Object{visible: Boolean, title: String, Content: ReactComponent} | undefined -
autoSave Enable auto save to IndexedDB autoSaveObject | undefined -
fieldIcons Show icon for input and number question type Boolean true
formRef Set react useRef for Form from host React useRef null
languagesDropdownSetting Languages Dropdown Setting Object{showLanguageDropdown: Boolean , languageDropdownValue: ISO 639-1 codes} | undefined {}
UIText UI localization custom param Object{[ISO 639-1 codes]: {...translations}} | undefined {}
showSpinner Show a loading spinner when seeding data in progress Boolean false

Properties

Translations (optional)

Props Description Type
Unique{any} Object to be translated Object{any}
language Language Enum[ISO 693-1]

Form (Root)

Props Description Type
name Form Name / Title String
question_group List of Question Group Array[Question Group]
Unique{any} Cascade definition, can be any properties Array[Cascade]
languages List of available languages Array[enum[ISO 639-1]] | undefined
defaultLanguage Default active language Enum[ISO 639-1]] | undefined
translations List of translations Array[Translations] | undefined

Question Group

Props Description Type
name Question Group Name / Title String
order Question Group Order Integer | undefined
description Question Group Description String | undefined
question List of Question Array[Question]
translations List of translations Array[Translations] | undefined

Cascade (any)

Cascading select questions are sets of questions whose options depend on the response to a previous question. Cascade object should be pre-defined on the question definition root object itself.

Props Description Type
value Cascade Value Unique (Integer | String)
label Cascade Label String
children Children of current object Array[Cascade] | undefined
translations List of translations Array[Translations] | undefined

Example:

{
  "name": "Community Culinary Survey 2021",
  "translations": [
    {
      "name": "Komunitas Kuliner Survey 2021",
      "language": "id"
    }
  ],
  "languages": ["en", "id"],
  "question_group": [
    {
      "name": "Registration",
      "order": 1,
      "translations": [
        {
          "name": "Registrasi",
          "language": "id"
        }
      ],
      "question": [
        {
          "id": 1,
          "name": "Location",
          "order": 1,
          "type": "cascade",
          "option": "administration",
          "required": true,
          "translations": [
            {
              "name": "Lokasi",
              "language": "id"
            }
          ]
        }
      ]
    }
  ],
  "cascade": {
    "administration": [
      {
        "value": 1,
        "label": "Jawa Barat",
        "children": [
          {
            "value": 1,
            "label": "Garut"
          }
        ]
      }
    ]
  }
}

Using API for Cascade

Cascading select also support for a chain API call for the cascade dropdown list.

Props Description Type
endpoint Cascade API String
initial Initial Parameter Integer | String | undefined
list Object name of array res.data?.[list] | res.data | String | undefined

Example:

  "name": "Community Culinary Survey 2021",
  "question_group": [{
      "name": "Registration",
      "order": 1,
      "question": [{
          "id": 1,
          "name": "Location",
          "order": 1,
          "type": "cascade",
          "api": {
            "endpoint": "https://tech-consultancy.akvo.org/akvo-flow-web-api/cascade/seap/cascade-296940912-v1.sqlite/",
            "initial": 0,
            "list": false
          },
          "required": true
       }]
  }]
Props Description Type
id Cascade Value Unique (Integer | String)
name Cascade Label String

API Example : https://tech-consultancy.akvo.org/akvo-flow-web-api/cascade/seap/cascade-296940912-v1.sqlite/0

[
  {
    "code": "ACEH",
    "id": 47,
    "name": "ACEH",
    "parent": 0
  },
  {
    "code": "BALI",
    "id": 128,
    "name": "BALI",
    "parent": 0
  }
]

Entity

Entity cascade selection is a dropdown option that requires an API and depends on the selected parent question, by setting the extra field and API format as follows.

Extra Entity
Props Description Type
type Set to "entity" (required) String
name Entity name String
parentId Parent entity question id Number
Entity API format
Props Description Type
id Entity Value Unique (Integer | String)
name Entity Label String
[
  {
    "id": 91,
    "name": "School - Bandung 1",
  },
  {
    "id": 92,
    "name": "School - Bandung 2",
  }
]

Example:

{
  "id": 67,
  "label": "School cascade",
  "name": "school_cascade",
  "type": "cascade",
  "required": false,
  "order": 7,
  "api": {
    "endpoint": "https://akvo.github.io/akvo-react-form/api/entities/1/"
  },
  "extra": {
    "type": "entity",
    "name": "School",
    "parentId": 5 // question id: 5 (eg: administration type of question)
  }
},

Question

Props Description Type
id Question ID Unique (Integer | String)
order Question Order Integer | undefined
tooltip Question Tooltip String | undefined
type Question Type number | input | text | option | multiple_option | cascade | tree | autofilled | table | image
option List of Option (for option type of question ) Array[Option] | String (cascade object name, only for 'cascade' type) | undefined
columns Columns of table (for table type question) question Array[Columns] | undefined
dependency List of Question Dependency Array[Dependency] | undefined
rule Question rule to be validated (Only for 'number' type of question) {min: Integer, max: Integer}
meta Question set to be used as data point name Boolean | undefined
required Set field as required Boolean | undefined
requiredSign Set custom required field symbol/mark before question label. requiredSign content will show if required param set to true ReactComponent | String | undefined
partialRequired Set a custom required rule for type cascade. Set true to fill without having to select all the cascade level options when the required param is true Boolean | undefined
translations List of translations Array[Translations] | undefined
extra Extra Component Array[ExtraComponent] | Object[ExtraEntity]
addonBefore Addon before Field (only support for number and input type of question) ReactComponent | String | undefined
addonAfter Addon before Field (only support for number and input type of question) ReactComponent | String | undefined
allowOther Allow other field (support for option and multiple_option type of question) Boolean | undefined
allowOtherText Text Replacement for allow other field (support for option and multiple_option type of question) String | undefined
checkStrategy The way show selected item in box when question type is tree. Default: show checked treeNodes (just show parent treeNode), "children": show only children node parent | children | undefined
expandAll Whether to expand all treeNodes by default. Default: false Boolean | undefined
fn Function for autofilled type of question Autofield Function | undefined
dataApiUrl Api data that returns pair of object and value for hint String | undefined
limit Set limit / maximum file size in Megabyte (MB) for image type of question Integer | undefined
disabled Define to disabled the question field Boolean | undefined
pre Define the default value only for option and multiple_option type questions based on the source question and its answer Object | undefined
displayOnly Define the question for display only and do not include it in the payload submission Boolean | undefined

Autofield Function

Autofield data use Javascript function in 1 line

Extra Component

Props Description Type
content Content of the Extra Component ReactComponent | String
placement Placement for the Extra Component before | after
translations List of translations Array[Translations] | undefined

Rule

Rule should be defined as object, currently we only support min max value for number type of question.

Props Type
min Integer | undefined
max Integer | undefined
allowDecimal Boolean | undefined

Example:

{
  "id": 1,
  "name": "Weight",
  "order": 1,
  "type": "number"
  "required": true,
  "tooltip": {"text": "Information Text"},
  "rule": {"min": 5,"max": 10},
  "addonAfter": "Kilograms",
  "meta": true,
  "translations": [{
      "name": "Berat Badan",
      "language": "id"
      }
   ],
   "extra": [{
       "placement": "before",
       "content": "Extra Component before the question",
       "translations": [{
           "content": "Komponen Tambahan sebelum pertanyaan ini",
           "language": "id"
        }]
    }]
}

Dependency (Skip Logic)

If question has dependency, question will be hidden by default. The question will only shows when dependency question values matches with the dependency rules.

Props Description Type
id Question ID Integer | String
options List of dependency options to be validated, for 'option' type of the dependency question Array[String] | undefined
min Minimum dependency value to be validate, for 'number' type of the dependency question Array[String] | undefined
max Maximum dependency value to be validate, for 'number' type of the dependency question Array[String] | undefined
equal Dependent answer is equal to Integer | String | undefined
notEqual Dependent answer is not blank and not equal to Integer | String | undefined

Example:

{
  "id": 11,
  "name": "Where do you usually order Rendang from ?",
  "dependency": [
    {
      "id": 9,
      "options": ["Yes"]
    },
    {
      "id": 10,
      "min": 8
    }
  ],
  "order": 5,
  "type": "option",
  "option": [
    {
      "name": "Pagi Sore",
      "order": 1
    },
    {
      "name": "Any Rendang Restaurant",
      "order": 2,
      "translations": [
        {
          "name": "Restoran Rendang Manapun",
          "language": "id"
        }
      ]
    }
  ],
  "required": true,
  "translations": [
    {
      "name": "Dimana anda biasanya membeli Rendang?",
      "language": "id"
    }
  ]
}

Option

Option is valid only for option type of question

Props Description Type
name Option Name / Label String
order Question Group Order Integer | undefined
translations List of translations Array[Translations] | undefined
color Color of the option String | undefined

Columns

Columns is valid only for table type of question

Props Description Type
name Column / Question object key String
type Column / Question Type number | input | text | option
label Column / Question Label String
option Option value Array[Option] | undefined

Initial Value (optional)

Props Description Type
question Question ID Unique (Integer | String)
value Value of the Question String | Integer | Object{lat,lng} | Array[Integer | String] | Date Format
repeatIndex Repeat Index in Repeated Question Group. Default: 0 Integer | undefined

Example: Initial Value Example

Autofieled Object

Props Description Type
fnString String of function String
multiline Wether function is multiline or not Bool | undefined
fnColor Color for the answer field Object

Example for fnString:

function () { return #1 / #2 }

OR

() => { return #1.includes("Test") ? #2 / #3 : 0 }

Example for fnColor:

{
  "Answer A": "#CCFFC4"
  "Answer B": "#FECDCD"
}

Prefix #N is use to indicate the value of question id N. Note that we don't use javascript eval to overcome the security issue, the function will be sanitized before it's executed.

Auto Save Object

Props Description Type
formId Required Integer
name Name for datapoint String | undefined
buttonText Custom text for save button String | undefined

Auto save object require formId when it's enabled. This will filter list of saved data for particular formId. To show the list of saved datapoint we can use dataStore.

Example:

import React, { useState, useEffect } from 'react'
import { dataStore } from 'akvo-react-form'

const DataList = () => {
  const [datapoint, setDatapoint] = useState([])

  useEffect(() => {
    const listData = dataStore.list(formId)
    listData.then((x) => {
      setDatapoint(x)
    })
  }, [])

  return (
    <table>{dataPoints.map((x, xi) => (
      <tr key={xi}>
        <td>
          {xi + 1}. {x.name}
        </td>
        <td>
            <button onClick={x.load}>
              Load
            </button>
            <button onClick={x.remove}>
              Delete
            </button>
          </Space>
        </td>
      </tr>
    ))}
  </table>)
}

Pre-filled question

Props Description Type
source_question Required String
source_answer Name for datapoint String
default_value Custom text for save button Array

Here the prefilled question is represented as an object

...
"pre": {
  "<source_question>": {
    "<source_answer>": ["<default_value>"]
  }
}
...

Example Form Structure

Please check the Form Definition Example which contains all the current features of akvo-react-form.

License

AGPL-3.0 © akvo