Skip to content

Commit

Permalink
Merge pull request #1325 from FlowFuse/1253-y-axis-type
Browse files Browse the repository at this point in the history
Add msg support for y axis type
  • Loading branch information
joepavitt authored Oct 4, 2024
2 parents 6bdeaaa + 913ee57 commit a0b468b
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 43 deletions.
40 changes: 38 additions & 2 deletions examples/bar-chart.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,23 @@
"group": "",
"name": "",
"label": "Sales in Million",
"order": 9007199254740991,
"order": 3,
"chartType": "bar",
"category": "location",
"categoryType": "property",
"xAxisLabel": "",
"xAxisProperty": "",
"xAxisPropertyType": "msg",
"xAxisPropertyType": "str",
"xAxisType": "category",
"xAxisFormat": "",
"xAxisFormatType": "auto",
"yAxisLabel": "",
"yAxisProperty": "sales_millions",
"yAxisPropertyType": "property",
"ymin": "",
"ymax": "",
"action": "append",
"stackSeries": false,
"pointShape": "circle",
"pointRadius": 4,
"showLegend": false,
Expand Down Expand Up @@ -71,5 +77,35 @@
"2a23595f05d3331e"
]
]
},
{
"id": "f4c5f4c74fbd2db2",
"type": "inject",
"z": "0758321f1687e812",
"name": "Clear",
"props": [
{
"p": "payload"
},
{
"p": "action",
"v": "replace",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "[]",
"payloadType": "json",
"x": 210,
"y": 800,
"wires": [
[
"2a23595f05d3331e"
]
]
}
]
40 changes: 38 additions & 2 deletions examples/line-chart.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,25 @@
"type": "ui-chart",
"z": "0758321f1687e812",
"group": "",
"name": "",
"name": "Line Chart",
"label": "chart",
"order": 9007199254740991,
"chartType": "line",
"category": "location",
"categoryType": "property",
"xAxisLabel": "",
"xAxisProperty": "datestamp",
"xAxisPropertyType": "msg",
"xAxisPropertyType": "property",
"xAxisType": "time",
"xAxisFormat": "",
"xAxisFormatType": "auto",
"yAxisLabel": "",
"yAxisProperty": "temp",
"yAxisPropertyType": "property",
"ymin": "",
"ymax": "",
"action": "append",
"stackSeries": false,
"pointShape": "circle",
"pointRadius": 4,
"showLegend": true,
Expand Down Expand Up @@ -71,5 +77,35 @@
"efccfdf502300871"
]
]
},
{
"id": "33ee5762cce0ee32",
"type": "inject",
"z": "862ec766f2af6f61",
"name": "Clear",
"props": [
{
"p": "payload"
},
{
"p": "action",
"v": "replace",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "[]",
"payloadType": "json",
"x": 290,
"y": 1100,
"wires": [
[
"efccfdf502300871"
]
]
}
]
44 changes: 40 additions & 4 deletions examples/scatter-chart.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,25 @@
"type": "ui-chart",
"z": "0758321f1687e812",
"group": "",
"name": "",
"name": "Scatter Chart",
"label": "chart",
"order": 9007199254740991,
"order": 4,
"chartType": "scatter",
"category": "",
"categoryType": "str",
"xAxisLabel": "",
"xAxisProperty": "x",
"xAxisPropertyType": "msg",
"xAxisPropertyType": "property",
"xAxisType": "linear",
"xAxisFormat": "",
"xAxisFormatType": "auto",
"yAxisLabel": "",
"yAxisProperty": "y",
"yAxisPropertyType": "property",
"ymin": "",
"ymax": "",
"action": "replace",
"stackSeries": false,
"pointShape": "circle",
"pointRadius": 4,
"showLegend": true,
Expand Down Expand Up @@ -47,7 +53,7 @@
"id": "937b42a40fdcf424",
"type": "inject",
"z": "0758321f1687e812",
"name": "",
"name": "Arc data",
"props": [
{
"p": "payload"
Expand All @@ -71,5 +77,35 @@
"91ced026339a3eeb"
]
]
},
{
"id": "60e4dfb81bdbcb03",
"type": "inject",
"z": "0758321f1687e812",
"name": "Clear",
"props": [
{
"p": "payload"
},
{
"p": "action",
"v": "replace",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "[]",
"payloadType": "json",
"x": 250,
"y": 860,
"wires": [
[
"91ced026339a3eeb"
]
]
}
]
24 changes: 13 additions & 11 deletions nodes/widgets/ui_chart.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
return RED._('@flowfuse/node-red-dashboard/ui-chart:ui-chart.' + property)
}
const noneType = { value: 'none', label: RED._('@flowfuse/node-red-dashboard/ui-chart:ui-chart.label.none'), hasValue: false }
const propertyType = { value: 'property', label: RED._('@flowfuse/node-red-dashboard/ui-chart:ui-chart.label.key') }
const keyType = { value: 'property', label: RED._('@flowfuse/node-red-dashboard/ui-chart:ui-chart.label.key') }

