Skip to content

Commit a4dee6e

Browse files
authored
Fix synthetic links not using useHref (#6346)
1 parent 91f63a6 commit a4dee6e

File tree

7 files changed

+60
-11
lines changed

7 files changed

+60
-11
lines changed

packages/@react-aria/gridlist/src/useGridListItem.ts

Lines changed: 3 additions & 2 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, getScrollParent, getSyntheticLinkProps, mergeProps, scrollIntoViewport, useSlotId} from '@react-aria/utils';
13+
import {chain, getScrollParent, mergeProps, scrollIntoViewport, useSlotId, useSyntheticLinkProps} from '@react-aria/utils';
1414
import {DOMAttributes, FocusableElement, Node as RSNode} from '@react-types/shared';
1515
import {focusSafely, getFocusableTreeWalker} from '@react-aria/focus';
1616
import {getLastItem} from '@react-stately/collections';
@@ -242,7 +242,8 @@ export function useGridListItem<T>(props: AriaGridListItemOptions, state: ListSt
242242
}
243243
};
244244

245-
let linkProps = itemStates.hasAction ? getSyntheticLinkProps(node.props) : {};
245+
let syntheticLinkProps = useSyntheticLinkProps(node.props);
246+
let linkProps = itemStates.hasAction ? syntheticLinkProps : {};
246247
// TODO: re-add when we get translations and fix this for iOS VO
247248
// let rowAnnouncement;
248249
// if (onAction) {

packages/@react-aria/table/src/useTableRow.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
import {FocusableElement} from '@react-types/shared';
1414
import {getLastItem} from '@react-stately/collections';
1515
import {getRowLabelledBy} from './utils';
16-
import {getSyntheticLinkProps, mergeProps} from '@react-aria/utils';
1716
import type {GridNode} from '@react-types/grid';
1817
import {GridRowAria, GridRowProps, useGridRow} from '@react-aria/grid';
1918
import {HTMLAttributes, RefObject} from 'react';
19+
import {mergeProps, useSyntheticLinkProps} from '@react-aria/utils';
2020
import {TableCollection} from '@react-types/table';
2121
import {tableNestedRows} from '@react-stately/flags';
2222
import {TableState, TreeGridState} from '@react-stately/table';
@@ -74,7 +74,8 @@ export function useTableRow<T>(props: GridRowProps<T>, state: TableState<T> | Tr
7474
}
7575
}
7676

