From 998f92bff8ad67aef90e24b0336b7c116bea4acd Mon Sep 17 00:00:00 2001 From: Roland Schilter Date: Fri, 4 Aug 2017 11:28:34 +0200 Subject: [PATCH] Fix rendering of exported SVG Replace invalid characters in attribute value; fixes the error Unescaped '<' not allowed in attributes values --- client/app/scripts/charts/edge.js | 7 ++++--- client/app/scripts/charts/node-shapes.js | 3 ++- .../scripts/utils/__tests__/dom-utils-test.js | 15 +++++++++++++++ client/app/scripts/utils/dom-utils.js | 16 ++++++++++++++++ 4 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 client/app/scripts/utils/__tests__/dom-utils-test.js create mode 100644 client/app/scripts/utils/dom-utils.js diff --git a/client/app/scripts/charts/edge.js b/client/app/scripts/charts/edge.js index ae0320a152..7d31aae8a3 100644 --- a/client/app/scripts/charts/edge.js +++ b/client/app/scripts/charts/edge.js @@ -3,6 +3,7 @@ import { connect } from 'react-redux'; import classNames from 'classnames'; import { enterEdge, leaveEdge } from '../actions/app-actions'; +import { encodeIdAttribute, decodeIdAttribute } from '../utils/dom-utils'; class Edge extends React.Component { @@ -19,7 +20,7 @@ class Edge extends React.Component { return ( @@ -35,11 +36,11 @@ class Edge extends React.Component { } handleMouseEnter(ev) { - this.props.enterEdge(ev.currentTarget.id); + this.props.enterEdge(decodeIdAttribute(ev.currentTarget.id)); } handleMouseLeave(ev) { - this.props.leaveEdge(ev.currentTarget.id); + this.props.leaveEdge(decodeIdAttribute(ev.currentTarget.id)); } } diff --git a/client/app/scripts/charts/node-shapes.js b/client/app/scripts/charts/node-shapes.js index f73545d864..42d7b3e3d9 100644 --- a/client/app/scripts/charts/node-shapes.js +++ b/client/app/scripts/charts/node-shapes.js @@ -20,13 +20,14 @@ import { octagonShapeProps, cloudShapeProps, } from '../utils/node-shape-utils'; +import { encodeIdAttribute } from '../utils/dom-utils'; function NodeShape(shapeType, shapeElement, shapeProps, { id, highlighted, color, metric }) { const { height, hasMetric, formattedValue } = getMetricValue(metric); const className = classNames('shape', `shape-${shapeType}`, { metrics: hasMetric }); const metricStyle = { fill: getMetricColor(metric) }; - const clipId = `metric-clip-${id}`; + const clipId = encodeIdAttribute(`metric-clip-${id}`); return ( diff --git a/client/app/scripts/utils/__tests__/dom-utils-test.js b/client/app/scripts/utils/__tests__/dom-utils-test.js new file mode 100644 index 0000000000..17a59a9ddd --- /dev/null +++ b/client/app/scripts/utils/__tests__/dom-utils-test.js @@ -0,0 +1,15 @@ +import { encodeIdAttribute, decodeIdAttribute } from '../dom-utils'; + +describe('DomUtils', () => { + describe('encodeIdAttribute/decodeIdAttribute', () => { + it('encode should be reversible by decode ', () => { + [ + '123-abc;', + ';;<<><>', + '!@#$%^&*()+-\'"', + ].forEach((input) => { + expect(decodeIdAttribute(encodeIdAttribute(input))).toEqual(input); + }); + }); + }); +}); diff --git a/client/app/scripts/utils/dom-utils.js b/client/app/scripts/utils/dom-utils.js new file mode 100644 index 0000000000..b993a51361 --- /dev/null +++ b/client/app/scripts/utils/dom-utils.js @@ -0,0 +1,16 @@ +/** + * Cleans up a value to be used as an element ID. + * + * Encodes invalid characters to be valid in XHTML and makes it + * so that it can be reversed by {@link decodeIdAttribute}. + */ +export function encodeIdAttribute(id) { + return id.replace(/[<>&;]/gm, m => `__u${m.charCodeAt(0)}__`); +} + +/** + * Reverts {@link encodeIdAttribute}. + */ +export function decodeIdAttribute(id) { + return id.replace(/__u(\d+)__/gm, (m, d) => String.fromCharCode(d)); +}