From d95b7bec3a824d7c7377d42506939866e5a6743f Mon Sep 17 00:00:00 2001
From: Adela Almasan <88068998+adela-almasan@users.noreply.github.com>
Date: Mon, 18 Sep 2023 12:38:45 -0500
Subject: [PATCH] Canvas: Avoid conflicting stylesheets when loading SVG icons
(#74461)
(cherry picked from commit 7171b35095fbef8013990ac49b4dfcd53082cb09)
---
.../core/components/SVG/SanitizedSVG.test.tsx | 58 +++++++++++++++++++
.../app/core/components/SVG/SanitizedSVG.tsx | 25 +++++++-
public/app/core/components/SVG/utils.ts | 30 ++++++++++
public/app/features/canvas/elements/icon.tsx | 1 +
4 files changed, 112 insertions(+), 2 deletions(-)
create mode 100644 public/app/core/components/SVG/SanitizedSVG.test.tsx
create mode 100644 public/app/core/components/SVG/utils.ts
diff --git a/public/app/core/components/SVG/SanitizedSVG.test.tsx b/public/app/core/components/SVG/SanitizedSVG.test.tsx
new file mode 100644
index 000000000000..16f2af2cd99c
--- /dev/null
+++ b/public/app/core/components/SVG/SanitizedSVG.test.tsx
@@ -0,0 +1,58 @@
+import { getSvgId, getSvgStyle, svgStyleCleanup } from './utils';
+
+const ID = 'TEST_ID';
+
+const svgNoId =
+ '';
+
+const svgWithId = ``;
+
+const svgWithWrongIdInStyle =
+ '';
+
+describe('SanitizedSVG', () => {
+ it('should cleanup the style and generate an ID', () => {
+ const cleanStyle = svgStyleCleanup(svgNoId);
+ const updatedStyle = getSvgStyle(cleanStyle);
+ const svgId = getSvgId(cleanStyle);
+
+ expect(cleanStyle.indexOf('id="')).toBeGreaterThan(-1);
+ expect(svgId).toBeDefined();
+ expect(svgId?.startsWith('x')).toBeTruthy();
+ expect(updatedStyle?.indexOf(`#${svgId}`)).toBeGreaterThan(-1);
+
+ expect(cleanStyle).toEqual(
+ ``
+ );
+ });
+
+ it('should cleanup the style and use the existing ID', () => {
+ const cleanStyle = svgStyleCleanup(svgWithId);
+ const updatedStyle = getSvgStyle(cleanStyle);
+ const svgId = getSvgId(cleanStyle);
+
+ expect(cleanStyle.indexOf('id="')).toBeGreaterThan(-1);
+ expect(svgId).toBeDefined();
+ expect(svgId).toEqual(ID);
+ expect(updatedStyle?.indexOf(`#${svgId}`)).toBeGreaterThan(-1);
+
+ expect(cleanStyle).toEqual(
+ ``
+ );
+ });
+
+ it('should cleanup the style and replace the wrong ID', () => {
+ const cleanStyle = svgStyleCleanup(svgWithWrongIdInStyle);
+ const updatedStyle = getSvgStyle(cleanStyle);
+ const svgId = getSvgId(cleanStyle);
+
+ expect(cleanStyle.indexOf('id="')).toBeGreaterThan(-1);
+ expect(svgId).toBeDefined();
+ expect(svgId?.startsWith('x')).toBeTruthy();
+ expect(updatedStyle?.indexOf(`#${svgId}`)).toBeGreaterThan(-1);
+
+ expect(cleanStyle).toEqual(
+ ``
+ );
+ });
+});
diff --git a/public/app/core/components/SVG/SanitizedSVG.tsx b/public/app/core/components/SVG/SanitizedSVG.tsx
index 5f4f0a6f5aa6..b53bdccf5a10 100644
--- a/public/app/core/components/SVG/SanitizedSVG.tsx
+++ b/public/app/core/components/SVG/SanitizedSVG.tsx
@@ -3,8 +3,13 @@ import SVG, { Props } from 'react-inlinesvg';
import { textUtil } from '@grafana/data';
-export const SanitizedSVG = (props: Props) => {
- return ;
+import { svgStyleCleanup } from './utils';
+
+type SanitizedSVGProps = Props & { cleanStyle?: boolean };
+
+export const SanitizedSVG = (props: SanitizedSVGProps) => {
+ const { cleanStyle, ...inlineSvgProps } = props;
+ return ;
};
let cache = new Map();
@@ -15,5 +20,21 @@ function getCleanSVG(code: string): string {
clean = textUtil.sanitizeSVGContent(code);
cache.set(code, clean);
}
+
+ return clean;
+}
+
+function getCleanSVGAndStyle(code: string): string {
+ let clean = cache.get(code);
+ if (!clean) {
+ clean = textUtil.sanitizeSVGContent(code);
+
+ if (clean.indexOf('