77-
let linkProps = states.hasAction ? getSyntheticLinkProps(node.props) : {};
77+
let syntheticLinkProps = useSyntheticLinkProps(node.props);
78+
let linkProps = states.hasAction ? syntheticLinkProps : {};
7879
return {
7980
rowProps: {
8081
...mergeProps(rowProps, treeGridRowProps, linkProps),

packages/@react-aria/tag/src/useTag.ts

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

1313
import {AriaButtonProps} from '@react-types/button';
1414
import {DOMAttributes, FocusableElement, Node} from '@react-types/shared';
15-
import {filterDOMProps, getSyntheticLinkProps, mergeProps, useDescription, useId} from '@react-aria/utils';
15+
import {filterDOMProps, mergeProps, useDescription, useId, useSyntheticLinkProps} from '@react-aria/utils';
1616
import {hookData} from './useTagGroup';
1717
// @ts-ignore
1818
import intlMessages from '../intl/*.json';
@@ -82,7 +82,7 @@ export function useTag<T>(props: AriaTagProps<T>, state: ListState<T>, ref: RefO
8282
let isFocused = item.key === state.selectionManager.focusedKey;
8383
// @ts-ignore - data attributes are ok but TS doesn't know about them.
8484
let domProps = filterDOMProps(item.props);
85-
let linkProps = getSyntheticLinkProps(item.props);
85+
let linkProps = useSyntheticLinkProps(item.props);
8686
return {
8787
removeButtonProps: {
8888
'aria-label': stringFormatter.format('removeButtonLabel'),

packages/@react-aria/utils/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export {mergeRefs} from './mergeRefs';
1717
export {filterDOMProps} from './filterDOMProps';
1818
export {focusWithoutScrolling} from './focusWithoutScrolling';
1919
export {getOffset} from './getOffset';
20-
export {openLink, getSyntheticLinkProps, RouterProvider, shouldClientNavigate, useRouter, useLinkProps} from './openLink';
20+
export {openLink, useSyntheticLinkProps, RouterProvider, shouldClientNavigate, useRouter, useLinkProps} from './openLink';
2121
export {runAfterTransition} from './runAfterTransition';
2222
export {useDrag1D} from './useDrag1D';
2323
export {useGlobalListeners} from './useGlobalListeners';

packages/@react-aria/utils/src/openLink.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,10 @@ function openSyntheticLink(target: Element, modifiers: Modifiers) {
146146
getSyntheticLink(target, link => openLink(link, modifiers));
147147
}
148148

149-
export function getSyntheticLinkProps(props: LinkDOMProps) {
149+
export function useSyntheticLinkProps(props: LinkDOMProps) {
150+
let router = useRouter();
150151
return {
151-
'data-href': props.href,
152+
'data-href': props.href ? router.useHref(props.href) : undefined,
152153
'data-target': props.target,
153154
'data-rel': props.rel,
154155
'data-download': props.download,

packages/react-aria-components/test/GridList.test.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,16 @@
1111
*/
1212

1313
import {act, fireEvent, mockClickDefault, pointerMap, render, within} from '@react-spectrum/test-utils-internal';
14-
import {Button, Checkbox, DropIndicator, GridList, GridListContext, GridListItem, useDragAndDrop} from '../';
14+
import {
15+
Button,
16+
Checkbox,
17+
DropIndicator,
18+
GridList,
19+
GridListContext,
20+
GridListItem,
21+
RouterProvider,
22+
useDragAndDrop
23+
} from '../';
1524
import React from 'react';
1625
import userEvent from '@testing-library/user-event';
1726

@@ -522,6 +531,23 @@ describe('GridList', () => {
522531
expect(onClick.mock.calls[0][0].target).toBeInstanceOf(HTMLAnchorElement);
523532
expect(onClick.mock.calls[0][0].target.href).toBe('https://google.com/');
524533
});
534+
535+
it('should work with RouterProvider', async () => {
536+
let navigate = jest.fn();
537+
let useHref = href => '/base' + href;
538+
let {getAllByRole} = render(
539+
<RouterProvider navigate={navigate} useHref={useHref}>
540+
<GridList aria-label="listview">
541+
<GridListItem href="/foo" routerOptions={{foo: 'bar'}}>One</GridListItem>
542+
</GridList>
543+
</RouterProvider>
544+
);
545+
546+
let items = getAllByRole('row');
547+
expect(items[0]).toHaveAttribute('data-href', '/base/foo');
548+
await trigger(items[0]);
549+
expect(navigate).toHaveBeenCalledWith('/foo', {foo: 'bar'});
550+
});
525551
});
526552
});
527553
});

packages/react-aria-components/test/TagGroup.test.js

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

13-
import {Button, Label, Tag, TagGroup, TagList, Text} from '../';
13+
import {Button, Label, RouterProvider, Tag, TagGroup, TagList, Text} from '../';
1414
import {fireEvent, mockClickDefault, pointerMap, render} from '@react-spectrum/test-utils-internal';
1515
import React from 'react';
1616
import userEvent from '@testing-library/user-event';
@@ -329,6 +329,26 @@ describe('TagGroup', () => {
329329
document.removeEventListener('click', onClick);
330330
}
331331
});
332+
333+
it('should work with RouterProvider', async () => {
334+
let navigate = jest.fn();
335+
let useHref = href => '/base' + href;
336+
let {getAllByRole} = render(
337+
<RouterProvider navigate={navigate} useHref={useHref}>
338+
<TagGroup selectionMode="none">
339+
<Label>Tags</Label>
340+
<TagList>
341+
<Tag href="/foo" routerOptions={{foo: 'bar'}}>One</Tag>
342+
</TagList>
343+
</TagGroup>
344+
</RouterProvider>
345+
);
346+
347+
let items = getAllByRole('row');
348+
expect(items[0]).toHaveAttribute('data-href', '/base/foo');
349+
await trigger(items[0]);
350+
expect(navigate).toHaveBeenCalledWith('/foo', {foo: 'bar'});
351+
});
332352
});
333353
});
334354
});

0 commit comments

Comments
 (0)