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

UI Form - Dynamic options & label #1016

Merged
merged 4 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
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
143 changes: 141 additions & 2 deletions cypress/fixtures/flows/dashboard-forms.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{
"id": "node-red-tab-forms",
"type": "tab",
"label": "UI Buttons",
"label": "UI Forms",
"disabled": false,
"info": "",
"env": []
Expand Down Expand Up @@ -47,7 +47,7 @@
{
"id": "test-helper",
"type": "function",
"z": "node-red-tab-button-groups",
"z": "node-red-tab-forms",
"name": "Store Latest Msg",
"func": "global.set('msg', msg)\nreturn msg;",
"outputs": 1,
Expand Down Expand Up @@ -126,5 +126,144 @@
"wires": [
[]
]
},
{
"id": "dashboard-ui-form-dynamic",
"type": "ui-form",
"z": "node-red-tab-forms",
"name": "Dynamic Form",
"group": "bab1dddbce88e07a",
"label": "Dynamic Form",
"order": 2,
"width": 0,
"height": 0,
"options": [
{
"label": "Name",
"key": "name0",
"type": "text",
"required": true,
"rows": null
}
],
"formValue": {
"name": ""
},
"payload": "",
"submit": "submit",
"cancel": "clear",
"resetOnSubmit": false,
"topic": "new-topic",
"topicType": "str",
"splitLayout": "",
"className": "",
"x": 520,
"y": 160,
"wires": [
[]
]
},
{
"id": "dashboard-ui-button-set-dynamic-options",
"type": "ui-button",
"z": "node-red-tab-forms",
"group": "bab1dddbce88e07a",
"name": "",
"label": "Override Form Options",
"order": 1,
"width": 0,
"height": 0,
"emulateClick": false,
"tooltip": "",
"color": "",
"bgcolor": "",
"className": "",
"icon": "",
"iconPosition": "left",
"payload": "",
"payloadType": "str",
"topic": "topic",
"topicType": "msg",
"x": 160,
"y": 160,
"wires": [
[
"6d940deccaa355f6"
]
]
},
{
"id": "dashboard-ui-button-set-defaults",
"type": "ui-button",
"z": "node-red-tab-forms",
"group": "bab1dddbce88e07a",
"name": "",
"label": "Set Defaults",
"order": 1,
"width": 0,
"height": 0,
"emulateClick": false,
"tooltip": "",
"color": "",
"bgcolor": "",
"className": "",
"icon": "",
"iconPosition": "left",
"payload": "{\"name0\": \"Overridden Default Name\"}",
"payloadType": "json",
"topic": "topic",
"topicType": "msg",
"x": 160,
"y": 200,
"wires": [
[
"dashboard-ui-form-dynamic"
]
]
},
{
"id": "6d940deccaa355f6",
"type": "change",
"z": "node-red-tab-forms",
"name": "Set Options",
"rules": [
{
"t": "delete",
"p": "payload",
"pt": "msg"
},
{
"t": "set",
"p": "ui_update.options",
"pt": "msg",
"to": "[{\"type\":\"text\",\"label\":\"Name\",\"key\":\"name\",\"required\":true},{\"type\":\"multiline\",\"label\":\"Name\",\"key\":\"multiline\",\"required\":true,\"rows\":4},{\"type\":\"password\",\"label\":\"Password\",\"key\":\"password\",\"required\":true},{\"type\":\"email\",\"label\":\"E-Mail Address\",\"key\":\"email\",\"required\":true},{\"type\":\"number\",\"label\":\"Age\",\"key\":\"age\",\"required\":true},{\"type\":\"checkbox\",\"label\":\"Subscribe to Newsletter\",\"key\":\"newsletter\"},{\"type\":\"switch\",\"label\":\"Enable Notifications\",\"key\":\"notifications\"},{\"type\":\"date\",\"label\":\"Date of Birth\",\"key\":\"dob\",\"required\":true},{\"type\":\"time\",\"label\":\"Time of Birth\",\"key\":\"tob\",\"required\":true}]",
"tot": "json"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 350,
"y": 160,
"wires": [
[
"dashboard-ui-form-dynamic"
]
]
},
{
"id": "bab1dddbce88e07a",
"type": "ui-group",
"name": "Dynamic Property Tests",
"page": "dashboard-ui-page-1",
"width": "6",
"height": "1",
"order": 1,
"showTitle": true,
"className": "",
"visible": "true",
"disabled": "false"
}
]
38 changes: 38 additions & 0 deletions cypress/tests/widgets/form.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,41 @@ describe('Node-RED Dashboard 2.0 - Forms', () => {
cy.get('[data-action="form-submit"]').should('not.be.disabled')
})
})

