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

Dynamic prop support for UI Text Input #1214

Merged
merged 17 commits into from
Aug 29, 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
516 changes: 516 additions & 0 deletions cypress/fixtures/flows/dashboard-text-input.json

Large diffs are not rendered by default.

30 changes: 0 additions & 30 deletions cypress/tests/widgets/number.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,4 @@ describe('Node/-RED Dashboard 2.0 - Number Input Widget', () => {
cy.get('#nrdb-ui-widget-2f48e919876213cc .nrdb-ui-number-field').trigger('mouseover')
cy.get('.v-tooltip').should('contain', 'Tooltip Text')
})

// Test case: Emits onClear event correctly
it('reset field on onClear event correctly and outputs the correct payload', () => {
cy.get('#nrdb-ui-widget-2f48e919876213cc input[type="text"]').clear()
cy.get('#nrdb-ui-widget-2f48e919876213cc input[type="text"]').type('2')
cy.get('#nrdb-ui-widget-2f48e919876213cc input[type="text"]').trigger('keydown', { key: 'Enter' })
cy.clickAndWait(cy.get('#nrdb-ui-widget-2f48e919876213cc .nrdb-ui-number-field .v-field__clearable'))
cy.get('#nrdb-ui-widget-2f48e919876213cc input[type="text"]').should('have.value', '')
cy.checkOutput('msg.payload', null)
})
})

describe('Node-RED Dashboard 2.0 - Number Input (Dynamic Properties)', () => {
beforeEach(() => {
cy.deployFixture('dashboard-number-input')
cy.visit('/dashboard/page1')
})

it('Set the dynamic properties: set input "label"', () => {
cy.get('#nrdb-ui-widget-bc2a346d36830a3f').should('exist')
cy.clickAndWait(cy.get('button').contains('Dynamic Property: Label'))
cy.get('#nrdb-ui-widget-bc2a346d36830a3f .v-input .v-field .v-field__field').find('.v-field-label').should('exist')
})

it('Set the dynamic properties: set input "clearable"', () => {
cy.get('#nrdb-ui-widget-bc2a346d36830a3f').should('exist')
cy.clickAndWait(cy.get('button').contains('Dynamic Property: Clearable'))
cy.get('#nrdb-ui-widget-bc2a346d36830a3f').type('4')
cy.get('#nrdb-ui-widget-bc2a346d36830a3f .v-input .v-field .v-field__field').find('.v-field-label').should('exist')
})
})
18 changes: 18 additions & 0 deletions cypress/tests/widgets/text-input.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* eslint-disable cypress/no-unnecessary-waiting */
describe('Node/-RED Dashboard 2.0 - Text Input Widget', () => {
beforeEach(() => {
cy.deployFixture('dashboard-text-input')
cy.visit('/dashboard/page1')
})

// Test case: Renders the Text Input widget correctly
it('renders the Text Input widget correctly', () => {
cy.get('#nrdb-ui-widget-ab3346b81a7cf742 input[type="text"]').should('exist')
})

// Test case: Displays the tooltip correctly
it('displays the tooltip correctly', () => {
cy.get('#nrdb-ui-widget-ab3346b81a7cf742 .nrdb-ui-text-field').trigger('mouseover')
cy.get('.v-tooltip').should('contain', 'Tooltip Text')
})
})
9 changes: 9 additions & 0 deletions docs/components/DynamicPropsTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<th>Prop</th>
<th>Payload</th>
<th>Structures</th>
<th>Example Values</th>
</tr>
</thead>
<tbody>
Expand All @@ -19,6 +20,14 @@
</ul>
<code v-else>{{ value.structure[0] }}</code>
</td>
<td>
<div v-if="value.examples" style="display: flex; gap: 4px; align-items: center;">
<template v-for="(example, i) in value.examples" :key="example">
<code >{{ example }}</code>
<span v-if="i !== value.examples?.length - 1">|</span>
</template>
</div>
</td>
</tr>
</tbody>
</table>
Expand Down
44 changes: 38 additions & 6 deletions docs/nodes/widgets/ui-text-input.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,28 @@ description: Incorporate ui-text-input in Node-RED Dashboard 2.0 for customizabl
props:
Group: Defines which group of the UI Dashboard this widget will render in.
Size: Controls the width of the text input field with respect to the parent group. Maximum value is the width of the group.
Icon: Renders a Material Design icon within the Text Input. There is no need to include the "mdi-" prefix.
Icon Position: If "Icon" is defined, this property controls which side of the "Label" the icon will render on.
Icon Inner Position: If "Icon" is defined, this property controls if icon is render inside or outside the text input box
Label: The text shown within the text input field.
Icon:
description: Renders a Material Design icon within the Text Input. There is no need to include the "mdi-" prefix.
dynamic: true
Icon Position:
description: If "Icon" is defined, this property controls which side of the "Label" the icon will render on.
dynamic: true
Icon Inner Position:
description: If "Icon" is defined, this property controls if icon is render inside or outside the text input box.
dynamic: true
Label:
description: The text shown within the text input field.
dynamic: true
Tooltip: The text shown when hovering over the text input field.
Mode: The type of HTML input to display. Options - text | password | email | number | tel | color | date | time | week | month | datetime-local
Mode:
description: The type of HTML input to display. Options - text | password | email | number | tel | color | date | time | week | month | datetime-local
dynamic: true
Passthrough: If this node receives a msg in Node-RED, should it be passed through to the output as if a new value was inserted to the input?
Send On "Delay": If true, then a msg will be emitted will be sent after the delay specified in "Delay (ms)".
Delay: If "Send on Delay" is true, then the value in the text input will be send after this (ms) delay.
Clear selection with button: If true, a clear icon/button appears on the rigth side to clear the text input
Clear selection with button:
description: If true, a clear icon/button appears on the rigth side to clear the text input
dynamic: true
Send On "Focus Leave": Sends a msg when the text input loses focus. Will always send, even if the value has not changed.
Send On "Press Enter": Sends a msg when the user presses the enter key. Will always send, even if the value has not changed.
Send On "Clear Button": Send a msg when the user clear the text input using the clear button, the "Clear Selection" button must be enabled.
Expand All @@ -24,6 +36,26 @@ dynamic:
Class:
payload: msg.class
structure: ["String"]
Mode:
payload: msg.ui_update.mode
structure: ["String"]
Label:
payload: msg.ui_update.label
structure: ["String"]
Icon:
payload: msg.ui_update.icon
structure: ["String"]
Icon Position:
payload: msg.ui_update.iconPosition
structure: ["String"]
examples: ["left", "right"]
Icon Inner Position:
payload: msg.ui_update.iconInnerPosition
structure: ["String"]
examples: ["inside", "outside"]
Clearable:
payload: msg.ui_update.clearable
structure: ["Boolean"]
---

