-
Notifications
You must be signed in to change notification settings - Fork 45
Parameter forms
Each Step has a <form>
with a bunch of Param
eters. The parameters can have different types (see Parameter types); each field type implies a React component.
Here are those React components' conventions:
All components will be rendered with:
{
name: PropTypes.string.isRequired,
fieldId: PropTypes.string.isRequired,
label: PropTypes.string.isRequired, // maybe ''
isReadOnly: PropTypes.bool.isRequired,
isZenMode: PropTypes.bool.isRequired,
upstreamValue: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired, // onChange(newValue) => undefined
value: PropTypes.string.isRequired,
inputColumns: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string.isRequired,
type: PropTypes.oneOf([ 'text', 'number', 'datetime' ]).isRequired
}).isRequired),
tabs: PropTypes.arrayOf(PropTypes.shape({
slug: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
outputColumns: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string.isRequired,
type: PropTypes.oneOf([ 'text', 'number', 'datetime' ]).isRequired
}).isRequired)
}).isRequired).isRequired
}
Each component must have isReadOnly: PropTypes.boolean.isRequired
. When set, the user cannot edit.
Each component manipulates a value: PropTypes.xxx.isRequired
. The component shouldn't keep value
in its internal state: rather, it should call onChange(newValue)
with every keypress. The form itself maintains parameters.
When the user runs onChange()
, that manipulates the form's state. When the user submits the form, the changes to "upstream" to the server. When other users (different people with their browser windows open on the same Workflow) write values, those "upstream" changes arrive in the user's form.
Basically, upstreamValue
means: "the value on the server."
During editing, the user may want to see which fields have been modified. Components should check whether value == upstreamValue
and set className="edited"
to indicate to the user that the value is different.
The component may also offer a "reset" feature that calls onChange(upstreamValue)
. (When value === upstreamValue
, value
will automatically update whenever a new upstreamValue
comes from the server.)
To help with testing and communication, every HTML5 form field should have a name
. This will bet set to parameter's id_name
from the module definition JSON. The component needs name: PropTypes.string.isRequired
. If the compoment has multiple HTML5 form fields, their names should use square brackets. For instance:
<input type='number' name={`${name}[subfield]`} .../>
<input type='number' name={`${name}[otherfield]`} .../>
This text should appear always, whether value
is empty or not.
Prefer an HTML5 <label>
. If the user clicks the label, the user will expect a form field to gain focus. To do that, you'll probably want htmlFor=[SOME ID]
, and for that we supply fieldId
, which is guaranteed to be unique across the entire HTML page.
There's a <MaybeLabel>
helper you can use. It does everything.
What does the user see when a components value
is "empty"? That depends on the component, of course; but in general, it should be:
- The "effective" or "default" value, grayed out. For instance, a field for "output column name" should predict what the output column name will be.
- A "prompt", grayed out. For instance:
Select a column
Should the component need to know input-dataframe columns, it can check inputColumns: PropTypes.arrayOf(PropTypes.shape({ name: PropTypes.string.isRequired, type: PropTypes.oneOf([ 'number', 'text', 'datetime' ]).isRequired }))
.
During rendering, inputColumns
is null
.
Should the component need to know about this workflow's tabs, it can check tabs
. It's an ordered list of tabs.
During rendering of a tab, tabs[*].outputColumns
is null
; otherwise it's a list of output columns.
Usually false
; some modules will allow it to be true
and it may decide upon a different layout if that's the case.