describe('Node-RED Dashboard 2.0 - Forms', () => {
beforeEach(() => {
cy.deployFixture('dashboard-forms')
cy.visit('/dashboard/page1')
})

it('permits users to set default values via msg.payload', () => {
// check that the form is empty
cy.get('#nrdb-ui-widget-dashboard-ui-form-dynamic').find('[data-form="form-row-name0"]').find('input[type="text"]').should('have.value', '')
cy.clickAndWait(cy.get('button').contains('Set Defaults'))
cy.get('#nrdb-ui-widget-dashboard-ui-form-dynamic').find('[data-form="form-row-name0"]').find('input[type="text"]').should('have.value', 'Overridden Default Name')
})

it('can have their content defined by msg.ui_update.options', () => {
cy.get('#nrdb-ui-widget-dashboard-ui-form-dynamic').find('[data-form="form-row-name"]').should('not.exist')
cy.get('#nrdb-ui-widget-dashboard-ui-form-dynamic').find('[data-form="form-row-multiline"]').should('not.exist')
cy.get('#nrdb-ui-widget-dashboard-ui-form-dynamic').find('[data-form="form-row-password"]').should('not.exist')
cy.get('#nrdb-ui-widget-dashboard-ui-form-dynamic').find('[data-form="form-row-email"]').should('not.exist')
cy.get('#nrdb-ui-widget-dashboard-ui-form-dynamic').find('[data-form="form-row-age"]').should('not.exist')
cy.get('#nrdb-ui-widget-dashboard-ui-form-dynamic').find('[data-form="form-row-newsletter"]').should('not.exist')
cy.get('#nrdb-ui-widget-dashboard-ui-form-dynamic').find('[data-form="form-row-notifications"]').should('not.exist')
cy.get('#nrdb-ui-widget-dashboard-ui-form-dynamic').find('[data-form="form-row-dob"]').should('not.exist')
cy.get('#nrdb-ui-widget-dashboard-ui-form-dynamic').find('[data-form="form-row-tob"]').should('not.exist')

cy.clickAndWait(cy.get('button').contains('Override Form Options'))

cy.get('#nrdb-ui-widget-dashboard-ui-form-dynamic').find('[data-form="form-row-name"]').find('input[type="text"]').should('exist')
cy.get('#nrdb-ui-widget-dashboard-ui-form-dynamic').find('[data-form="form-row-multiline"]').find('textarea').should('exist')
cy.get('#nrdb-ui-widget-dashboard-ui-form-dynamic').find('[data-form="form-row-password"]').find('input[type="password"]').should('exist')
cy.get('#nrdb-ui-widget-dashboard-ui-form-dynamic').find('[data-form="form-row-email"]').find('input[type="email"]').should('exist')
cy.get('#nrdb-ui-widget-dashboard-ui-form-dynamic').find('[data-form="form-row-age"]').find('input[type="number"]').should('exist')
cy.get('#nrdb-ui-widget-dashboard-ui-form-dynamic').find('[data-form="form-row-newsletter"]').find('input[type="checkbox"]').should('exist')
cy.get('#nrdb-ui-widget-dashboard-ui-form-dynamic').find('[data-form="form-row-notifications"]').find('input[type="checkbox"]').should('exist')
cy.get('#nrdb-ui-widget-dashboard-ui-form-dynamic').find('[data-form="form-row-dob"]').find('input[type="date"]').should('exist')
cy.get('#nrdb-ui-widget-dashboard-ui-form-dynamic').find('[data-form="form-row-tob"]').find('input[type="time"]').should('exist')
})
})
143 changes: 134 additions & 9 deletions docs/nodes/widgets/ui-form.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,29 @@ description: Gather user input efficiently with ui-form in Node-RED Dashboard 2.
props:
Group: Defines which group of the UI Dashboard this widget will render in.
Size: Controls the width of the button with respect to the parent group. Maximum value is the width of the group.
Label: A label shown before the form rows.
Form Elements: >
A list of the rows presented in the form. Each row has the following properties:
<ul>
<li>Label: A label shown in the form row.</li>
<li>Name: The name of the form element, which will be used as the key in the <code>msg.payload</code> object.</li>
<li>Type: The type of input to display. Options - <code>text | multiline | password | email | number | checkbox | switch | date | time</code></li>
<li>Required: Whether the form element is required to be filled in before the form can be submitted.</li>
Label:
description: A label shown before the form rows.
dynamic: true
Options:
description: >
A list of the rows presented in the form. Each row has the following properties:
<ul>
<li>Label: A label shown in the form row.</li>
<li>Name: The name of the form element, which will be used as the key in the <code>msg.payload</code> object.</li>
<li>Type: The type of input to display. Options - <code>text | multiline | password | email | number | checkbox | switch | date | time</code></li>
<li>Required: Whether the form element is required to be filled in before the form can be submitted.</li>
dynamic: true
Buttons: The text shown on each of the form's buttons. If "cancel" text is left empty, then no cancel button will be shown.
Two Columns: Will render the form as a two-column layout.
Reset on Submit: If checked, the form will be reset to an empty state after the form is submitted.
Topic: Defines how to compute the topic, included in the `msg` object, when the form is submitted.
dynamic:
Label:
payload: msg.ui_update.label
structure: ["String"]
Options:
payload: msg.ui_update.options
structure: ["Array<Object>"]
Class:
payload: msg.class
structure: ["String"]
Expand All @@ -36,7 +46,122 @@ Adds a form to user interface which helps to collect multiple value from the use

