Skip to content

Commit 2318292

Browse files
authored
Add support for routerOptions and useHref (#5864)
* Add support for routerOptions and useHref
1 parent 0e61098 commit 2318292

File tree

31 files changed

+462
-91
lines changed

31 files changed

+462
-91
lines changed

examples/next-app/app/page.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
"use client";
22

33
import {Provider, defaultTheme, DatePicker} from '@adobe/react-spectrum';
4+
import {useRouter} from 'next/navigation';
5+
6+
declare module '@adobe/react-spectrum' {
7+
interface RouterConfig {
8+
routerOptions: NonNullable<Parameters<ReturnType<typeof useRouter>['push']>[1]>
9+
}
10+
}
411

512
export default function Home() {
13+
let router = useRouter();
614
return (
7-
<Provider theme={defaultTheme} locale="en">
15+
<Provider theme={defaultTheme} locale="en" router={{navigate: router.push}}>
816
<DatePicker label="Date" />
917
</Provider>
1018
)

examples/remix/app/root.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,20 @@ import {
55
Outlet,
66
Scripts,
77
ScrollRestoration,
8+
useNavigate,
9+
useHref
810
} from '@remix-run/react';
11+
import type {NavigateOptions} from 'react-router-dom';
912
import {Provider, defaultTheme} from '@adobe/react-spectrum';
1013

14+
declare module '@adobe/react-spectrum' {
15+
interface RouterConfig {
16+
routerOptions: NavigateOptions
17+
}
18+
}
19+
1120
export default function App() {
21+
let navigate = useNavigate();
1222
return (
1323
<html lang="en">
1424
<head>
@@ -18,7 +28,13 @@ export default function App() {
1828
<Links />
1929
</head>
2030
<body>
21-
<Provider theme={defaultTheme} locale="en">
31+
<Provider
32+
theme={defaultTheme}
33+
locale="en"
34+
router={{
35+
navigate,
36+
useHref
37+
}}>
2238
<Outlet />
2339
</Provider>
2440
<ScrollRestoration />

examples/remix/app/routes/_index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { MetaFunction } from "@remix-run/node";
2-
import {DatePicker} from '@adobe/react-spectrum';
2+
import {ActionMenu, DatePicker, Item} from '@adobe/react-spectrum';
33

44
export const meta: MetaFunction = () => {
55
return [
@@ -13,6 +13,9 @@ export default function Index() {
1313
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}>
1414
<h1>Welcome to Remix</h1>
1515
<DatePicker label="Date" />
16+
<ActionMenu>
17+
<Item href="/foo" routerOptions={{replace: true}}>Link to foo</Item>
18+
</ActionMenu>
1619
</div>
1720
);
1821
}

examples/remix/app/routes/foo.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Foo() {
2+
return <h1>Foo</h1>
3+
}

examples/rsp-next-ts/pages/_app.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,13 @@ import Moon from "@spectrum-icons/workflow/Moon";
1414
import Light from "@spectrum-icons/workflow/Light";
1515
import { ToastContainer } from "@react-spectrum/toast";
1616
import {enableTableNestedRows} from '@react-stately/flags';
17-
import {useRouter} from 'next/router';
17+
import {useRouter, type NextRouter} from 'next/router';
18+
19+
declare module '@adobe/react-spectrum' {
20+
interface RouterConfig {
21+
routerOptions: NonNullable<Parameters<NextRouter['push']>[2]>
22+
}
23+
}
1824

1925
function MyApp({ Component, pageProps }: AppProps) {
2026
const [theme, setTheme] = useState<ColorScheme>("light");
@@ -25,7 +31,14 @@ function MyApp({ Component, pageProps }: AppProps) {
2531
enableTableNestedRows();
2632

2733
return (
28-
<Provider theme={lightTheme} colorScheme={theme} router={{navigate: router.push}} locale="en">
34+
<Provider
35+
theme={lightTheme}
36+
colorScheme={theme}
37+
router={{
38+
navigate: (href, opts) => router.push(href, undefined, opts),
39+
useHref: (href: string) => router.basePath + href
40+
}}
41+
locale="en">
2942
<Grid
3043
areas={["header", "content"]}
3144
columns={["1fr"]}

examples/rsp-next-ts/pages/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ export default function Home() {
183183
<MenuTrigger>
184184
<ActionButton>Menu Trigger</ActionButton>
185185
<Menu>
186-
<Item href="/foo">Link to /foo</Item>
186+
<Item href="/foo" routerOptions={{scroll: false}}>Link to /foo</Item>
187187
<Item>Cut</Item>
188188
<Item>Copy</Item>
189189
<Item>Paste</Item>

packages/@adobe/react-spectrum/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,4 @@ export type {VisuallyHiddenAria, VisuallyHiddenProps} from '@react-aria/visually
115115
export type {DateFormatter, DateFormatterOptions, Filter, FormatMessage, Locale, LocalizedStrings} from '@react-aria/i18n';
116116
export type {SSRProviderProps} from '@react-aria/ssr';
117117
export type {DirectoryDropItem, DragAndDropHooks, DragAndDropOptions, DraggableCollectionEndEvent, DraggableCollectionMoveEvent, DraggableCollectionStartEvent, DragPreviewRenderer, DragTypes, DropItem, DropOperation, DroppableCollectionDropEvent, DroppableCollectionEnterEvent, DroppableCollectionExitEvent, DroppableCollectionInsertDropEvent, DroppableCollectionMoveEvent, DroppableCollectionOnItemDropEvent, DroppableCollectionReorderEvent, DroppableCollectionRootDropEvent, DropPosition, DropTarget, FileDropItem, ItemDropTarget, RootDropTarget, TextDropItem} from '@react-spectrum/dnd';
118-
export type {Key, Selection, ItemProps, SectionProps} from '@react-types/shared';
118+
export type {Key, Selection, ItemProps, SectionProps, RouterConfig} from '@react-types/shared';

packages/@react-aria/combobox/src/useComboBox.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {AriaButtonProps} from '@react-types/button';
1515
import {AriaComboBoxProps} from '@react-types/combobox';
1616
import {ariaHideOutside} from '@react-aria/overlays';
1717
import {AriaListBoxOptions, getItemId, listData} from '@react-aria/listbox';
18-
import {BaseEvent, DOMAttributes, KeyboardDelegate, PressEvent, ValidationResult} from '@react-types/shared';
18+
import {BaseEvent, DOMAttributes, KeyboardDelegate, PressEvent, RouterOptions, ValidationResult} from '@react-types/shared';
1919
import {chain, isAppleDevice, mergeProps, useLabels, useRouter} from '@react-aria/utils';
2020
import {ComboBoxState} from '@react-stately/combobox';
2121
import {FocusEvent, InputHTMLAttributes, KeyboardEvent, RefObject, TouchEvent, useEffect, useMemo, useRef} from 'react';
@@ -124,7 +124,8 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta
124124
if (e.key === 'Enter') {
125125
let item = listBoxRef.current.querySelector(`[data-key="${CSS.escape(state.selectionManager.focusedKey.toString())}"]`);
126126
if (item instanceof HTMLAnchorElement) {
127-
router.open(item, e);
127+
let collectionItem = state.collection.getItem(state.selectionManager.focusedKey);
128+
router.open(item, e, collectionItem.props.href, collectionItem.props.routerOptions as RouterOptions);
128129
}
129130
}
130131

packages/@react-aria/link/src/useLink.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import {AriaLinkProps} from '@react-types/link';
1414
import {DOMAttributes, FocusableElement} from '@react-types/shared';
15-
import {filterDOMProps, mergeProps, shouldClientNavigate, useRouter} from '@react-aria/utils';
15+
import {filterDOMProps, mergeProps, shouldClientNavigate, useLinkProps, useRouter} from '@react-aria/utils';
1616
import React, {RefObject} from 'react';
1717
import {useFocusable} from '@react-aria/focus';
1818
import {usePress} from '@react-aria/interactions';
@@ -60,13 +60,14 @@ export function useLink(props: AriaLinkOptions, ref: RefObject<FocusableElement>
6060
}
6161
let {focusableProps} = useFocusable(props, ref);
6262
let {pressProps, isPressed} = usePress({onPress, onPressStart, onPressEnd, isDisabled, ref});
63-
let domProps = filterDOMProps(otherProps, {labelable: true, isLink: elementType === 'a'});
63+
let domProps = filterDOMProps(otherProps, {labelable: true});
6464
let interactionHandlers = mergeProps(focusableProps, pressProps);
6565
let router = useRouter();
66+
let routerLinkProps = useLinkProps(props);
6667

6768
return {
6869
isPressed, // Used to indicate press state for visual
69-
linkProps: mergeProps(domProps, {
70+
linkProps: mergeProps(domProps, routerLinkProps, {
7071
...interactionHandlers,
7172
...linkProps,
7273
'aria-disabled': isDisabled || undefined,
@@ -85,10 +86,11 @@ export function useLink(props: AriaLinkOptions, ref: RefObject<FocusableElement>
8586
e.currentTarget.href &&
8687
// If props are applied to a router Link component, it may have already prevented default.
8788
!e.isDefaultPrevented() &&
88-
shouldClientNavigate(e.currentTarget, e)
89+
shouldClientNavigate(e.currentTarget, e) &&
90+
props.href
8991
) {
9092
e.preventDefault();
91-
router.open(e.currentTarget, e);
93+
router.open(e.currentTarget, e, props.href, props.routerOptions);
9294
}
9395
}
9496
})

packages/@react-aria/listbox/src/useOption.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* governing permissions and limitations under the License.
1111
*/
1212

13-
import {chain, filterDOMProps, isMac, isWebKit, mergeProps, useSlotId} from '@react-aria/utils';
13+
import {chain, filterDOMProps, isMac, isWebKit, mergeProps, useLinkProps, useSlotId} from '@react-aria/utils';
1414
import {DOMAttributes, FocusableElement, Key} from '@react-types/shared';
1515
import {getItemCount} from '@react-stately/collections';
1616
import {getItemId, listData} from './utils';
@@ -149,13 +149,14 @@ export function useOption<T>(props: AriaOptionProps, state: ListState<T>, ref: R
149149
}
150150
});
151151

152-
let domProps = filterDOMProps(item?.props, {isLink: !!item?.props?.href});
152+
let domProps = filterDOMProps(item?.props);
153153
delete domProps.id;
154+
let linkProps = useLinkProps(item?.props);
154155

155156
return {
156157
optionProps: {
157158
...optionProps,
158-
...mergeProps(domProps, itemProps, hoverProps),
159+
...mergeProps(domProps, itemProps, hoverProps, linkProps),
159160
id: getItemId(state, key)
160161
},
161162
labelProps: {

0 commit comments

Comments
 (0)