From 8dfe10ebe87d8974c1689427628094a26b502b75 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Mon, 1 Apr 2019 18:29:48 +0200 Subject: [PATCH] fix(icon): handle unescaped characters in names Handles the icon registry not being able to find icons whose ids contain unescaped characters (e.g. spaces). Note that for this example in particular having a space in the id is invalid, however I did it since supporting it is trivial. Fixes #15673. --- src/material/icon/fake-svgs.ts | 8 ++++++++ src/material/icon/icon-registry.ts | 4 +++- src/material/icon/icon.spec.ts | 23 +++++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/material/icon/fake-svgs.ts b/src/material/icon/fake-svgs.ts index 1148f2387584..ec345f525673 100644 --- a/src/material/icon/fake-svgs.ts +++ b/src/material/icon/fake-svgs.ts @@ -14,6 +14,7 @@ export const FAKE_SVGS = { cat: '', dog: '', + dogWithSpaces: '', farmSet1: ` @@ -37,6 +38,13 @@ export const FAKE_SVGS = { `, + farmSet4: ` + + + + + + `, arrows: ` diff --git a/src/material/icon/icon-registry.ts b/src/material/icon/icon-registry.ts index 20dc882480e1..d85747bf59a2 100644 --- a/src/material/icon/icon-registry.ts +++ b/src/material/icon/icon-registry.ts @@ -449,7 +449,9 @@ export class MatIconRegistry implements OnDestroy { * returns it. Returns null if no matching element is found. */ private _extractSvgIconFromSet(iconSet: SVGElement, iconName: string): SVGElement | null { - const iconSource = iconSet.querySelector('#' + iconName); + // Use the `id="iconName"` syntax in order to escape special + // characters in the ID (versus using the #iconName syntax). + const iconSource = iconSet.querySelector(`[id="${iconName}"]`); if (!iconSource) { return null; diff --git a/src/material/icon/icon.spec.ts b/src/material/icon/icon.spec.ts index c80e5d29ce7e..a14eace6a48c 100644 --- a/src/material/icon/icon.spec.ts +++ b/src/material/icon/icon.spec.ts @@ -268,6 +268,29 @@ describe('MatIcon', () => { verifyPathChildElement(svgChild, 'moo'); }); + it('should handle unescape characters in icon names', () => { + iconRegistry.addSvgIconSetInNamespace('farm', trustUrl('farm-set-4.svg')); + + const fixture = TestBed.createComponent(IconFromSvgName); + const testComponent = fixture.componentInstance; + const matIconElement = fixture.debugElement.nativeElement.querySelector('mat-icon'); + let svgElement: any; + let svgChild: any; + + testComponent.iconName = 'farm:pig with spaces'; + fixture.detectChanges(); + http.expectOne('farm-set-4.svg').flush(FAKE_SVGS.farmSet4); + + expect(matIconElement.childNodes.length).toBe(1); + svgElement = verifyAndGetSingleSvgChild(matIconElement); + expect(svgElement.childNodes.length).toBe(1); + svgChild = svgElement.childNodes[0]; + // The first child should be the element. + expect(svgChild.tagName.toLowerCase()).toBe('g'); + expect(svgChild.getAttribute('name')).toBe('pig'); + verifyPathChildElement(svgChild, 'oink'); + }); + it('should never parse the same icon set multiple times', () => { // Normally we avoid spying on private methods like this, but the parsing is a private // implementation detail that should not be exposed to the public API. This test, though,