mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
fix(react-router): hide all views in outlet for container route transitions, allowing navigation to pages not properly wrapped in ion-pages
This commit is contained in:
@@ -232,8 +232,7 @@ export class StackManager extends React.PureComponent<StackManagerProps> {
|
||||
this.outOfScopeUnmountTimeout = undefined;
|
||||
}
|
||||
|
||||
const allViewsInOutlet = this.context.getViewItemsForOutlet ? this.context.getViewItemsForOutlet(this.id) : [];
|
||||
|
||||
const allViewsInOutlet = this.context.getViewItemsForOutlet(this.id);
|
||||
allViewsInOutlet.forEach((viewItem) => {
|
||||
hideIonPageElement(viewItem.ionPageElement);
|
||||
this.context.unMountViewItem(viewItem);
|
||||
@@ -501,8 +500,7 @@ export class StackManager extends React.PureComponent<StackManagerProps> {
|
||||
return;
|
||||
}
|
||||
|
||||
const allViewsInOutlet = this.context.getViewItemsForOutlet ? this.context.getViewItemsForOutlet(this.id) : [];
|
||||
|
||||
const allViewsInOutlet = this.context.getViewItemsForOutlet(this.id);
|
||||
const areSiblingRoutes = (path1: string, path2: string): boolean => {
|
||||
const path1IsRelative = !path1.startsWith('/');
|
||||
const path2IsRelative = !path2.startsWith('/');
|
||||
@@ -569,8 +567,15 @@ export class StackManager extends React.PureComponent<StackManagerProps> {
|
||||
}
|
||||
this.pendingPageTransition = false;
|
||||
|
||||
// Hide the leaving view immediately for Navigate redirects
|
||||
hideIonPageElement(leavingViewItem?.ionPageElement);
|
||||
// Hide ALL other visible views in this outlet for Navigate redirects.
|
||||
// Same rationale as the timeout path: intermediate redirects can shift
|
||||
// the leaving view reference, leaving the original page visible.
|
||||
const allViewsInOutlet = this.context.getViewItemsForOutlet(this.id);
|
||||
allViewsInOutlet.forEach((viewItem) => {
|
||||
if (viewItem.id !== enteringViewItem.id && viewItem.ionPageElement) {
|
||||
hideIonPageElement(viewItem.ionPageElement);
|
||||
}
|
||||
});
|
||||
|
||||
// Don't unmount if entering and leaving are the same view item
|
||||
if (shouldUnmountLeavingViewItem && leavingViewItem && enteringViewItem !== leavingViewItem) {
|
||||
@@ -617,11 +622,16 @@ export class StackManager extends React.PureComponent<StackManagerProps> {
|
||||
/**
|
||||
* Timeout fired and entering view still has no ionPageElement.
|
||||
* This happens for container routes that render nested outlets without a direct IonPage.
|
||||
* Hide the leaving view since there's no entering IonPage to wait for.
|
||||
* Hide ALL other visible views in this outlet, not just the computed leaving view.
|
||||
* This handles cases where intermediate redirects (e.g., Navigate in nested routes)
|
||||
* change the leaving view reference, leaving the original page still visible.
|
||||
*/
|
||||
if (latestLeavingView?.ionPageElement) {
|
||||
hideIonPageElement(latestLeavingView.ionPageElement);
|
||||
}
|
||||
const allViewsInOutlet = this.context.getViewItemsForOutlet(this.id);
|
||||
allViewsInOutlet.forEach((viewItem) => {
|
||||
if (viewItem.id !== latestEnteringView.id && viewItem.ionPageElement) {
|
||||
hideIonPageElement(viewItem.ionPageElement);
|
||||
}
|
||||
});
|
||||
this.forceUpdate();
|
||||
}
|
||||
}, ION_PAGE_WAIT_TIMEOUT_MS);
|
||||
@@ -688,7 +698,7 @@ export class StackManager extends React.PureComponent<StackManagerProps> {
|
||||
// the nested outlet's componentDidUpdate won't be called, so we must hide
|
||||
// the ion-page elements here to prevent them from remaining visible on top
|
||||
// of other content after navigation to a different route.
|
||||
const allViewsInOutlet = this.context.getViewItemsForOutlet ? this.context.getViewItemsForOutlet(this.id) : [];
|
||||
const allViewsInOutlet = this.context.getViewItemsForOutlet(this.id);
|
||||
allViewsInOutlet.forEach((viewItem) => {
|
||||
hideIonPageElement(viewItem.ionPageElement);
|
||||
});
|
||||
|
||||
@@ -100,6 +100,43 @@ describe('Nested Tabs with Relative Links', () => {
|
||||
cy.get('[data-testid="page-a-content"]').should('exist');
|
||||
});
|
||||
|
||||
/**
|
||||
* This test navigates from the home page (/) to nested-tabs-relative-links
|
||||
* via routerLink, rather than visiting the URL directly.
|
||||
*
|
||||
* This is a distinct scenario because NestedTabsRelativeLinks is a container
|
||||
* route that does NOT wrap its content in IonPage — it renders IonRouterOutlet
|
||||
* directly. When the root outlet transitions to a container without IonPage,
|
||||
* the nested <Navigate to="tab1" replace /> redirect fires before the root
|
||||
* outlet's timeout, causing the leaving view reference to shift. Without
|
||||
* proper handling, the home page is never hidden.
|
||||
*/
|
||||
it('should navigate from home page to nested tabs', () => {
|
||||
cy.visit(`http://localhost:${port}/`);
|
||||
cy.ionPageVisible('home');
|
||||
|
||||
// Click the Nested Tabs Relative Links item
|
||||
cy.get('ion-item[router-link="/nested-tabs-relative-links"]').click();
|
||||
|
||||
// Should navigate to tab1 (via index redirect)
|
||||
cy.ionPageVisible('nested-tabs-relative-tab1');
|
||||
cy.get('[data-testid="tab1-content"]').should('exist');
|
||||
|
||||
// Home page should be hidden
|
||||
cy.ionPageHidden('home');
|
||||
|
||||
// URL should be correct
|
||||
cy.url().should('include', '/nested-tabs-relative-links/tab1');
|
||||
|
||||
// Verify the container route does NOT have an IonPage wrapper.
|
||||
// This is the key invariant: NestedTabsRelativeLinks renders IonRouterOutlet
|
||||
// directly without IonPage. If someone adds IonPage to the container, this
|
||||
// test would pass trivially and no longer cover the container-without-IonPage
|
||||
// transition path. The ion-tabs element should have no [data-pageid] ancestors,
|
||||
// confirming no IonPage wraps the container.
|
||||
cy.get('ion-tabs').parents('[data-pageid]').should('have.length', 0);
|
||||
});
|
||||
|
||||
it('should switch tabs and maintain correct relative link resolution', () => {
|
||||
cy.visit(`http://localhost:${port}/nested-tabs-relative-links/tab1`);
|
||||
cy.ionPageVisible('nested-tabs-relative-tab1');
|
||||
|
||||
Reference in New Issue
Block a user