Using UIBUILDER with Plotly

Many thanks to @Paul-Reed for prompting me on this.

When using Plotly charts with UIBUILDER, you should note that their code is rather dated in places. In particular, their CSS styling is not really up to current standards. It is neither properly responsive nor does it correctly isolate itself.

Because of this, there are some clashes between their styling and the default UIBUILDER styles. This post fixes that.

To use Plotly correctly, use this as your index.css content:

/* Load defaults from `<userDir>/node_modules/node-red-contrib-uibuilder/front-end/uib-brand.min.css`
 * This is optional but reasonably complete and allows for light/dark mode switching.
 */
@import url("../uibuilder/uib-brand.min.css");

#tester {
  width: 100%;
  height: 50vh;
  background-color: antiquewhite;
  margin-top: 1em;
}

.js-plotly-plot .plotly .main-svg {
  margin:0;
  background-color: unset;
}

.js-plotly-plot .plotly .modebar-btn svg {
    background-color: unset;
    margin: unset;
}

With this, you can also simplify the required HTML to this:

<div id="tester"></div>

Note: this is based on Plotly's getting started example.

2 Likes

And you probably want Plotly to be responsive to page/window sizing!

<script defer src="https://cdn.plot.ly/plotly-3.0.1.min.js" charset="utf-8"></script>
<div id="plotly-chart"></div>
/* Load defaults from `<userDir>/node_modules/node-red-contrib-uibuilder/front-end/uib-brand.min.css`
 * This is optional but reasonably complete and allows for light/dark mode switching.
 */
@import url("../uibuilder/uib-brand.min.css");

#plotly-chart {
    /* HAVE to set at least width and height for Plotly to render */
    /* width: 100%;
    height: 50vh; */
    margin-top: 1em;
}

/* Overrides for UIUILDER's default styles */
.js-plotly-plot .plotly .main-svg {
    margin:0;
    background-color: unset;
}
.js-plotly-plot .plotly .modebar-btn svg {
    background-color: unset;
    margin: unset;
}
const plotlyChart = document.getElementById('plotly-chart')
if (plotlyChart) {
    const plotlyData = [{
            x: [1, 2, 3, 4, 5],
            y: [1, 2, 4, 8, 16]
        }]
    const plotlyLayout = {
        title: {
            text: 'Responsive to window\'s size!'
        },
        font: {size: 18}
    }
    const plotlyConfig = {
        responsive: true, // Make the chart responsive
        margin: { t: 0 },
    }
    // @ts-ignore
    Plotly.newPlot( plotlyChart, plotlyData, plotlyLayout, plotlyConfig )
}

You can, of course, update the data, titles, etc from Node-RED by using the uibuilder client library's uibuilder.onChange('msg', (msg) => { ... }) or uibuilder.onTopic('mytopic', (msg) => { ... }) front-end functions.


Note that there is already a documentation placeholder for doing charts with UIBUILDER, this and some examples for other chart libraries like uPlot are being added and will be available in the next release.

So to be clear...
<script defer src="https://cdn.plot.ly/plotly-3.0.1.min.js" charset="utf-8"></script>
goes in the head of index.html?

1 Like

Correct.

Please also see my updated note in the main forum about plotly. I discovered how to make sizing more dynamic. You can use the updated example there.

Oh, that probably was from there wasn't it! :slight_smile:

Doh, hadn't noticed we had changed threads! :face_with_spiral_eyes:

Not rendering here unfortunately.
I did notice this what you posted above...

#plotly-chart {
    /* HAVE to set at least width and height for Plotly to render */
    /* width: 100%;
    height: 50vh; */
    margin-top: 1em;
}

...which seems contradictory, but uncommenting them made little difference.

However, the console showed;

z5

I have attached a uib-save node & index.html, index.css etc

