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
This commit is contained in:
Matthieu Riegler
2023-01-26 14:23:57 +01:00
committed by Dylan Hunn
parent 6e0e94def4
commit 16ef770db8
2 changed files with 71 additions and 49 deletions
@@ -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) => {
+69 -47
View File
@@ -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: `<a [routerLink]="link"></a>`})
class LinkComponent {
link: string|null|undefined = '/';
}
let fixture: ComponentFixture<LinkComponent>;
let link: HTMLAnchorElement;
describe('on an anchor', () => {
describe('RouterLink for elements with `href` attributes', () => {
@Component({template: `<a [routerLink]="link"></a>`})
class LinkComponent {
link: string|null|undefined = '/';
}
let fixture: ComponentFixture<LinkComponent>;
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: `<svg><a routerLink="test"></a></svg>`})
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');
});
});
});