Skip to content

Commit 76806e3

Browse files
jelbournjosephperrott
authored andcommitted
fix(icon): remove IDs from source icon set from rendered output (#8266)
1 parent ba36d3a commit 76806e3

File tree

3 files changed

+33
-25
lines changed

3 files changed

+33
-25
lines changed

src/lib/icon/fake-svgs.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,39 +8,40 @@
88

99
/**
1010
* Fake URLs and associated SVG documents used by tests.
11+
* The ID attribute is used to load the icons, the name attribute is only used for testing.
1112
* @docs-private
1213
*/
1314
export const FAKE_SVGS = {
14-
cat: '<svg><path id="meow"></path></svg>',
15-
dog: '<svg><path id="woof"></path></svg>',
15+
cat: '<svg><path id="meow" name="meow"></path></svg>',
16+
dog: '<svg><path id="woof" name="woof"></path></svg>',
1617
farmSet1: `
1718
<svg>
1819
<defs>
19-
<g id="pig"><path id="oink"></path></g>
20-
<g id="cow"><path id="moo"></path></g>
20+
<g id="pig" name="pig"><path name="oink"></path></g>
21+
<g id="cow" name="cow"><path name="moo"></path></g>
2122
</defs>
2223
</svg>
2324
`,
2425
farmSet2: `
2526
<svg>
2627
<defs>
27-
<g id="cow"><path id="moo moo"></path></g>
28-
<g id="sheep"><path id="baa"></path></g>
28+
<g id="cow" name="cow"><path name="moo moo"></path></g>
29+
<g id="sheep" name="sheep"><path name="baa"></path></g>
2930
</defs>
3031
</svg>
3132
`,
3233
farmSet3: `
3334
<svg>
34-
<symbol id="duck">
35-
<path id="quack"></path>
35+
<symbol id="duck" name="duck">
36+
<path id="quack" name="quack"></path>
3637
</symbol>
3738
</svg>
3839
`,
3940
arrows: `
4041
<svg>
4142
<defs>
42-
<svg id="left-arrow"><path id="left"></path></svg>
43-
<svg id="right-arrow"><path id="right"></path></svg>
43+
<svg id="left-arrow" name="left-arrow"><path name="left"></path></svg>
44+
<svg id="right-arrow" name="right-arrow"><path name="right"></path></svg>
4445
</defs>
4546
</svg> `
4647
};

src/lib/icon/icon-registry.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -365,23 +365,28 @@ export class MatIconRegistry {
365365
* returns it. Returns null if no matching element is found.
366366
*/
367367
private _extractSvgIconFromSet(iconSet: SVGElement, iconName: string): SVGElement | null {
368-
const iconNode = iconSet.querySelector('#' + iconName);
368+
const iconSource = iconSet.querySelector('#' + iconName);
369369

370-
if (!iconNode) {
370+
if (!iconSource) {
371371
return null;
372372
}
373373

374+
// Clone the element and remove the ID to prevent multiple elements from being added
375+
// to the page with the same ID.
376+
const iconElement = iconSource.cloneNode(true) as Element;
377+
iconElement.id = '';
378+
374379
// If the icon node is itself an <svg> node, clone and return it directly. If not, set it as
375380
// the content of a new <svg> node.
376-
if (iconNode.tagName.toLowerCase() === 'svg') {
377-
return this._setSvgAttributes(iconNode.cloneNode(true) as SVGElement);
381+
if (iconElement.nodeName.toLowerCase() === 'svg') {
382+
return this._setSvgAttributes(iconElement as SVGElement);
378383
}
379384

380385
// If the node is a <symbol>, it won't be rendered so we have to convert it into <svg>. Note
381386
// that the same could be achieved by referring to it via <use href="#id">, however the <use>
382387
// tag is problematic on Firefox, because it needs to include the current page path.
383-
if (iconNode.nodeName.toLowerCase() === 'symbol') {
384-
return this._setSvgAttributes(this._toSvgElement(iconNode));
388+
if (iconElement.nodeName.toLowerCase() === 'symbol') {
389+
return this._setSvgAttributes(this._toSvgElement(iconElement));
385390
}
386391

387392
// createElement('SVG') doesn't work as expected; the DOM ends up with
@@ -391,7 +396,7 @@ export class MatIconRegistry {
391396
// http://stackoverflow.com/questions/23003278/svg-innerhtml-in-firefox-can-not-display
392397
const svg = this._svgElementFromString('<svg></svg>');
393398
// Clone the node so we don't remove it from the parent icon set element.
394-
svg.appendChild(iconNode.cloneNode(true));
399+
svg.appendChild(iconElement);
395400

396401
return this._setSvgAttributes(svg);
397402
}
@@ -400,8 +405,6 @@ export class MatIconRegistry {
400405
* Creates a DOM element from the given SVG string.
401406
*/
402407
private _svgElementFromString(str: string): SVGElement {
403-
// TODO: Is there a better way than innerHTML? Renderer doesn't appear to have a method for
404-
// creating an element from an HTML string.
405408
const div = document.createElement('DIV');
406409
div.innerHTML = str;
407410
const svg = div.querySelector('svg') as SVGElement;

src/lib/icon/icon.spec.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ function sortedClassNames(element: Element): string[] {
1717
* Verifies that an element contains a single <svg> child element, and returns that child.
1818
*/
1919
function verifyAndGetSingleSvgChild(element: SVGElement): SVGElement {
20+
expect(element.id).toBeFalsy();
2021
expect(element.childNodes.length).toBe(1);
2122
const svgChild = element.childNodes[0] as SVGElement;
2223
expect(svgChild.tagName.toLowerCase()).toBe('svg');
@@ -31,7 +32,9 @@ function verifyPathChildElement(element: Element, attributeValue: string): void
3132
expect(element.childNodes.length).toBe(1);
3233
const pathElement = element.childNodes[0] as SVGPathElement;
3334
expect(pathElement.tagName.toLowerCase()).toBe('path');
34-
expect(pathElement.getAttribute('id')).toBe(attributeValue);
35+
36+
// The testing data SVGs have the name attribute set for verification.
37+
expect(pathElement.getAttribute('name')).toBe(attributeValue);
3538
}
3639

3740

@@ -190,7 +193,7 @@ describe('MatIcon', () => {
190193
svgChild = svgElement.childNodes[0];
191194
// The first <svg> child should be the <g id="pig"> element.
192195
expect(svgChild.tagName.toLowerCase()).toBe('g');
193-
expect(svgChild.getAttribute('id')).toBe('pig');
196+
expect(svgChild.getAttribute('name')).toBe('pig');
194197
verifyPathChildElement(svgChild, 'oink');
195198

196199
// Change the icon, and the SVG element should be replaced.
@@ -200,7 +203,7 @@ describe('MatIcon', () => {
200203
svgChild = svgElement.childNodes[0];
201204
// The first <svg> child should be the <g id="cow"> element.
202205
expect(svgChild.tagName.toLowerCase()).toBe('g');
203-
expect(svgChild.getAttribute('id')).toBe('cow');
206+
expect(svgChild.getAttribute('name')).toBe('cow');
204207
verifyPathChildElement(svgChild, 'moo');
205208
});
206209

@@ -224,7 +227,8 @@ describe('MatIcon', () => {
224227
svgChild = svgElement.childNodes[0];
225228
// The <svg> child should be the <g id="pig"> element.
226229
expect(svgChild.tagName.toLowerCase()).toBe('g');
227-
expect(svgChild.getAttribute('id')).toBe('pig');
230+
expect(svgChild.getAttribute('name')).toBe('pig');
231+
expect(svgChild.getAttribute('id')).toBe('');
228232
expect(svgChild.childNodes.length).toBe(1);
229233
verifyPathChildElement(svgChild, 'oink');
230234

@@ -237,7 +241,7 @@ describe('MatIcon', () => {
237241
svgChild = svgElement.childNodes[0];
238242
// The first <svg> child should be the <g id="cow"> element.
239243
expect(svgChild.tagName.toLowerCase()).toBe('g');
240-
expect(svgChild.getAttribute('id')).toBe('cow');
244+
expect(svgChild.getAttribute('name')).toBe('cow');
241245
expect(svgChild.childNodes.length).toBe(1);
242246
verifyPathChildElement(svgChild, 'moo moo');
243247
});
@@ -259,7 +263,7 @@ describe('MatIcon', () => {
259263
expect(svgElement.querySelector('symbol')).toBeFalsy();
260264
expect(svgElement.childNodes.length).toBe(1);
261265
expect(firstChild.nodeName.toLowerCase()).toBe('path');
262-
expect((firstChild as HTMLElement).getAttribute('id')).toBe('quack');
266+
expect((firstChild as HTMLElement).getAttribute('name')).toBe('quack');
263267
});
264268

265269
it('should not wrap <svg> elements in icon sets in another svg tag', () => {

0 commit comments

Comments
 (0)