Skip to content

Commit 323445a

Browse files
crisbetojosephperrott
authored andcommitted
fix(icon): not taking current path after initialization
When resolving the current path while prefixing `url()` references, we use a provider to get the current path so we can account for server-side rendering and to be able to stub it out during tests. Since the provider is an object with a regular `pathname` property, the path will only be resolved once the first time an icon is resolved and then it'll be cached. This means that all subsequent icons will take an incorrect path. Related to #13628.
1 parent 8cfaeea commit 323445a

File tree

2 files changed

+42
-5
lines changed

2 files changed

+42
-5
lines changed

src/lib/icon/icon.spec.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,11 @@ function verifyPathChildElement(element: Element, attributeValue: string): void
3939

4040

4141
describe('MatIcon', () => {
42+
let fakePath: string;
4243

4344
beforeEach(async(() => {
45+
fakePath = '/fake-path';
46+
4447
TestBed.configureTestingModule({
4548
imports: [HttpClientTestingModule, MatIconModule],
4649
declarations: [
@@ -55,7 +58,7 @@ describe('MatIcon', () => {
5558
],
5659
providers: [{
5760
provide: MAT_ICON_LOCATION,
58-
useValue: {pathname: '/fake-path'}
61+
useValue: {getPathname: () => fakePath}
5962
}]
6063
});
6164

@@ -608,6 +611,36 @@ describe('MatIcon', () => {
608611
tick();
609612
}));
610613

614+
it('should use latest path when prefixing the `url()` references', fakeAsync(() => {
615+
iconRegistry.addSvgIconLiteral('fido', trustHtml(`
616+
<svg>
617+
<filter id="blur">
618+
<feGaussianBlur in="SourceGraphic" stdDeviation="5" />
619+
</filter>
620+
621+
<circle cx="170" cy="60" r="50" fill="green" filter="url('#blur')" />
622+
</svg>
623+
`));
624+
625+
let fixture = TestBed.createComponent(IconFromSvgName);
626+
fixture.componentInstance.iconName = 'fido';
627+
fixture.detectChanges();
628+
let circle = fixture.nativeElement.querySelector('mat-icon svg circle');
629+
630+
expect(circle.getAttribute('filter')).toMatch(/^url\(['"]?\/fake-path#blur['"]?\)$/);
631+
tick();
632+
fixture.destroy();
633+
634+
fakePath = '/another-fake-path';
635+
fixture = TestBed.createComponent(IconFromSvgName);
636+
fixture.componentInstance.iconName = 'fido';
637+
fixture.detectChanges();
638+
circle = fixture.nativeElement.querySelector('mat-icon svg circle');
639+
640+
expect(circle.getAttribute('filter')).toMatch(/^url\(['"]?\/another-fake-path#blur['"]?\)$/);
641+
tick();
642+
}));
643+
611644
});
612645

613646
describe('custom fonts', () => {

src/lib/icon/icon.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,18 @@ export const MAT_ICON_LOCATION = new InjectionToken<MatIconLocation>('mat-icon-l
5151
* @docs-private
5252
*/
5353
export interface MatIconLocation {
54-
pathname: string;
54+
getPathname: () => string;
5555
}
5656

5757
/** @docs-private */
5858
export function MAT_ICON_LOCATION_FACTORY(): MatIconLocation {
5959
const _document = inject(DOCUMENT);
60-
const pathname = (_document && _document.location && _document.location.pathname) || '';
61-
return {pathname};
60+
61+
return {
62+
// Note that this needs to be a function, rather than a property, because Angular
63+
// will only resolve it once, but we want the current path on each call.
64+
getPathname: () => (_document && _document.location && _document.location.pathname) || ''
65+
};
6266
}
6367

6468

@@ -324,7 +328,7 @@ export class MatIcon extends _MatIconMixinBase implements OnChanges, OnInit, Can
324328
}
325329

326330
const elementsWithFuncIri = element.querySelectorAll(funcIriAttributeSelector);
327-
const path = this._location.pathname ? this._location.pathname.split('#')[0] : '';
331+
const path = this._location.getPathname().split('#')[0];
328332

329333
for (let i = 0; i < elementsWithFuncIri.length; i++) {
330334
funcIriAttributes.forEach(attr => {

0 commit comments

Comments
 (0)