From 476771b6e4aba25edeed02323eff3d9d644718f1 Mon Sep 17 00:00:00 2001 From: "grafana-delivery-bot[bot]" <132647405+grafana-delivery-bot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 20:52:32 +0300 Subject: [PATCH] [v10.1.x] Canvas: Avoid conflicting stylesheets when loading SVG icons (#75032) --- .../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('