Skip to content

Commit 7791f8b

Browse files
committed
fix(ion-tab): adding fallback to select tab if router integration fails for some reason
1 parent a00a389 commit 7791f8b

File tree

4 files changed

+152
-1
lines changed

4 files changed

+152
-1
lines changed

core/src/components/tabs/tabs.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,27 @@ export class Tabs implements NavOutlet {
6868
componentWillRender() {
6969
const tabBar = this.el.querySelector('ion-tab-bar');
7070
if (tabBar) {
71-
const tab = this.selectedTab ? this.selectedTab.tab : undefined;
71+
let tab = this.selectedTab ? this.selectedTab.tab : undefined;
72+
73+
// Fallback: if no selectedTab is set but we're using router mode,
74+
// determine the active tab from the current URL. This works around
75+
// timing issues in React Router integration where setRouteId may not
76+
// be called in time for the initial render.
77+
// TODO(FW-6724): Remove this with React Router upgrade
78+
if (!tab && this.useRouter && typeof window !== 'undefined') {
79+
const currentPath = window.location.pathname;
80+
const tabButtons = this.el.querySelectorAll('ion-tab-button');
81+
82+
// Look for a tab button that matches the current path pattern
83+
for (const tabButton of tabButtons) {
84+
const tabId = tabButton.getAttribute('tab');
85+
if (tabId && currentPath.includes(tabId)) {
86+
tab = tabId;
87+
break;
88+
}
89+
}
90+
}
91+
7292
tabBar.selectedTab = tab;
7393
}
7494
}

packages/react/test/base/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import Main from './pages/Main';
2727
import Tabs from './pages/Tabs';
2828
import TabsBasic from './pages/TabsBasic';
2929
import NavComponent from './pages/navigation/NavComponent';
30+
import TabsDirectNavigation from './pages/TabsDirectNavigation';
3031
import IonModalConditional from './pages/overlay-components/IonModalConditional';
3132
import IonModalConditionalSibling from './pages/overlay-components/IonModalConditionalSibling';
3233
import IonModalDatetimeButton from './pages/overlay-components/IonModalDatetimeButton';
@@ -63,6 +64,7 @@ const App: React.FC = () => (
6364
<Route path="/navigation" component={NavComponent} />
6465
<Route path="/tabs" component={Tabs} />
6566
<Route path="/tabs-basic" component={TabsBasic} />
67+
<Route path="/tabs-direct-navigation" component={TabsDirectNavigation} />
6668
<Route path="/icons" component={Icons} />
6769
<Route path="/inputs" component={Inputs} />
6870
</IonRouterOutlet>
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { IonContent, IonHeader, IonIcon, IonLabel, IonPage, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs, IonTitle, IonToolbar } from '@ionic/react';
2+
import { homeOutline, radioOutline, libraryOutline, searchOutline } from 'ionicons/icons';
3+
import React from 'react';
4+
import { Route, Redirect } from 'react-router-dom';
5+
6+
const HomePage: React.FC = () => (
7+
<IonPage>
8+
<IonHeader>
9+
<IonToolbar>
10+
<IonTitle>Home</IonTitle>
11+
</IonToolbar>
12+
</IonHeader>
13+
<IonContent>
14+
<div data-testid="home-content">Home Content</div>
15+
</IonContent>
16+
</IonPage>
17+
);
18+
19+
const RadioPage: React.FC = () => (
20+
<IonPage>
21+
<IonHeader>
22+
<IonToolbar>
23+
<IonTitle>Radio</IonTitle>
24+
</IonToolbar>
25+
</IonHeader>
26+
<IonContent>
27+
<div data-testid="radio-content">Radio Content</div>
28+
</IonContent>
29+
</IonPage>
30+
);
31+
32+
const LibraryPage: React.FC = () => (
33+
<IonPage>
34+
<IonHeader>
35+
<IonToolbar>
36+
<IonTitle>Library</IonTitle>
37+
</IonToolbar>
38+
</IonHeader>
39+
<IonContent>
40+
<div data-testid="library-content">Library Content</div>
41+
</IonContent>
42+
</IonPage>
43+
);
44+
45+
const SearchPage: React.FC = () => (
46+
<IonPage>
47+
<IonHeader>
48+
<IonToolbar>
49+
<IonTitle>Search</IonTitle>
50+
</IonToolbar>
51+
</IonHeader>
52+
<IonContent>
53+
<div data-testid="search-content">Search Content</div>
54+
</IonContent>
55+
</IonPage>
56+
);
57+
58+
const TabsDirectNavigation: React.FC = () => {
59+
return (
60+
<IonTabs data-testid="tabs-direct-navigation">
61+
<IonRouterOutlet>
62+
<Redirect exact path="/tabs-direct-navigation" to="/tabs-direct-navigation/home" />
63+
<Route path="/tabs-direct-navigation/home" render={() => <HomePage />} exact={true} />
64+
<Route path="/tabs-direct-navigation/radio" render={() => <RadioPage />} exact={true} />
65+
<Route path="/tabs-direct-navigation/library" render={() => <LibraryPage />} exact={true} />
66+
<Route path="/tabs-direct-navigation/search" render={() => <SearchPage />} exact={true} />
67+
</IonRouterOutlet>
68+
69+
<IonTabBar slot="bottom" data-testid="tab-bar">
70+
<IonTabButton tab="home" href="/tabs-direct-navigation/home" data-testid="home-tab">
71+
<IonIcon icon={homeOutline}></IonIcon>
72+
<IonLabel>Home</IonLabel>
73+
</IonTabButton>
74+
75+
<IonTabButton tab="radio" href="/tabs-direct-navigation/radio" data-testid="radio-tab">
76+
<IonIcon icon={radioOutline}></IonIcon>
77+
<IonLabel>Radio</IonLabel>
78+
</IonTabButton>
79+
80+
<IonTabButton tab="library" href="/tabs-direct-navigation/library" data-testid="library-tab">
81+
<IonIcon icon={libraryOutline}></IonIcon>
82+
<IonLabel>Library</IonLabel>
83+
</IonTabButton>
84+
85+
<IonTabButton tab="search" href="/tabs-direct-navigation/search" data-testid="search-tab">
86+
<IonIcon icon={searchOutline}></IonIcon>
87+
<IonLabel>Search</IonLabel>
88+
</IonTabButton>
89+
</IonTabBar>
90+
</IonTabs>
91+
);
92+
};
93+
94+
export default TabsDirectNavigation;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
describe('Tabs Direct Navigation', () => {
2+
it('should select the correct tab when navigating directly to home route', () => {
3+
cy.visit('/tabs-direct-navigation/home');
4+
cy.get('[data-testid="home-tab"]').should('have.class', 'tab-selected');
5+
cy.get('[data-testid="home-content"]').should('be.visible');
6+
});
7+
8+
it('should select the correct tab when navigating directly to radio route', () => {
9+
cy.visit('/tabs-direct-navigation/radio');
10+
cy.get('[data-testid="radio-tab"]').should('have.class', 'tab-selected');
11+
cy.get('[data-testid="radio-content"]').should('be.visible');
12+
});
13+
14+
it('should select the correct tab when navigating directly to library route', () => {
15+
cy.visit('/tabs-direct-navigation/library');
16+
cy.get('[data-testid="library-tab"]').should('have.class', 'tab-selected');
17+
cy.get('[data-testid="library-content"]').should('be.visible');
18+
});
19+
20+
it('should select the correct tab when navigating directly to search route', () => {
21+
cy.visit('/tabs-direct-navigation/search');
22+
cy.get('[data-testid="search-tab"]').should('have.class', 'tab-selected');
23+
cy.get('[data-testid="search-content"]').should('be.visible');
24+
});
25+
26+
it('should update tab selection when navigating between tabs', () => {
27+
cy.visit('/tabs-direct-navigation/home');
28+
cy.get('[data-testid="home-tab"]').should('have.class', 'tab-selected');
29+
30+
cy.get('[data-testid="radio-tab"]').click();
31+
cy.get('[data-testid="radio-tab"]').should('have.class', 'tab-selected');
32+
cy.get('[data-testid="home-tab"]').should('not.have.class', 'tab-selected');
33+
cy.get('[data-testid="radio-content"]').should('be.visible');
34+
});
35+
});

0 commit comments

Comments
 (0)