<DynamicPropsTable/>

### Populating Form Data

If you want to set defaults, or pre-fill values in your form, you can do so by passing a `msg.payload` value. This value should be an object, where each key represents the `key` of a form element, and the value represents the default value for that element.

Foe example, if you want to pre-fill a form with a "text" field, with a name, "first_name", you can pass the following `msg`:

```js
msg.payload = {
"first_name": "John"
}
```

### Defining Form Elements (Options)

If you want to override the configuration for your `ui-form`, and provide details of your elements after your Node-RED flow has been deployed, you can do so by passing a `msg.ui_update.options` value. This value should be an array of objects, where each object represents a form element. Each object should have the following properties:

#### Element: Text

```json
{
"type": "text",
"label": "Name",
"key": "name",
"required": true
}
```

#### Element: Multiline

```json
{
"type": "multiline",
"label": "Name",
"key": "name",
"required": true,
"rows": 4
}
```

#### Element: Password

```json
{
"type": "password",
"label": "Password",
"key": "password",
"required": true
}
```

#### Element: Email

```json
{
"type": "email",
"label": "E-Mail Address",
"key": "email",
"required": true
}
```

#### Element: Number

```json
{
"type": "number",
"label": "Age",
"key": "age",
"required": true
}
```

#### Element: Checkbox

```json
{
"type": "checkbox",
"label": "Subscribe to Newsletter",
"key": "newsletter"
}
```

#### Element: Switch

```json
{
"type": "switch",
"label": "Enable Notifications",
"key": "notifications"
}
```

#### Element: Date

```json
{
"type": "date",
"label": "Date of Birth",
"key": "dob",
"required": true
}
```

#### Element: Time

```json
{
"type": "time",
"label": "Time of Birth",
"key": "tob",
"required": true
}
```


## Example

![Example of a Form](/images/node-examples/ui-form.png "Example of tewo-column Form"){data-zoomable}
![Example of a Form](/images/node-examples/ui-form.png "Example of two-column Form"){data-zoomable}
*Example of a rendered form in a Dashboard.*
2 changes: 1 addition & 1 deletion nodes/widgets/locales/en-US/ui_dropdown.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ <h3>Selecting Options via <code>msg.</code></h3>
To clear any selection for a dropdown, pass an empty array <code>[]</code> as <code>msg.payload</code>.
</p>
<h3>Dynamic Properties (Inputs)</h3>
<p>Any of the following can be appended to a <code>msg.</code> in order to override or set properties on this node at runtime.</p>
<p>Any of the following can be appended to a <code>msg.ui_update</code> in order to override or set properties on this node at runtime.</p>
<dl class="message-properties">
<dt class="optional">label <span class="property-type">string</span></dt>
<dd>The label displayed to explain the purpose of the dropdown.</dd>
Expand Down
Loading
Loading