<script setup>
Expand Down
18 changes: 17 additions & 1 deletion nodes/widgets/locales/en-US/ui_text_input.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,25 @@
</ul>
</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">class <span class="property-type">string</span></dt>
<dd>Add a CSS class, or more, to the Button at runtime.</dd>
</dl>
<dl class="message-properties">
<dt class="optional">mode <span class="property-type">string</span></dt>
<dd>Change the text input to a "password", "color", or any of the other available "modes".</dd>
<dt class="optional">label <span class="property-type">string</span></dt>
<dd>Override the label displayed with the Text Input.</dd>
<dt class="optional">icon <span class="property-type">string</span></dt>
<dd>Override the icon defined in the initial configuration</dd>
<dt class="optional">iconPosition <span class="property-type">'left' | 'right'</span></dt>
<dd>Change which side of the text-input the icon renders</dd>
<dt class="optional">iconInnerPosition <span class="property-type">'inside' | 'outside'</span></dt>
<dd>Defines whether or not the icon renders within the frame of the text-input.</dd>
<dt class="optional">clearable <span class="property-type">boolean</span></dt>
<dd>Controls whether an "x" button appears as a user types in order to quickly clear any written content.</dd>
<dt class="optional">class <span class="property-type">string</span></dt>
<dd>Add a CSS class, or more, to the Button at runtime.</dd>
</dl>
</script>
38 changes: 32 additions & 6 deletions nodes/widgets/ui_text_input.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,46 @@
const datastore = require('../store/data.js')
const statestore = require('../store/state.js')

module.exports = function (RED) {
function TextInputNode (config) {
const node = this

// create node in Node-RED
RED.nodes.createNode(this, config)

// this ndoe need to store content/value from UI
node.value = null
const node = this

// which group are we rendering this widget
const group = RED.nodes.getNode(config.group)

const evts = {
onChange: true,
beforeSend: async function (msg) {
const updates = msg.ui_update
if (updates) {
if (typeof updates.label !== 'undefined') {
// dynamically set "label" property
statestore.set(group.getBase(), node, msg, 'label', updates.label)
}
if (typeof updates.mode !== 'undefined') {
// dynamically set "mode" property
statestore.set(group.getBase(), node, msg, 'mode', updates.mode)
}
if (typeof updates.clearable !== 'undefined') {
// dynamically set "clearable" property
statestore.set(group.getBase(), node, msg, 'clearable', updates.clearable)
}
if (typeof updates.icon !== 'undefined') {
// dynamically set "icon" property
statestore.set(group.getBase(), node, msg, 'icon', updates.icon)
}
if (typeof updates.iconPosition !== 'undefined') {
// dynamically set "icon position" property
statestore.set(group.getBase(), node, msg, 'iconPosition', updates.iconPosition)
}
if (typeof updates.iconInnerPosition !== 'undefined') {
// dynamically set "icon inner position" property
statestore.set(group.getBase(), node, msg, 'iconInnerPosition', updates.iconInnerPosition)
}
}
return msg
},
onInput: function (msg, send) {
// store the latest msg passed to node
datastore.save(group.getBase(), node, msg)
Expand Down
Loading
Loading