[{"id":"5a5653fbb5559e32","type":"group","z":"1326aadbacf36704","name":"Run this to update the front-end code files (after setting the uibuilder url name)","style":{"fill":"#ffffff","fill-opacity":"0.31","label":true,"color":"#000000"},"nodes":["55abd225ad81d743","490d3439c60c1b7a","c1d742d7201e9392","73dc5646a81ea558","46757650f470b5bd","85c41b050ea3a3de","e26eb52f5db65966","d7be075fdea36b47"],"x":1124,"y":1859,"w":662,"h":162},{"id":"55abd225ad81d743","type":"inject","z":"1326aadbacf36704","g":"5a5653fbb5559e32","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1185,"y":1940,"wires":[["73dc5646a81ea558","46757650f470b5bd","e26eb52f5db65966"]],"l":false},{"id":"490d3439c60c1b7a","type":"template","z":"1326aadbacf36704","g":"5a5653fbb5559e32","name":"","field":"payload","fieldType":"msg","format":"html","syntax":"mustache","template":"<!doctype html>\n<html lang=\"en\"><head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\n    <title>Quote of the day - Node-RED UIBUILDER</title>\n    <meta name=\"description\" content=\"Node-RED UIBUILDER - Quote of the day\">\n    <link rel=\"icon\" href=\"./images/node-blue.ico\">\n\n    <!-- Your own CSS -->\n    <link type=\"text/css\" rel=\"stylesheet\" href=\"./index.css\" media=\"all\">\n\n    <!-- #region Supporting Scripts. These MUST be in the right order. Note no leading / - socket.io no longer needed  -->\n    <script defer src=\"../uibuilder/uibuilder.iife.min.js\"></script>\n    <script defer src=\"./index.js\"></script>\n    <script defer src=\"https://cdn.plot.ly/plotly-3.0.1.min.js\" charset=\"utf-8\"></script>\n    <!-- #endregion -->\n</head><body>\n    <h1>UIBUILDER Plotly test</h1>\n    <div role=\"doc-subtitle\">Using the UIBUILDER IIFE library.</div>\n    <!-- Updated for uibuilder v6.1.0 -->\n\n    <div id=\"plotly-chart\"></div>\n\n    <div id=\"more\"><!-- '#more' is used as a parent for dynamic HTML content in examples --></div>\n\n\n</body></html>","output":"str","x":1460,"y":1900,"wires":[["c1d742d7201e9392"]]},{"id":"c1d742d7201e9392","type":"uib-save","z":"1326aadbacf36704","g":"5a5653fbb5559e32","url":"","folder":"src","fname":"","createFolder":false,"reload":false,"usePageName":false,"encoding":"utf8","mode":438,"name":"","topic":"","x":1660,"y":1940,"wires":[]},{"id":"73dc5646a81ea558","type":"change","z":"1326aadbacf36704","g":"5a5653fbb5559e32","name":"index.html","rules":[{"t":"set","p":"fname","pt":"msg","to":"index.html","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1310,"y":1900,"wires":[["490d3439c60c1b7a"]]},{"id":"46757650f470b5bd","type":"change","z":"1326aadbacf36704","g":"5a5653fbb5559e32","name":"index.js","rules":[{"t":"set","p":"fname","pt":"msg","to":"index.js","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1300,"y":1940,"wires":[["85c41b050ea3a3de"]]},{"id":"85c41b050ea3a3de","type":"template","z":"1326aadbacf36704","g":"5a5653fbb5559e32","name":"","field":"payload","fieldType":"msg","format":"javascript","syntax":"mustache","template":"const plotlyChart = document.getElementById('plotly-chart')\nif (plotlyChart) {\n    const plotlyData = [{\n        x: [1, 2, 3, 4, 5],\n        y: [1, 2, 4, 8, 16]\n    }]\n    const plotlyLayout = {\n        title: {\n            text: 'Responsive to window\\'s size!'\n        },\n        font: { size: 18 }\n    }\n    const plotlyConfig = {\n        responsive: true, // Make the chart responsive\n        margin: { t: 0 },\n    }\n    // @ts-ignore\n    Plotly.newPlot(plotlyChart, plotlyData, plotlyLayout, plotlyConfig)\n}","output":"str","x":1460,"y":1940,"wires":[["c1d742d7201e9392"]]},{"id":"e26eb52f5db65966","type":"change","z":"1326aadbacf36704","g":"5a5653fbb5559e32","name":"index.css","rules":[{"t":"set","p":"fname","pt":"msg","to":"index.css","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1300,"y":1980,"wires":[["d7be075fdea36b47"]]},{"id":"d7be075fdea36b47","type":"template","z":"1326aadbacf36704","g":"5a5653fbb5559e32","name":"","field":"payload","fieldType":"msg","format":"css","syntax":"mustache","template":"/* Load defaults from `<userDir>/node_modules/node-red-contrib-uibuilder/front-end/uib-brand.min.css`\n * This is optional but reasonably complete and allows for light/dark mode switching.\n */\n@import url(\"../uibuilder/uib-brand.min.css\");\n\n#plotly-chart {\n    /* HAVE to set at least width and height for Plotly to render */\n    width: 100%;\n    height: 50vh;\n    margin-top: 1em;\n}\n\n/* Overrides for UIUILDER's default styles */\n.js-plotly-plot .plotly .main-svg {\n    margin: 0;\n    background-color: unset;\n}\n\n.js-plotly-plot .plotly .modebar-btn svg {\n    background-color: unset;\n    margin: unset;\n}","output":"str","x":1460,"y":1980,"wires":[["c1d742d7201e9392"]]}]

Hi Paul, you've loaded the plotly library AFTER the index.js, try moving it to before the uibuilder library

1 Like

That is only showing that if you use the responsive layout, you don't need to specify width and height explicitly. You do have to do that if you don't use the responsive layout.

1 Like

Yes, it's rendered now!
Thanks :laughing:

1 Like

Getting some really nice clean looking charts now with Plotly + UIbuilder.

I've written a function that builds the data points/timestamps into the the required format, stores them in context, adding new data, and weeding out old data, so I can control the size of the stored data. Then it redraws the chart from that data whenever a new browser connection takes place, and continues to add further data, point by point thereafter.
I did consider using the uib-cache node, but it could not store it in Plotly's bulk data format.
This chart is mainly for phone/tablet use, so I've disabled most of the interactive functions, but iis nicely responsive.

4 Likes