fix(react, vue): correct view now chosen when going back inside tabs (#23154)

resolves #23087
resolves #23101
This commit is contained in:
Liam DeBeasi
2021-04-06 10:16:23 -04:00
committed by GitHub
parent 42e6c90c46
commit 7203190234
7 changed files with 293 additions and 2 deletions

View File

@ -243,7 +243,19 @@ class IonRouterInner extends React.PureComponent<IonRouteProps, IonRouteState> {
routeDirection: 'back', routeDirection: 'back',
routeAnimation: routeAnimation || routeInfo.routeAnimation, routeAnimation: routeAnimation || routeInfo.routeAnimation,
}; };
if (routeInfo.lastPathname === routeInfo.pushedByRoute || prevInfo.pathname === routeInfo.pushedByRoute) { if (
routeInfo.lastPathname === routeInfo.pushedByRoute ||
(
/**
* We need to exclude tab switches/tab
* context changes here because tabbed
* navigation is not linear, but router.back()
* will go back in a linear fashion.
*/
prevInfo.pathname === routeInfo.pushedByRoute &&
routeInfo.tab === '' && prevInfo.tab === ''
)
) {
this.props.history.goBack(); this.props.history.goBack();
} else { } else {
this.handleNavigate(prevInfo.pathname + (prevInfo.search || ''), 'pop', 'back'); this.handleNavigate(prevInfo.pathname + (prevInfo.search || ''), 'pop', 'back');

View File

@ -0,0 +1,46 @@
const port = 3000;
describe('Tabs', () => {
// Verifies fix for https://github.com/ionic-team/ionic-framework/issues/23101
it('should return to previous tab instance when using the ion-back-button', () => {
cy.visit(`http://localhost:${port}/tabs/tab1`);
cy.get('#tabs-secondary').click();
cy.ionPageVisible('tab1-secondary');
cy.get('ion-tab-button#tab-button-tab2-secondary').click();
cy.ionPageHidden('tab1-secondary');
cy.ionPageVisible('tab2-secondary');
cy.get('ion-tab-button#tab-button-tab1-secondary').click();
cy.ionPageHidden('tab2-secondary');
cy.ionPageVisible('tab1-secondary');
cy.ionBackClick('tab1-secondary');
cy.ionPageDoesNotExist('tabs-secondary');
cy.ionPageVisible('tab1');
});
// Verifies fix for https://github.com/ionic-team/ionic-framework/issues/23087
it('should return to correct view and url when going back from child page after switching tabs', () => {
cy.visit(`http://localhost:${port}/tabs/tab1`);
cy.get('#child-one').click();
cy.ionPageHidden('tab1');
cy.ionPageVisible('tab1child1');
cy.get('ion-tab-button#tab-button-tab2').click();
cy.ionPageHidden('tab1child1');
cy.ionPageVisible('tab2');
cy.get('ion-tab-button#tab-button-tab1').click();
cy.ionPageHidden('tab2');
cy.ionPageVisible('tab1child1');
cy.ionBackClick('tab1child1');
cy.ionPageDoesNotExist('tab1child1');
cy.ionPageVisible('tab1');
cy.url().should('include', '/tabs/tab1');
});
});

View File

@ -34,6 +34,8 @@ import { OutletRef } from './pages/outlet-ref/OutletRef';
import { SwipeToGoBack } from './pages/swipe-to-go-back/SwipToGoBack'; import { SwipeToGoBack } from './pages/swipe-to-go-back/SwipToGoBack';
import Refs from './pages/refs/Refs'; import Refs from './pages/refs/Refs';
import DynamicIonpageClassnames from './pages/dynamic-ionpage-classnames/DynamicIonpageClassnames'; import DynamicIonpageClassnames from './pages/dynamic-ionpage-classnames/DynamicIonpageClassnames';
import Tabs from './pages/tabs/Tabs';
import TabsSecondary from './pages/tabs/TabsSecondary';
debugger; debugger;
const App: React.FC = () => { const App: React.FC = () => {
return ( return (
@ -51,6 +53,8 @@ const App: React.FC = () => {
<Route path="/outlet-ref" component={OutletRef} /> <Route path="/outlet-ref" component={OutletRef} />
<Route path="/swipe-to-go-back" component={SwipeToGoBack} /> <Route path="/swipe-to-go-back" component={SwipeToGoBack} />
<Route path="/dynamic-ionpage-classnames" component={DynamicIonpageClassnames} /> <Route path="/dynamic-ionpage-classnames" component={DynamicIonpageClassnames} />
<Route path="/tabs" component={Tabs} />
<Route path="/tabs-secondary" component={TabsSecondary} />
<Route path="/refs" component={Refs} /> <Route path="/refs" component={Refs} />
</IonReactRouter> </IonReactRouter>
</IonApp> </IonApp>

View File

@ -0,0 +1,95 @@
import React from 'react';
import {
IonTabs,
IonRouterOutlet,
IonTabBar,
IonTabButton,
IonIcon,
IonLabel,
IonPage,
IonHeader,
IonToolbar,
IonButtons,
IonBackButton,
IonTitle,
IonContent,
IonButton,
} from '@ionic/react';
import { Route, Redirect } from 'react-router';
import { triangle, square } from 'ionicons/icons';
interface Tabs {}
const Tabs: React.FC<Tabs> = () => {
return (
<IonTabs>
<IonRouterOutlet id="tabs">
<Route path="/tabs/tab1" component={Tab1} exact />
<Route path="/tabs/tab2" component={Tab2} exact />
<Route path="/tabs/tab1/child" component={Tab1Child1} exact />
<Redirect from="/tabs" to="/tabs/tab1" exact />
</IonRouterOutlet>
<IonTabBar slot="bottom">
<IonTabButton tab="tab1" href="/tabs/tab1">
<IonIcon icon={triangle} />
<IonLabel>Tab1</IonLabel>
</IonTabButton>
<IonTabButton tab="tab2" href="/tabs/tab2">
<IonIcon icon={square} />
<IonLabel>Tab2</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
);
};
const Tab1 = () => {
return (
<IonPage data-pageid="tab1">
<IonHeader>
<IonToolbar>
<IonTitle>Tab1</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent>
<IonButton routerLink="/tabs/tab1/child" id="child-one">Go to Tab1Child1</IonButton>
<IonButton routerLink="/tabs-secondary/tab1" id="tabs-secondary">Go to Secondary Tabs</IonButton>
</IonContent>
</IonPage>
);
};
const Tab1Child1 = () => {
return (
<IonPage data-pageid="tab1child1">
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonBackButton />
</IonButtons>
<IonTitle>Tab1</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent>
</IonContent>
</IonPage>
);
};
const Tab2 = () => {
return (
<IonPage data-pageid="tab2">
<IonHeader>
<IonToolbar>
<IonTitle>Tab2</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent>
Tab 2
</IonContent>
</IonPage>
);
};
export default Tabs;

View File

@ -0,0 +1,78 @@
import React from 'react';
import {
IonTabs,
IonRouterOutlet,
IonTabBar,
IonTabButton,
IonIcon,
IonLabel,
IonPage,
IonHeader,
IonToolbar,
IonButtons,
IonBackButton,
IonTitle,
IonContent,
IonButton,
} from '@ionic/react';
import { Route, Redirect } from 'react-router';
import { triangle, square } from 'ionicons/icons';
interface TabsSecondary {}
const TabsSecondary: React.FC<TabsSecondary> = () => {
return (
<IonTabs>
<IonRouterOutlet id="tabs-secondary">
<Route path="/tabs-secondary/tab1" component={Tab1} exact />
<Route path="/tabs-secondary/tab2" component={Tab2} exact />
<Redirect from="/tabs-secondary" to="/tabs-secondary/tab1" exact />
</IonRouterOutlet>
<IonTabBar slot="bottom">
<IonTabButton tab="tab1-secondary" href="/tabs-secondary/tab1">
<IonIcon icon={triangle} />
<IonLabel>Tab1</IonLabel>
</IonTabButton>
<IonTabButton tab="tab2-secondary" href="/tabs-secondary/tab2">
<IonIcon icon={square} />
<IonLabel>Tab2</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
);
};
const Tab1 = () => {
return (
<IonPage data-pageid="tab1-secondary">
<IonHeader>
<IonToolbar>
<IonTitle>Tab1</IonTitle>
<IonButtons slot="start">
<IonBackButton />
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent>
Tab 1
</IonContent>
</IonPage>
);
};
const Tab2 = () => {
return (
<IonPage data-pageid="tab2-secondary">
<IonHeader>
<IonToolbar>
<IonTitle>Tab2</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent>
Tab 2
</IonContent>
</IonPage>
);
};
export default TabsSecondary;

View File

@ -88,7 +88,19 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
const prevInfo = locationHistory.findLastLocation(routeInfo); const prevInfo = locationHistory.findLastLocation(routeInfo);
if (prevInfo) { if (prevInfo) {
incomingRouteParams = { ...prevInfo, routerAction: 'pop', routerDirection: 'back', routerAnimation: routerAnimation || routeInfo.routerAnimation }; incomingRouteParams = { ...prevInfo, routerAction: 'pop', routerDirection: 'back', routerAnimation: routerAnimation || routeInfo.routerAnimation };
if (routeInfo.lastPathname === routeInfo.pushedByRoute || prevInfo.pathname === routeInfo.pushedByRoute) { if (
routeInfo.lastPathname === routeInfo.pushedByRoute ||
(
/**
* We need to exclude tab switches/tab
* context changes here because tabbed
* navigation is not linear, but router.back()
* will go back in a linear fashion.
*/
prevInfo.pathname === routeInfo.pushedByRoute &&
routeInfo.tab === '' && prevInfo.tab === ''
)
) {
router.back(); router.back();
} else { } else {
router.replace({ path: prevInfo.pathname, query: parseQuery(prevInfo.search) }); router.replace({ path: prevInfo.pathname, query: parseQuery(prevInfo.search) });

View File

@ -220,6 +220,50 @@ describe('Tabs', () => {
cy.get('ion-tab-button#tab-button-tab1').should('not.have.class', 'tab-selected'); cy.get('ion-tab-button#tab-button-tab1').should('not.have.class', 'tab-selected');
}); });
// Verifies fix for https://github.com/ionic-team/ionic-framework/issues/23101
it('should return to previous tab instance when using the ion-back-button', () => {
cy.visit('http://localhost:8080/tabs/tab1');
cy.get('#tabs-secondary').click();
cy.ionPageHidden('tabs');
cy.ionPageVisible('tab1-secondary');
cy.get('ion-tab-button#tab-button-tab2-secondary').click();
cy.ionPageHidden('tab1-secondary');
cy.ionPageVisible('tab2-secondary');
cy.get('ion-tab-button#tab-button-tab1-secondary').click();
cy.ionPageHidden('tab2-secondary');
cy.ionPageVisible('tab1-secondary');
cy.ionBackClick('tab1-secondary');
cy.ionPageDoesNotExist('tabs-secondary');
cy.ionPageVisible('tab1');
});
// Verifies fix for https://github.com/ionic-team/ionic-framework/issues/23087
it('should return to correct view and url when going back from child page after switching tabs', () => {
cy.visit('http://localhost:8080/tabs/tab1');
cy.get('#child-one').click();
cy.ionPageHidden('tab1');
cy.ionPageVisible('tab1childone');
cy.get('ion-tab-button#tab-button-tab2').click();
cy.ionPageHidden('tab1childone');
cy.ionPageVisible('tab2');
cy.get('ion-tab-button#tab-button-tab1').click();
cy.ionPageHidden('tab2');
cy.ionPageVisible('tab1childone');
cy.ionBackClick('tab1childone');
cy.ionPageDoesNotExist('tab1childone');
cy.ionPageVisible('tab1');
cy.url().should('include', '/tabs/tab1');
});
}) })
describe('Tabs - Swipe to Go Back', () => { describe('Tabs - Swipe to Go Back', () => {