diff --git a/packages/angular/common/src/directives/navigation/router-link-delegate.ts b/packages/angular/common/src/directives/navigation/router-link-delegate.ts index d996d739428..4391d6ea0d1 100644 --- a/packages/angular/common/src/directives/navigation/router-link-delegate.ts +++ b/packages/angular/common/src/directives/navigation/router-link-delegate.ts @@ -31,12 +31,45 @@ export class RouterLinkDelegateDirective implements OnInit, OnChanges { ngOnInit(): void { this.updateTargetUrlAndHref(); + this.updateTabindex(); } ngOnChanges(): void { this.updateTargetUrlAndHref(); } + /** + * The `tabindex` is set to `0` by default on the host element when + * the `routerLink` directive is used. This causes issues with Ionic + * components that wrap an `a` or `button` element, such as `ion-item`. + * See issue https://github.com/angular/angular/issues/28345 + * + * This method removes the `tabindex` attribute from the host element + * to allow the Ionic component to manage the focus state correctly. + */ + private updateTabindex() { + // Ionic components that render a native anchor or button element + const ionicComponents = [ + 'ION-BACK-BUTTON', + 'ION-BREADCRUMB', + 'ION-BUTTON', + 'ION-CARD', + 'ION-FAB-BUTTON', + 'ION-ITEM', + 'ION-ITEM-OPTION', + 'ION-MENU-BUTTON', + 'ION-SEGMENT-BUTTON', + 'ION-TAB-BUTTON', + ]; + const hostElement = this.elementRef.nativeElement; + + if (ionicComponents.includes(hostElement.tagName)) { + if (hostElement.getAttribute('tabindex') === '0') { + hostElement.removeAttribute('tabindex'); + } + } + } + private updateTargetUrlAndHref() { if (this.routerLink?.urlTree) { const href = this.locationStrategy.prepareExternalUrl(this.router.serializeUrl(this.routerLink.urlTree)); diff --git a/packages/angular/test/base/e2e/src/lazy/router-link.spec.ts b/packages/angular/test/base/e2e/src/lazy/router-link.spec.ts index 5550d6326fc..d8513540de9 100644 --- a/packages/angular/test/base/e2e/src/lazy/router-link.spec.ts +++ b/packages/angular/test/base/e2e/src/lazy/router-link.spec.ts @@ -123,6 +123,21 @@ describe('Router Link', () => { testBack(); }); }); + + // Angular sets the `tabindex` to `"0"` on any element that uses + // the `routerLink` directive. Ionic removes the `tabindex` from + // components that wrap an `a` or `button` element, so we are + // checking here that it is only removed from Ionic components. + // https://github.com/ionic-team/ionic-framework/issues/20632 + describe('tabindex', () => { + it('should have tabindex="0" with a native span', () => { + cy.get('#span').should('have.attr', 'tabindex', '0'); + }); + + it('should not have tabindex set with an ionic button', () => { + cy.get('#routerLink').should('not.have.attr', 'tabindex'); + }); + }); }); function testForward() { diff --git a/packages/angular/test/base/e2e/src/standalone/router-link.spec.ts b/packages/angular/test/base/e2e/src/standalone/router-link.spec.ts index ce8724876a9..9550b4f1dc3 100644 --- a/packages/angular/test/base/e2e/src/standalone/router-link.spec.ts +++ b/packages/angular/test/base/e2e/src/standalone/router-link.spec.ts @@ -2,7 +2,7 @@ describe('RouterLink', () => { beforeEach(() => { cy.visit('/standalone/router-link'); }); - + it('should mount the root component', () => { cy.ionPageVisible('app-router-link'); @@ -21,4 +21,17 @@ describe('RouterLink', () => { cy.url().should('include', '/standalone/popover'); }); + + // Angular sets the `tabindex` to `"0"` on any element that uses + // the `routerLink` directive. Ionic removes the `tabindex` from + // components that wrap an `a` or `button` element, so we are + // checking here that it is only removed from Ionic components. + // https://github.com/ionic-team/ionic-framework/issues/20632 + it('should have tabindex="0" with a native span', () => { + cy.get('span').should('have.attr', 'tabindex', '0'); + }); + + it('should not have tabindex set with an ionic button', () => { + cy.get('ion-button').should('not.have.attr', 'tabindex'); + }); }); diff --git a/packages/angular/test/base/src/app/lazy/router-link/router-link.component.html b/packages/angular/test/base/src/app/lazy/router-link/router-link.component.html index 791b6a54424..6c9f1f00eb3 100644 --- a/packages/angular/test/base/src/app/lazy/router-link/router-link.component.html +++ b/packages/angular/test/base/src/app/lazy/router-link/router-link.component.html @@ -36,4 +36,5 @@
+span[routerLink]
diff --git a/packages/angular/test/base/src/app/standalone/router-link/router-link.component.html b/packages/angular/test/base/src/app/standalone/router-link/router-link.component.html index 078d4b7c3a4..8887626d1cb 100644 --- a/packages/angular/test/base/src/app/standalone/router-link/router-link.component.html +++ b/packages/angular/test/base/src/app/standalone/router-link/router-link.component.html @@ -4,3 +4,9 @@ + + I'm a span + +