diff --git a/src/lib/icon/fake-svgs.ts b/src/lib/icon/fake-svgs.ts index eca39169006..6486efbe6e7 100644 --- a/src/lib/icon/fake-svgs.ts +++ b/src/lib/icon/fake-svgs.ts @@ -30,6 +30,14 @@ const FAKE_SVGS = (() => { `); + svgs.set('farm-set-3.svg', ` + + + + + + `); + svgs.set('arrow-set.svg', ` diff --git a/src/lib/icon/icon-registry.ts b/src/lib/icon/icon-registry.ts index 528f73a75c3..29e2c48be24 100644 --- a/src/lib/icon/icon-registry.ts +++ b/src/lib/icon/icon-registry.ts @@ -327,14 +327,24 @@ export class MdIconRegistry { */ private _extractSvgIconFromSet(iconSet: SVGElement, iconName: string): SVGElement { const iconNode = iconSet.querySelector('#' + iconName); + if (!iconNode) { return null; } + // If the icon node is itself an node, clone and return it directly. If not, set it as // the content of a new node. - if (iconNode.tagName.toLowerCase() == 'svg') { + if (iconNode.tagName.toLowerCase() === 'svg') { return this._setSvgAttributes(iconNode.cloneNode(true) as SVGElement); } + + // If the node is a , it won't be rendered so we have to convert it into . Note + // that the same could be achieved by referring to it via , however the + // tag is problematic on Firefox, because it needs to include the current page path. + if (iconNode.nodeName.toLowerCase() === 'symbol') { + return this._setSvgAttributes(this._toSvgElement(iconNode)); + } + // createElement('SVG') doesn't work as expected; the DOM ends up with // the correct nodes, but the SVG content doesn't render. Instead we // have to create an empty SVG node using innerHTML and append its content. @@ -343,6 +353,7 @@ export class MdIconRegistry { const svg = this._svgElementFromString(''); // Clone the node so we don't remove it from the parent icon set element. svg.appendChild(iconNode.cloneNode(true)); + return this._setSvgAttributes(svg); } @@ -361,6 +372,21 @@ export class MdIconRegistry { return svg; } + /** + * Converts an element into an SVG node by cloning all of its children. + */ + private _toSvgElement(element: Element): SVGElement { + let svg = this._svgElementFromString(''); + + for (let i = 0; i < element.childNodes.length; i++) { + if (element.childNodes[i].nodeType === Node.ELEMENT_NODE) { + svg.appendChild(element.childNodes[i].cloneNode(true)); + } + } + + return svg; + } + /** * Sets the default attributes for an SVG element to be used as an icon. */ diff --git a/src/lib/icon/icon.spec.ts b/src/lib/icon/icon.spec.ts index 41570c07470..a47d91a90ea 100644 --- a/src/lib/icon/icon.spec.ts +++ b/src/lib/icon/icon.spec.ts @@ -10,28 +10,31 @@ import {wrappedErrorMessage} from '../core/testing/wrapped-error-message'; /** Returns the CSS classes assigned to an element as a sorted array. */ -const sortedClassNames = (elem: Element) => elem.className.split(' ').sort(); +function sortedClassNames(element: Element): string[] { + return element.className.split(' ').sort(); +} /** * Verifies that an element contains a single child element, and returns that child. */ -const verifyAndGetSingleSvgChild = (element: SVGElement): any => { +function verifyAndGetSingleSvgChild(element: SVGElement): SVGElement { expect(element.childNodes.length).toBe(1); - const svgChild = element.childNodes[0]; + const svgChild = element.childNodes[0] as SVGElement; expect(svgChild.tagName.toLowerCase()).toBe('svg'); return svgChild; -}; +} /** * Verifies that an element contains a single child element whose "id" attribute has * the specified value. */ -const verifyPathChildElement = (element: Element, attributeValue: string) => { +function verifyPathChildElement(element: Element, attributeValue: string): void { expect(element.childNodes.length).toBe(1); - const pathElement = element.childNodes[0]; + const pathElement = element.childNodes[0] as SVGPathElement; expect(pathElement.tagName.toLowerCase()).toBe('path'); expect(pathElement.getAttribute('id')).toBe(attributeValue); -}; +} + describe('MdIcon', () => { @@ -240,6 +243,25 @@ describe('MdIcon', () => { expect(httpRequestUrls.sort()).toEqual(['farm-set-1.svg', 'farm-set-2.svg']); }); + it('should unwrap nodes', () => { + mdIconRegistry.addSvgIconSetInNamespace('farm', trust('farm-set-3.svg')); + + const fixture = TestBed.createComponent(MdIconFromSvgNameTestApp); + const testComponent = fixture.componentInstance; + const mdIconElement = fixture.debugElement.nativeElement.querySelector('md-icon'); + + testComponent.iconName = 'farm:duck'; + fixture.detectChanges(); + + const svgElement = verifyAndGetSingleSvgChild(mdIconElement); + const firstChild = svgElement.childNodes[0]; + + expect(svgElement.querySelector('symbol')).toBeFalsy(); + expect(svgElement.childNodes.length).toBe(1); + expect(firstChild.nodeName.toLowerCase()).toBe('path'); + expect((firstChild as HTMLElement).getAttribute('id')).toBe('quack'); + }); + it('should not wrap elements in icon sets in another svg tag', () => { mdIconRegistry.addSvgIconSet(trust('arrow-set.svg'));