function hexToRgb (hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
Expand All @@ -57,9 +57,9 @@

function getSeriesTypes (xAxisType) {
if (xAxisType === 'category') {
return [noneType, 'msg', 'str', 'json', propertyType]
return [noneType, 'msg', 'str', 'json', keyType]
} else {
return [noneType, 'msg', 'str', 'json', propertyType]
return [noneType, 'msg', 'str', 'json', keyType]
}
}

Expand All @@ -82,6 +82,7 @@
xAxisFormatType: { value: 'auto' },
yAxisLabel: { value: '' },
yAxisProperty: { value: null },
yAxisPropertyType: { value: 'property' },
ymin: { value: '', validate: function (value) { return value === '' || RED.validators.number() } },
ymax: { value: '', validate: function (value) { return value === '' || RED.validators.number() } },
action: { value: 'append' },
Expand Down Expand Up @@ -224,14 +225,14 @@
types: getSeriesTypes($('#node-input-xAxisType').val())
})
$('#node-input-xAxisProperty').typedInput({
default: propertyType.value,
default: keyType.value,
typeField: $('#node-input-xAxisPropertyType'),
types: ['msg', 'str', propertyType]
types: ['msg', 'str', keyType]
})
$('#node-input-yAxisProperty').typedInput({
default: propertyType.value,
default: keyType.value,
typeField: $('#node-input-yAxisPropertyType'),
types: [propertyType]
types: ['msg', keyType] // makes no sense for the y-axis to be a string
})

const updateXAxisTypeOptions = function (options, defaultValue) {
Expand Down Expand Up @@ -298,9 +299,9 @@
}
})

// handle event when chart's type is changed
// handle event when chart's series property is changed
$('#node-input-category').change((evt) => {
const categoryType = $(evt.target).val()
const categoryType = $('#node-input-category').typedInput('type')

if (categoryType === 'json') {
// hide y-axis property setting as category will now control that
Expand All @@ -313,8 +314,8 @@
// ensure the xAxis PropertyType isn't an invalid value
if (this.xAxisPropertyType === 'msg' && this.xAxisProperty === '') {
// auto-fix it for the user
this.xAxisPropertyType = propertyType.value
$('#node-input-xAxisProperty').typedInput('type', propertyType.value)
this.xAxisPropertyType = keyType.value
$('#node-input-xAxisProperty').typedInput('type', keyType.value)
}

// Stack/Group-By Options - convert from bool to string values for <select>
Expand Down Expand Up @@ -550,6 +551,7 @@ <h4>Axis Configuration</h4>
</div>
<div id="node-container-yAxisProperty" class="node-chart-property">
<label style="width: auto" for="node-input-yAxisProperty">Y:</label>
<input type="hidden" id="node-input-yAxisPropertyType">
<input style="flex-grow: 1" type="text" id="node-input-yAxisProperty" data-i18n="[placeholder]ui-chart.label.propertyPlaceholder">
</div>
</div>
Expand Down
44 changes: 33 additions & 11 deletions nodes/widgets/ui_chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,22 @@ module.exports = function (RED) {
return value
}

// ensure sane defaults
if (!['msg', 'str', 'property'].includes(config.xAxisPropertyType)) {
config.xAxisPropertyType = 'property' // default to 'key'
}
if (!['msg', 'property'].includes(config.yAxisPropertyType)) {
config.yAxisPropertyType = 'property' // default to 'key'
}
if (config.xAxisPropertyType === 'msg' && !config.xAxisProperty) {
config.xAxisPropertyType = 'property' // msg needs a property to evaluate, default to 'key'
}
if (config.yAxisPropertyType === 'msg' && !config.yAxisProperty) {
config.yAxisPropertyType = 'property' // msg needs a property to evaluate, default to 'key'
}
config.xAxisProperty = config.xAxisProperty || ''
config.yAxisProperty = config.yAxisProperty || ''

const evts = {
// beforeSend will run before messages are sent client-side, as well as before sending on within Node-RED
// here, we use it to pre-process chart data to format it ready for plotting
Expand Down Expand Up @@ -67,42 +83,48 @@ module.exports = function (RED) {
}
}

function evaluateNodePropertyWithKey (node, msg, payload, property, propertyType) {
if (propertyType === 'property' /* AKA key */) {
return RED.util.evaluateNodeProperty(property, 'msg', node, payload)
}
return RED.util.evaluateNodeProperty(property, propertyType, node, msg)
}

// function to process a data point being appended to a line/scatter chart
function addToChart (payload, series) {
const datapoint = {}
// we group/categorize data by "series"
datapoint.category = series

// get our x value, if set
if (config.xAxisPropertyType === 'msg' && config.xAxisProperty === '') {
// handle a missing declaration of x-axis property, and backup to time series
config.xAxisPropertyType = 'property'
}
const x = RED.util.evaluateNodeProperty(config.xAxisProperty, config.xAxisPropertyType, node, msg)

// construct our datapoint
if (typeof payload === 'number') {
// do we have an x-property defined - if not, we're assuming time series
datapoint.x = config.xAxisProperty !== '' ? x : (new Date()).getTime()
// since key would attempt to evaluate a property on a number, we don't do evaluation when
// x-axis is type is 'key' (only when it's 'msg' or 'str')
datapoint.x = config.xAxisPropertyType === 'msg' || config.xAxisPropertyType === 'str'
? evaluateNodePropertyWithKey(node, msg, payload, config.xAxisProperty, config.xAxisPropertyType)
: (new Date()).getTime()
datapoint.y = payload
} else if (typeof payload === 'object') {
let x = evaluateNodePropertyWithKey(node, msg, payload, config.xAxisProperty, config.xAxisPropertyType)
// may have been given an x/y object already
let x = getProperty(payload, config.xAxisProperty)
let y = payload.y
if (x === undefined || x === null) {
x = (new Date()).getTime()
}
if (Array.isArray(series)) {
let y
if (series.length > 1) {
y = series.map((s) => {
return getProperty(payload, s)
})
} else {
y = getProperty(payload, series[0])
}
datapoint.y = y
} else {
datapoint.y = evaluateNodePropertyWithKey(node, msg, payload, config.yAxisProperty, config.yAxisPropertyType)
}
datapoint.x = x
datapoint.y = y
}
return datapoint
}
Expand Down
Loading

0 comments on commit a0b468b

Please sign in to comment.