Skip to main content

Forms

Forms are automatically generated using react-jsonschema-form from JSONSchema, which in turn is generated from marshmallow schemas on the server side in python.

An example schema may be as follows:

actor.py
class MySchema(Schema):
energy = fields.Float(title="Energy", unit="eV", required=True)
dwell = fields.Float(title="Dwell", unit="s", required=True)
counters = fields.List(
fields.Str(enum=["temperature", "pressure"]), uniqueItems=True, title="Counters"
)

Which will generate the following form:

Field Order

Fields can be specifically ordered by adding the Meta.uiorder property to the Schema, for example:

actor.py
class MySchema(Schema):
class Meta:
uiorder: ["dwell", "energy", "counters"]

Will order the fields as follows:

caution

When ordering, all fields must be specified, even those which are hidden.

Customising Fields

react-jsonschema-form will by default render a field type with a sensible input widget. However sometimes it is useful to be able to override this with a custom field type. This can be done by specifying the Meta.uischema property on the Schema.

For example:

actor.py
class MySchema(Schema):
class Meta:
uischema = {
"sampleid": {"classNames": "hidden-row", "ui:widget": "hidden"},
"plot": {"ui:field": "plot0d"},
}

Hidden field

A hidden field is specified with:

actor.py
class MySchema(Schema):
class Meta:
uischema = {
"sampleid": {"classNames": "hidden-row", "ui:widget": "hidden"},
}

0d Plot

actor.py
class MySchema(Schema):
class Meta:
uischema = {
"plot": {"ui:field": "plot0d"},
}

With the schema calculating the following data structure for the plot:

actor.py
class MySchema(Schema):
def calculated(self, data):
plot = {
"x": [...],
"series": [
{"name": "Energy", "data": [...]},
{"name": "Delta", "data": [...], "yaxis": "y2"},
],
"nodata": "Awaiting Data...",
"lines": [
{
"x0": ...,
"x1": ...,
"y0": ...,
"y1": ...,
}
]
}

return { "plot": plot }

Where:

  • x is a list of the x data
  • series is a list of the series to plot with names and optionally a second y axis
  • nodata is a label before the field is populated
  • lines is a series of lines to annotate onto the plot

Optional / Typeahead

A typeahead style input can be used to select fields from a nested schema, for example:

actor.py
class BeamlineParamsSchema(Schema):
detector_distance = fields.Float(
title="Detector Distance",
validate=validate.Range(min=20, max=400),
unit="mm",
description="Move the detector to specified distance",
)

fast_shutter = fields.Bool(
title="Fast Shutter", description="Open fast shutter before and close after"
)

class MySchema(Schema):
beamlineparams = fields.Nested(BeamlineParamsSchema, title="Beamline Parameters")

class Meta:
uischema = {
"beamlineparams": {"ui:field": "optionalParams"},
}

Will generate:

Array Table

A table with add / remove buttons can be generated for a field that is an array of Nested schemas, for example:

actor.py
class MotorSchema(Schema):
name = fields.Str(enum=["x", "y", "z"])
value = fields.Int()

class MySchema(Schema):
motors = fields.List(fields.Nested(MotorSchema))

class Meta:
uischema = {
"motors": {"ui:field": "arrayTable"},
}

Will generate:

Field Grouping

To optimise space usage and provide UI hints to the user fields can be grouped together:

actor.py
class MySchema(Schema):
class Meta:
uigroups = [
{
"Params": ["energy", "dwell"]
},
"counters"
]

Will generate:

The label:input proportion can be modified using the ui:colwidth property, this will scale automatically with the label length, but can also be set for a group of fields. This number relates to the number of columns to take of 12 in the standard bootstrap grid system. For example ui:colwidth = 4 would take 1/3 of the space for the label.

actor.py
class MySchema(Schema):
class Meta:
uigroups = [
{
"Params": ["energy", "dwell"],
"ui:colwidth": 5,
},
"counters"
]

The size of each field group can also be explicitly specified. By default the number of fields per row is 2. This can be changed by specifying ui:minwidth. Again this relates to the default bootstrap grid. Where two fields = 6 / 12 columns. Specifying ui:minwidth = 4 would give 3 fields per row.

actor.py
class MySchema(Schema):
class Meta:
uigroups = [
{
"Params": ["energy", "dwell"],
"ui:minwidth": 4,
},
"counters"
]