Skip to content

Commit 048a858

Browse files
committed
fix(react): fixing issue where modifier keys were blocked while clicking links
1 parent c3b9598 commit 048a858

5 files changed

Lines changed: 106 additions & 0 deletions

File tree

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ import { Step1, Step2, Step3, Step4 } from './pages/replace-params/ReplaceParams
6464
import { ParamSwipeBack, ParamSwipeBackB } from './pages/param-swipe-back/ParamSwipeBack';
6565
import TabLifecycle from './pages/tab-lifecycle/TabLifecycle';
6666
import TabLifecycleOutside from './pages/tab-lifecycle/TabLifecycleOutside';
67+
import { RouterLinkModifierClick, RouterLinkModifierClickTarget } from './pages/router-link-modifier-click/RouterLinkModifierClick';
6768

6869
setupIonicReact();
6970

@@ -122,6 +123,8 @@ const App: React.FC = () => {
122123
<Route path="/replace-params" element={<Navigate to="/replace-params/step1" replace />} />
123124
<Route path="/tab-lifecycle/*" element={<TabLifecycle />} />
124125
<Route path="/tab-lifecycle-outside" element={<TabLifecycleOutside />} />
126+
<Route path="/router-link-modifier-click" element={<RouterLinkModifierClick />} />
127+
<Route path="/router-link-modifier-click/target" element={<RouterLinkModifierClickTarget />} />
125128
</IonRouterOutlet>
126129
</IonReactRouter>
127130
</IonApp>

packages/react-router/test/base/src/pages/Main.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ const Main: React.FC = () => {
134134
<IonItem routerLink="/tab-lifecycle">
135135
<IonLabel>Tab Lifecycle</IonLabel>
136136
</IonItem>
137+
<IonItem routerLink="/router-link-modifier-click">
138+
<IonLabel>Router Link Modifier Click</IonLabel>
139+
</IonItem>
137140
</IonList>
138141
</IonContent>
139142
</IonPage>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import {
2+
IonContent,
3+
IonHeader,
4+
IonPage,
5+
IonTitle,
6+
IonToolbar,
7+
IonItem,
8+
IonLabel,
9+
IonButtons,
10+
IonBackButton,
11+
} from '@ionic/react';
12+
import React from 'react';
13+
14+
/**
15+
* Test for issue #26394: routerLink should allow modifier key clicks (ctrl/cmd/shift)
16+
* to open links in a new tab without triggering SPA navigation on the current page.
17+
*/
18+
19+
export const RouterLinkModifierClick: React.FC = () => {
20+
return (
21+
<IonPage data-pageid="router-link-modifier-click">
22+
<IonHeader>
23+
<IonToolbar>
24+
<IonTitle>Router Link Modifier Click</IonTitle>
25+
</IonToolbar>
26+
</IonHeader>
27+
<IonContent>
28+
<IonItem id="nav-to-target" routerLink="/router-link-modifier-click/target">
29+
<IonLabel>Navigate to Target</IonLabel>
30+
</IonItem>
31+
</IonContent>
32+
</IonPage>
33+
);
34+
};
35+
36+
export const RouterLinkModifierClickTarget: React.FC = () => {
37+
return (
38+
<IonPage data-pageid="router-link-modifier-click-target">
39+
<IonHeader>
40+
<IonToolbar>
41+
<IonButtons slot="start">
42+
<IonBackButton defaultHref="/router-link-modifier-click" />
43+
</IonButtons>
44+
<IonTitle>Target Page</IonTitle>
45+
</IonToolbar>
46+
</IonHeader>
47+
<IonContent>
48+
<p>Target Page</p>
49+
</IonContent>
50+
</IonPage>
51+
);
52+
};
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { test } from '@playwright/test';
2+
import { ionPageVisible, ionPageDoesNotExist, withTestingMode } from './utils/test-utils';
3+
4+
/**
5+
* Tests for issue #26394:
6+
* routerLink on IonItem (and other routing components) should allow modifier key
7+
* clicks (ctrl/cmd/shift+click) to open in a new tab without triggering SPA
8+
* navigation on the current page.
9+
*/
10+
test.describe('Router Link Modifier Click', () => {
11+
test('normal click should trigger SPA navigation', async ({ page }) => {
12+
await page.goto(withTestingMode('/router-link-modifier-click'));
13+
await ionPageVisible(page, 'router-link-modifier-click');
14+
15+
await page.locator('#nav-to-target').click();
16+
17+
await ionPageVisible(page, 'router-link-modifier-click-target');
18+
});
19+
20+
test('meta+click should not trigger SPA navigation on the current page', async ({ page }) => {
21+
await page.goto(withTestingMode('/router-link-modifier-click'));
22+
await ionPageVisible(page, 'router-link-modifier-click');
23+
24+
await page.locator('#nav-to-target').click({ modifiers: ['Meta'] });
25+
await page.waitForTimeout(500);
26+
27+
// The current page should NOT have navigated to the target via SPA
28+
await ionPageVisible(page, 'router-link-modifier-click');
29+
await ionPageDoesNotExist(page, 'router-link-modifier-click-target');
30+
});
31+
32+
test('shift+click should not trigger SPA navigation on the current page', async ({ page }) => {
33+
await page.goto(withTestingMode('/router-link-modifier-click'));
34+
await ionPageVisible(page, 'router-link-modifier-click');
35+
36+
await page.locator('#nav-to-target').click({ modifiers: ['Shift'] });
37+
await page.waitForTimeout(500);
38+
39+
// The current page should NOT have navigated to the target via SPA
40+
await ionPageVisible(page, 'router-link-modifier-click');
41+
await ionPageDoesNotExist(page, 'router-link-modifier-click-target');
42+
});
43+
});

packages/react/src/components/createRoutingComponent.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ export const createRoutingComponent = <PropType, ElementType>(tagName: string, c
5656
private handleClick = (e: React.MouseEvent<PropType>) => {
5757
const { routerLink, routerDirection, routerOptions, routerAnimation } = this.props;
5858
if (routerLink !== undefined) {
59+
// Allow modifier key clicks (ctrl/cmd/shift) to open the link in a new tab/window
60+
// without triggering SPA navigation on the current page.
61+
if (e.metaKey || e.ctrlKey || e.shiftKey) {
62+
return;
63+
}
5964
e.preventDefault();
6065
this.context.navigate(routerLink, routerDirection, undefined, routerAnimation, routerOptions);
6166
}

0 commit comments

Comments
 (0)