fix(react-router): include search params in navigation change detection

This commit is contained in:
ShaneK
2026-03-10 12:37:57 -07:00
parent f9c0eb8a89
commit f98be5dfcc
5 changed files with 144 additions and 1 deletions

View File

@@ -177,7 +177,7 @@ export const IonRouter = ({ children, registerHistoryListener }: PropsWithChildr
}
const leavingUrl = leavingLocationInfo.pathname + leavingLocationInfo.search;
if (leavingUrl !== location.pathname) {
if (leavingUrl !== location.pathname + location.search) {
if (!incomingRouteParams.current) {
// Determine if the destination is a tab route by checking if it matches
// the pattern of tab routes (containing /tabs/ in the path)

View File

@@ -46,6 +46,7 @@ import Overlays from './pages/overlays/Overlays';
import NestedTabsRelativeLinks from './pages/nested-tabs-relative-links/NestedTabsRelativeLinks';
import RootSplatTabs from './pages/root-splat-tabs/RootSplatTabs';
import ContentChangeNavigation from './pages/content-change-navigation/ContentChangeNavigation';
import SearchParams from './pages/search-params/SearchParams';
setupIonicReact();
@@ -81,6 +82,7 @@ const App: React.FC = () => {
<Route path="/nested-tabs-relative-links/*" element={<NestedTabsRelativeLinks />} />
<Route path="/root-splat-tabs/*" element={<RootSplatTabs />} />
<Route path="/content-change-navigation/*" element={<ContentChangeNavigation />} />
<Route path="/search-params" element={<SearchParams />} />
</IonRouterOutlet>
</IonReactRouter>
</IonApp>

View File

@@ -83,6 +83,9 @@ const Main: React.FC = () => {
<IonItem routerLink="/content-change-navigation">
<IonLabel>Content Change Navigation</IonLabel>
</IonItem>
<IonItem routerLink="/search-params">
<IonLabel>Search Params</IonLabel>
</IonItem>
</IonList>
</IonContent>
</IonPage>

View File

@@ -0,0 +1,50 @@
import {
IonBackButton,
IonButton,
IonButtons,
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
} from '@ionic/react';
import React from 'react';
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
const SearchParams: React.FC = () => {
const navigate = useNavigate();
const location = useLocation();
const [searchParams] = useSearchParams();
const query = searchParams.get('q') || '';
return (
<IonPage data-pageid="search-params">
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonBackButton />
</IonButtons>
<IonTitle>Search Params</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent>
<div id="current-path">{location.pathname + location.search}</div>
<div id="query-value">{query}</div>
<IonButton id="add-search" onClick={() => navigate('/search-params?q=test')}>
Add Search Param
</IonButton>
<IonButton id="change-search" onClick={() => navigate('/search-params?q=changed')}>
Change Search Param
</IonButton>
<IonButton id="remove-search" onClick={() => navigate('/search-params')}>
Remove Search Param
</IonButton>
<IonButton id="go-home" routerLink="/">
Go Home
</IonButton>
</IonContent>
</IonPage>
);
};
export default SearchParams;

View File

@@ -0,0 +1,88 @@
describe('Search Params Navigation', () => {
beforeEach(() => {
cy.visit('/search-params');
// Wait for the page to be visible
cy.get('[data-pageid="search-params"]').should('be.visible');
});
it('should navigate when adding search params to the same path', () => {
// Verify we start without search params
cy.get('#current-path').should('have.text', '/search-params');
cy.get('#query-value').should('have.text', '');
// Click to add search param - this navigates from /search-params to /search-params?q=test
cy.get('#add-search').click();
// The URL should update with the search param
cy.location('pathname').should('eq', '/search-params');
cy.location('search').should('eq', '?q=test');
// The component should re-render with the new search param value
cy.get('#current-path').should('have.text', '/search-params?q=test');
cy.get('#query-value').should('have.text', 'test');
});
it('should navigate when changing search params on the same path', () => {
// First add a search param
cy.get('#add-search').click();
cy.get('#query-value').should('have.text', 'test');
// Now change the search param
cy.get('#change-search').click();
cy.location('search').should('eq', '?q=changed');
cy.get('#current-path').should('have.text', '/search-params?q=changed');
cy.get('#query-value').should('have.text', 'changed');
});
it('should navigate when removing search params from the same path', () => {
// First add a search param
cy.get('#add-search').click();
cy.get('#query-value').should('have.text', 'test');
// Now remove the search param
cy.get('#remove-search').click();
cy.location('search').should('eq', '');
cy.get('#current-path').should('have.text', '/search-params');
cy.get('#query-value').should('have.text', '');
});
it('should restore search params when navigating back from another page', () => {
// Add search param
cy.get('#add-search').click();
cy.get('#query-value').should('have.text', 'test');
cy.location('search').should('eq', '?q=test');
// Navigate to home page
cy.get('#go-home').click();
cy.get('[data-pageid="home"]').should('be.visible');
cy.location('pathname').should('eq', '/');
// Go back - should restore /search-params?q=test
cy.go('back');
cy.get('[data-pageid="search-params"]').should('be.visible');
cy.location('pathname').should('eq', '/search-params');
cy.location('search').should('eq', '?q=test');
cy.get('#query-value').should('have.text', 'test');
});
it('should support back through multiple search param changes', () => {
// Navigate: /search-params -> /search-params?q=test -> /search-params?q=changed
cy.get('#add-search').click();
cy.get('#query-value').should('have.text', 'test');
cy.get('#change-search').click();
cy.get('#query-value').should('have.text', 'changed');
// Go back to ?q=test
cy.go('back');
cy.location('search').should('eq', '?q=test');
cy.get('#query-value').should('have.text', 'test');
// Go back to no search params
cy.go('back');
cy.location('search').should('eq', '');
cy.get('#query-value').should('have.text', '');
});
});