node-export-server
node-export-server copied to clipboard
Inject css variables using nodejs module
Hello,
My team and I are attempting to implement the export module to enable the downloading of our charts. However, we’re encountering an issue due to our extensive use of CSS variables, which your server doesn’t seem to accept as it doesn’t allow external resources.
I’ve begun setting up the export server Node.js module on our Express app. The image generation appears to be functioning well, but we’re still facing the same issue. We’re unable to inject the CSS variables. Here’s how we’ve implemented it:
module.exports = {
downloadChart: async (req, res) => {
const exportSettings = {
export: {
type: req.body.type,
// this line is just for test purposes
outfile: "./samples/module/" + req.body.filename + "." + "png",
scale: req.body.scale
},
pool: {
minWorkers: 1,
maxWorkers: 1
},
payload: {
svg: req.body.svg
},
customLogic: {
allowCodeExecution: true,
resources: {
css: "here I'd like to inject our variables"
}
}
};
const options = exporter.setOptions(exportSettings);
// Initialize a pool of workers
await exporter.initPool(options);
// Perform an export
exporter.startExport(exportSettings, function(res, err) {
console.log(res);
// The export result is now in res.
// It will be base64 encoded (res.data).
// use fs to write the file
fs.writeFile(exportSettings.export.outfile, res.data, "base64", function(err) {
if (err) {
console.error(err);
}
});
// Kill the pool when we're done with it.
exporter.killPool();
});
// send file
res.status(200);
}
};
Thanks for reporting @sebherrerabe!
As a workaround, try exporting from a JSON config and not from an SVG payload. Then, the resources.css
option should most likely be taken into account properly. If you try it out and it does not work, please share your chart's config as well as the CSS variables that you're setting so I could investigate this further.
@jszuminski
Thanks for replying! In fact, I found the reason why the variables weren’t working. I think the documentation is wrong, at least for the export settings of the Node.js module. The property is not "customLogic"
, instead it’s "customCode"
. I was able to inject our CSS variables successfully.
Unfortunately, that wasn’t our only problem… Sadly, our HTML elements do not render as desired when exporting a chart (we use React). As you might’ve guessed, what we are trying to do is to use the Exporting module provided by you in the front and change the URL to the one pointing at our custom Express endpoint (where we import the export server module).
I believe the Exporting module in the front converts the whole chart into an SVG element but this does not support some features natively. This is a problem to be honest, because we use HTML almost everywhere (we have lots of Highcharts charts). For now, we are even questioning if we should use a library to just take screenshots in the browser.
Config in NodeJs now:
const exportSettings = {
export: {
type: req.body.type,
// TODO: Define the actual directory where the files will be saved
outfile: "./samples/module/" + randomName + "." + req.body.type.split("/")[1],
scale: req.body.scale
},
pool: {
minWorkers: 1,
maxWorkers: 1
},
payload: {
svg: req.body.svg
},
customCode: {
allowCodeExecution: true,
allowFileResources: true,
resources: {
// TODO: Find a way to import the css directly from the file system
css: "@import 'http://localhost:3001/compiled/site.css';"
}
}
};
Config in the front:
/// ...imports
// we init the exporting module
Exporting(Highcharts);
const ChartComponent = (props: Props): JSX.Element => {
// some logic
const chartOptions = {
exporting: {
url: "/charts/download",
allowHtml: true
},
plotOptions: {
column: {
borderRadius: 4,
borderColor: "white",
stacking: "normal",
dataLabels: {
enabled: true,
align: "center",
useHTML: true,
style: {
color: "white",
textOutline: "none"
},
formatter: JSXFormatter<PointLabelObject>(({ point }) => {
if (checkIfSimpleColumn(point)) {
return null;
}
// THE FOLLOWING COMPONENT DOES NOT RENDER CORRECTLY
return (
<Badge variant={dataCollectionStatusData[point.series.userOptions.id].variant}>
{point.y !== point.total && <strong style={{ marginRight: 2 }}>{point.y}</strong>}
{point.series.name}
</Badge>
);
})
}
// ... other properties
}
}
};
return (
<HighchartsReact
highcharts={Highcharts}
options={chartOptions}
/>
);
};
Expected result:
Actual result: