From 16ef770db803ce4037a90c72477da412642dfb33 Mon Sep 17 00:00:00 2001 From: Matthieu Riegler Date: Thu, 26 Jan 2023 14:23:57 +0100 Subject: [PATCH] fix(router): Handle routerLink directive on svg anchors. (#48857) On svgs, the tagNames are lowercase even for non-svg related tags like `a`. fixes #48854 PR Close #48857 --- packages/router/src/directives/router_link.ts | 4 +- packages/router/test/router_link_spec.ts | 116 +++++++++++------- 2 files changed, 71 insertions(+), 49 deletions(-) diff --git a/packages/router/src/directives/router_link.ts b/packages/router/src/directives/router_link.ts index 5fb80cb66af..6f6740ab872 100644 --- a/packages/router/src/directives/router_link.ts +++ b/packages/router/src/directives/router_link.ts @@ -190,8 +190,8 @@ export class RouterLink implements OnChanges, OnDestroy { @Attribute('tabindex') private readonly tabIndexAttribute: string|null|undefined, private readonly renderer: Renderer2, private readonly el: ElementRef, private locationStrategy?: LocationStrategy) { - const tagName = el.nativeElement.tagName; - this.isAnchorElement = tagName === 'A' || tagName === 'AREA'; + const tagName = el.nativeElement.tagName?.toLowerCase(); + this.isAnchorElement = tagName === 'a' || tagName === 'area'; if (this.isAnchorElement) { this.subscription = router.events.subscribe((s: Event) => { diff --git a/packages/router/test/router_link_spec.ts b/packages/router/test/router_link_spec.ts index f529e0ae657..a14e2d6011a 100644 --- a/packages/router/test/router_link_spec.ts +++ b/packages/router/test/router_link_spec.ts @@ -39,8 +39,10 @@ describe('RouterLink', () => { let router: Router; beforeEach(() => { - TestBed.configureTestingModule( - {imports: [RouterTestingModule], declarations: [LinkComponent]}); + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + declarations: [LinkComponent], + }); fixture = TestBed.createComponent(LinkComponent); fixture.detectChanges(); link = fixture.debugElement.query(By.css('div')).nativeElement; @@ -93,56 +95,76 @@ describe('RouterLink', () => { }); }); - describe('RouterLink for elements with `href` attributes', () => { - @Component({template: ``}) - class LinkComponent { - link: string|null|undefined = '/'; - } - let fixture: ComponentFixture; - let link: HTMLAnchorElement; + describe('on an anchor', () => { + describe('RouterLink for elements with `href` attributes', () => { + @Component({template: ``}) + class LinkComponent { + link: string|null|undefined = '/'; + } + let fixture: ComponentFixture; + let link: HTMLAnchorElement; - beforeEach(() => { - TestBed.configureTestingModule( - {imports: [RouterTestingModule], declarations: [LinkComponent]}); - fixture = TestBed.createComponent(LinkComponent); - fixture.detectChanges(); - link = fixture.debugElement.query(By.css('a')).nativeElement; + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + declarations: [LinkComponent], + }); + fixture = TestBed.createComponent(LinkComponent); + fixture.detectChanges(); + link = fixture.debugElement.query(By.css('a')).nativeElement; + }); + + it('null, removes href', () => { + expect(link.outerHTML).toContain('href'); + fixture.componentInstance.link = null; + fixture.detectChanges(); + expect(link.outerHTML).not.toContain('href'); + }); + + it('undefined, removes href', () => { + expect(link.outerHTML).toContain('href'); + fixture.componentInstance.link = undefined; + fixture.detectChanges(); + expect(link.outerHTML).not.toContain('href'); + }); + + it('should coerce boolean input values', () => { + const dir = fixture.debugElement.query(By.directive(RouterLink)).injector.get(RouterLink); + + for (const truthy of [true, '', 'true', 'anything']) { + dir.preserveFragment = truthy; + dir.skipLocationChange = truthy; + dir.replaceUrl = truthy; + expect(dir.preserveFragment).toBeTrue(); + expect(dir.skipLocationChange).toBeTrue(); + expect(dir.replaceUrl).toBeTrue(); + } + + for (const falsy of [false, null, undefined, 'false']) { + dir.preserveFragment = falsy; + dir.skipLocationChange = falsy; + dir.replaceUrl = falsy; + expect(dir.preserveFragment).toBeFalse(); + expect(dir.skipLocationChange).toBeFalse(); + expect(dir.replaceUrl).toBeFalse(); + } + }); }); - it('null, removes href', () => { - expect(link.outerHTML).toContain('href'); - fixture.componentInstance.link = null; - fixture.detectChanges(); - expect(link.outerHTML).not.toContain('href'); - }); - - it('undefined, removes href', () => { - expect(link.outerHTML).toContain('href'); - fixture.componentInstance.link = undefined; - fixture.detectChanges(); - expect(link.outerHTML).not.toContain('href'); - }); - - it('should coerce boolean input values', () => { - const dir = fixture.debugElement.query(By.directive(RouterLink)).injector.get(RouterLink); - - for (const truthy of [true, '', 'true', 'anything']) { - dir.preserveFragment = truthy; - dir.skipLocationChange = truthy; - dir.replaceUrl = truthy; - expect(dir.preserveFragment).toBeTrue(); - expect(dir.skipLocationChange).toBeTrue(); - expect(dir.replaceUrl).toBeTrue(); + it('should handle routerLink in svg templates', () => { + @Component({template: ``}) + class LinkComponent { } - for (const falsy of [false, null, undefined, 'false']) { - dir.preserveFragment = falsy; - dir.skipLocationChange = falsy; - dir.replaceUrl = falsy; - expect(dir.preserveFragment).toBeFalse(); - expect(dir.skipLocationChange).toBeFalse(); - expect(dir.replaceUrl).toBeFalse(); - } + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + declarations: [LinkComponent], + }); + const fixture = TestBed.createComponent(LinkComponent); + fixture.detectChanges(); + const link = fixture.debugElement.query(By.css('a')).nativeElement; + + expect(link.outerHTML).toContain('href'); }); }); });