Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion core/src/components/tabs/tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,27 @@ export class Tabs implements NavOutlet {
componentWillRender() {
const tabBar = this.el.querySelector('ion-tab-bar');
if (tabBar) {
const tab = this.selectedTab ? this.selectedTab.tab : undefined;
let tab = this.selectedTab ? this.selectedTab.tab : undefined;

// Fallback: if no selectedTab is set but we're using router mode,
// determine the active tab from the current URL. This works around
// timing issues in React Router integration where setRouteId may not
// be called in time for the initial render.
// TODO(FW-6724): Remove this with React Router upgrade
if (!tab && this.useRouter && typeof window !== 'undefined') {
const currentPath = window.location.pathname;
const tabButtons = this.el.querySelectorAll('ion-tab-button');

// Look for a tab button that matches the current path pattern
for (const tabButton of tabButtons) {
const tabId = tabButton.getAttribute('tab');
if (tabId && currentPath.includes(tabId)) {
tab = tabId;
break;
}
}
}

tabBar.selectedTab = tab;
}
}
Expand Down
2 changes: 2 additions & 0 deletions packages/react/test/base/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import Main from './pages/Main';
import Tabs from './pages/Tabs';
import TabsBasic from './pages/TabsBasic';
import NavComponent from './pages/navigation/NavComponent';
import TabsDirectNavigation from './pages/TabsDirectNavigation';
import IonModalConditional from './pages/overlay-components/IonModalConditional';
import IonModalConditionalSibling from './pages/overlay-components/IonModalConditionalSibling';
import IonModalDatetimeButton from './pages/overlay-components/IonModalDatetimeButton';
Expand Down Expand Up @@ -63,6 +64,7 @@ const App: React.FC = () => (
<Route path="/navigation" component={NavComponent} />
<Route path="/tabs" component={Tabs} />
<Route path="/tabs-basic" component={TabsBasic} />
<Route path="/tabs-direct-navigation" component={TabsDirectNavigation} />
<Route path="/icons" component={Icons} />
<Route path="/inputs" component={Inputs} />
</IonRouterOutlet>
Expand Down
94 changes: 94 additions & 0 deletions packages/react/test/base/src/pages/TabsDirectNavigation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { IonContent, IonHeader, IonIcon, IonLabel, IonPage, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs, IonTitle, IonToolbar } from '@ionic/react';
import { homeOutline, radioOutline, libraryOutline, searchOutline } from 'ionicons/icons';
import React from 'react';
import { Route, Redirect } from 'react-router-dom';

const HomePage: React.FC = () => (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Home</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent>
<div data-testid="home-content">Home Content</div>
</IonContent>
</IonPage>
);

const RadioPage: React.FC = () => (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Radio</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent>
<div data-testid="radio-content">Radio Content</div>
</IonContent>
</IonPage>
);

const LibraryPage: React.FC = () => (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Library</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent>
<div data-testid="library-content">Library Content</div>
</IonContent>
</IonPage>
);

const SearchPage: React.FC = () => (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Search</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent>
<div data-testid="search-content">Search Content</div>
</IonContent>
</IonPage>
);

const TabsDirectNavigation: React.FC = () => {
return (
<IonTabs data-testid="tabs-direct-navigation">
<IonRouterOutlet>
<Redirect exact path="/tabs-direct-navigation" to="/tabs-direct-navigation/home" />
<Route path="/tabs-direct-navigation/home" render={() => <HomePage />} exact={true} />
<Route path="/tabs-direct-navigation/radio" render={() => <RadioPage />} exact={true} />
<Route path="/tabs-direct-navigation/library" render={() => <LibraryPage />} exact={true} />
<Route path="/tabs-direct-navigation/search" render={() => <SearchPage />} exact={true} />
</IonRouterOutlet>

<IonTabBar slot="bottom" data-testid="tab-bar">
<IonTabButton tab="home" href="/tabs-direct-navigation/home" data-testid="home-tab">
<IonIcon icon={homeOutline}></IonIcon>
<IonLabel>Home</IonLabel>
</IonTabButton>

<IonTabButton tab="radio" href="/tabs-direct-navigation/radio" data-testid="radio-tab">
<IonIcon icon={radioOutline}></IonIcon>
<IonLabel>Radio</IonLabel>
</IonTabButton>

<IonTabButton tab="library" href="/tabs-direct-navigation/library" data-testid="library-tab">
<IonIcon icon={libraryOutline}></IonIcon>
<IonLabel>Library</IonLabel>
</IonTabButton>

<IonTabButton tab="search" href="/tabs-direct-navigation/search" data-testid="search-tab">
<IonIcon icon={searchOutline}></IonIcon>
<IonLabel>Search</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
);
};

export default TabsDirectNavigation;
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
describe('Tabs Direct Navigation', () => {
it('should select the correct tab when navigating directly to home route', () => {
cy.visit('/tabs-direct-navigation/home');
cy.get('[data-testid="home-tab"]').should('have.class', 'tab-selected');
cy.get('[data-testid="home-content"]').should('be.visible');
});

it('should select the correct tab when navigating directly to radio route', () => {
cy.visit('/tabs-direct-navigation/radio');
cy.get('[data-testid="radio-tab"]').should('have.class', 'tab-selected');
cy.get('[data-testid="radio-content"]').should('be.visible');
});

it('should select the correct tab when navigating directly to library route', () => {
cy.visit('/tabs-direct-navigation/library');
cy.get('[data-testid="library-tab"]').should('have.class', 'tab-selected');
cy.get('[data-testid="library-content"]').should('be.visible');
});

it('should select the correct tab when navigating directly to search route', () => {
cy.visit('/tabs-direct-navigation/search');
cy.get('[data-testid="search-tab"]').should('have.class', 'tab-selected');
cy.get('[data-testid="search-content"]').should('be.visible');
});

it('should update tab selection when navigating between tabs', () => {
cy.visit('/tabs-direct-navigation/home');
cy.get('[data-testid="home-tab"]').should('have.class', 'tab-selected');

cy.get('[data-testid="radio-tab"]').click();
cy.get('[data-testid="radio-tab"]').should('have.class', 'tab-selected');
cy.get('[data-testid="home-tab"]').should('not.have.class', 'tab-selected');
cy.get('[data-testid="radio-content"]').should('be.visible');
});
});
Loading