Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8b71adf
initial changes to support textfields in gridlists
LFDanLu Jun 4, 2026
f491a18
use robs event leak type select and prevent space from triggering sel…
LFDanLu Jun 4, 2026
8fd5392
fix tests, but also use a more robust check instead of active element
LFDanLu Jun 4, 2026
10beb03
add S2 Card story and tests for tree and gridlist
LFDanLu Jun 5, 2026
5dfa72f
make presses on components in rows not trigger selection
LFDanLu Jun 5, 2026
0aecbf1
add docs for rac
LFDanLu Jun 5, 2026
4c35292
update docs and dedupe tree logic in useGridListItem keyboard handlers
LFDanLu Jun 8, 2026
40fe024
Merge branch 'main' of github.com:adobe/react-spectrum into textfield…
LFDanLu Jun 8, 2026
4e8d6f5
create stories for table with textfields
LFDanLu Jun 9, 2026
dde69eb
support textfields in tables
LFDanLu Jun 9, 2026
8ea84d4
add tests
LFDanLu Jun 9, 2026
fd92d8f
add table docs and update copy
LFDanLu Jun 9, 2026
d5261df
fix missing s2 table columnheader focus ring and add focus ring to th…
LFDanLu Jun 9, 2026
ef47186
formatting
LFDanLu Jun 9, 2026
9570eb4
make editable table edit buttons focusable via tab when using tab key…
LFDanLu Jun 12, 2026
52f8707
Merge branch 'main' of github.com:adobe/react-spectrum into textfield…
LFDanLu Jun 17, 2026
63114f1
clean up uneeded code and mirror useGridListItem more closely
LFDanLu Jun 18, 2026
9318022
add focusMode to Cell, make S2 TableView auto focus selection checkbo…
LFDanLu Jun 18, 2026
9edd140
Merge branch 'main' into textfield_gridlists
LFDanLu Jun 18, 2026
2558dd3
add focusMode to gridListItem
LFDanLu Jun 18, 2026
f58f627
Merge branch 'textfield_gridlists' of github.com:adobe/react-spectrum…
LFDanLu Jun 18, 2026
0d6d429
add allowsArrowNavigation so arrow key navigation can be triggered ev…
LFDanLu Jun 18, 2026
9bd8d76
add allowsArrowNavigation and focusMode to tree, tests, and docs updates
LFDanLu Jun 19, 2026
d954118
add docs for focusMode/allowsArrowNavigation
LFDanLu Jun 19, 2026
babfeb4
move press selection prevention logic into useSelectableItem
LFDanLu Jun 19, 2026
06b5308
fix case where click on cell didnt trigger row selection
LFDanLu Jun 19, 2026
7da4f02
fix case with nested collections and other review comments
LFDanLu Jun 22, 2026
f7e7e3e
clean up tests so it uses one table implementation
LFDanLu Jun 22, 2026
1bdd6e0
Merge branch 'main' of github.com:adobe/react-spectrum into textfield…
LFDanLu Jun 25, 2026
f156d28
make sure shift tabbing from a child of a cell doesnt move focus back…
LFDanLu Jun 25, 2026
ab75b15
add coverage tests
LFDanLu Jun 26, 2026
34e52a9
fix lint
LFDanLu Jun 26, 2026
b57da87
Merge branch 'main' of github.com:adobe/react-spectrum into textfield…
LFDanLu Jul 13, 2026
e80b56c
restore table column header default
LFDanLu Jul 13, 2026
32605c6
Merge branch 'main' into textfield_gridlists
LFDanLu Jul 16, 2026
c06a5ec
Merge branch 'main' into textfield_gridlists
snowystinger Jul 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/src/TreeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export interface TreeViewProps<T>
| 'selectionBehavior'
| 'onScroll'
| 'onCellAction'
| 'keyboardNavigationBehavior'
| keyof GlobalDOMAttributes
>,
UnsafeStyles,
Expand Down
42 changes: 36 additions & 6 deletions packages/@react-spectrum/s2/stories/CardView.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {MenuItem} from '../src/Menu';
import type {Meta, StoryObj} from '@storybook/react';
import {SkeletonCollection} from '../src/SkeletonCollection';
import {style} from '../style/spectrum-theme' with {type: 'macro'};
import {TextField} from '../src/TextField';
import {useAsyncList} from 'react-stately/useAsyncList';

const meta: Meta<typeof CardView> = {
Expand Down Expand Up @@ -72,7 +73,15 @@ const avatarSize = {
XL: 32
} as const;

export function PhotoCard({item, layout}: {item: Item; layout: string}) {
export function PhotoCard({
item,
layout,
interactive
}: {
item: Item;
layout: string;
interactive?: React.ReactNode;
}) {
return (
<Card id={item.id} textValue={item.description || item.alt_description}>
{({size}) => (
Expand Down Expand Up @@ -112,12 +121,20 @@ export function PhotoCard({item, layout}: {item: Item; layout: string}) {
<div
className={style({
display: 'flex',
alignItems: 'center',
flexDirection: 'column',
gap: 8,
gridArea: 'description'
})}>
<Avatar src={item.user.profile_image.small} size={avatarSize[size]} />
<Text slot="description">{item.user.name}</Text>
<div
className={style({
display: 'flex',
alignItems: 'center',
gap: 8
})}>
<Avatar src={item.user.profile_image.small} size={avatarSize[size]} />
<Text slot="description">{item.user.name}</Text>
</div>
{interactive}
</div>
</Content>
</>
Expand All @@ -126,7 +143,7 @@ export function PhotoCard({item, layout}: {item: Item; layout: string}) {
);
}

export const ExampleRender = (args: CardViewProps<any>) => {
export const ExampleRender = (args: CardViewProps<any> & {interactive?: React.ReactNode}) => {
let list = useAsyncList<Item, number | null>({
async load({signal, cursor, items}) {
let page = cursor || 1;
Expand Down Expand Up @@ -155,7 +172,9 @@ export const ExampleRender = (args: CardViewProps<any>) => {
onLoadMore={args.loadingState === 'idle' ? list.loadMore : undefined}
styles={cardViewStyles}>
<Collection items={items} dependencies={[args.layout]}>
{item => <PhotoCard item={item} layout={args.layout || 'grid'} />}
{item => (
<PhotoCard interactive={args.interactive} item={item} layout={args.layout || 'grid'} />
)}
</Collection>
{(loadingState === 'loading' || loadingState === 'loadingMore') && (
<SkeletonCollection>
Expand Down Expand Up @@ -288,3 +307,14 @@ export const CollectionCards: Story = {
onAction: undefined
}
};

export const CardViewWithTextField: Story = {
render: args => (
<ExampleRender {...args} interactive={<TextField aria-label="search photos" />} />
),
args: {
loadingState: 'idle',
onAction: undefined,
selectionMode: 'multiple'
}
};
51 changes: 50 additions & 1 deletion packages/dev/s2-docs/pages/react-aria/GridList.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,7 @@ function Example(props) {

Use the `layout` and `orientation` props to arrange items in horizontal and vertical stacks and grids. This affects keyboard navigation and drag and drop behavior.

```tsx render docs={docs.exports.GridList} links={docs.links} props={['layout', 'orientation', 'keyboardNavigationBehavior']} initialProps={{layout: 'grid', orientation: 'horizontal', keyboardNavigationBehavior: 'tab'}} wide
```tsx render docs={docs.exports.GridList} links={docs.links} props={['layout', 'orientation']} initialProps={{layout: 'grid', orientation: 'horizontal'}} wide
"use client";
import {GridList, GridListItem, Text} from 'vanilla-starter/GridList';

Expand Down Expand Up @@ -704,6 +704,55 @@ let photos = [
</GridList>
```

## Keyboard navigation

By default, GridList uses arrow key navigation to move focus into rows. Set `keyboardNavigationBehavior="tab"` to have <Keyboard>Tab</Keyboard> move focus in and out of a row.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should include a blurb about when you'd be likely to want to change the keyboard navigation behavior, ie you have something like a textfield or something else that would conflict with arrow key navigation


```tsx render
"use client";
import {GridList, GridListItem, Text} from 'vanilla-starter/GridList';
import {ComboBox, ComboBoxItem} from 'vanilla-starter/ComboBox';

///- begin collapse -///
///- begin collapse -///
let photos = [
{id: 1, title: 'Desert Sunset', description: 'PNG • 2/3/2024', src: 'https://images.unsplash.com/photo-1705034598432-1694e203cdf3?q=80&w=600&auto=format&fit=crop'},
{id: 2, title: 'Hiking Trail', description: 'JPEG • 1/10/2022', src: 'https://images.unsplash.com/photo-1722233987129-61dc344db8b6?q=80&w=600&auto=format&fit=crop'},
{id: 3, title: 'Lion', description: 'JPEG • 8/28/2021', src: 'https://images.unsplash.com/photo-1629812456605-4a044aa38fbc?q=80&w=600&auto=format&fit=crop'},
{id: 4, title: 'Mountain Sunrise', description: 'PNG • 3/15/2015', src: 'https://images.unsplash.com/photo-1722172118908-1a97c312ce8c?q=80&w=600&auto=format&fit=crop'},
{id: 5, title: 'Giraffe tongue', description: 'PNG • 11/27/2019', src: 'https://images.unsplash.com/photo-1574870111867-089730e5a72b?q=80&w=600&auto=format&fit=crop'},
{id: 6, title: 'Golden Hour', description: 'WEBP • 7/24/2024', src: 'https://images.unsplash.com/photo-1718378037953-ab21bf2cf771?q=80&w=600&auto=format&fit=crop'},
];

function PermissionPicker({label}) {
return (
<ComboBox style={{paddingInlineStart: 12, width: '80%'}} aria-label={label} defaultSelectedKey="view" placeholder="Permission">
<ComboBoxItem id="view">Can view</ComboBoxItem>
<ComboBoxItem id="comment">Can comment</ComboBoxItem>
<ComboBoxItem id="edit">Can edit</ComboBoxItem>
</ComboBox>
);
}
///- end collapse -///

<GridList
/*- begin highlight -*/
keyboardNavigationBehavior="tab"
/*- end highlight -*/
items={photos}
selectionMode="multiple"
aria-label="Shared files">
{item => (
<GridListItem textValue={item.title}>
<img src={item.src} alt="" />
<Text>{item.title}</Text>
<Text slot="description">{item.description}</Text>
<PermissionPicker label={`${item.title} permission`} />
</GridListItem>
)}
</GridList>
```

## Drag and drop

GridList supports drag and drop interactions when the `dragAndDropHooks` prop is provided using the <TypeLink links={docs.links} type={docs.exports.useDragAndDrop} /> hook. Users can drop data on the list as a whole, on individual items, insert new items between existing ones, or reorder items. React Aria supports drag and drop via mouse, touch, keyboard, and screen reader interactions. See the [drag and drop guide](dnd?component=GridList) to learn more.
Expand Down
56 changes: 55 additions & 1 deletion packages/dev/s2-docs/pages/react-aria/Tree.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ import {Tree, TreeHeader, TreeItem, TreeSection} from 'vanilla-starter/Tree';
<TreeItem id="shared-photos" title="Shared Photos">
<TreeItem id="shared-photo-1" title="Shared Photo 1" />
<TreeItem id="shared-photo-2" title="Shared Photo 2" />
</TreeItem>
</TreeItem>
</TreeSection>
<TreeSection>
<TreeHeader>Documents</TreeHeader>
Expand Down Expand Up @@ -322,6 +322,60 @@ function Example(props) {
}
```

## Keyboard navigation

By default, Tree uses arrow key navigation to move focus into rows. Set `keyboardNavigationBehavior="tab"` to have <Keyboard>Option</Keyboard> move focus in and out of a row.

```tsx render
"use client";
import {Tree, TreeItem, TreeItemContent} from 'vanilla-starter/Tree';
import {ComboBox, ComboBoxItem} from 'vanilla-starter/ComboBox';

///- begin collapse -///
function PermissionPicker({label}) {
return (
<ComboBox style={{marginInlineStart: 'auto', flexShrink: 0}} aria-label={label} defaultSelectedKey="view" placeholder="Permission">
<ComboBoxItem id="view">Can view</ComboBoxItem>
<ComboBoxItem id="comment">Can comment</ComboBoxItem>
<ComboBoxItem id="edit">Can edit</ComboBoxItem>
</ComboBox>
);
}
///- end collapse -///

<Tree
/*- begin highlight -*/
keyboardNavigationBehavior="tab"
/*- end highlight -*/
selectionMode="multiple"
defaultExpandedKeys={['documents', 'photos']}
aria-label="Shared files"
style={{width: 420}}>
<TreeItem id="documents" title="Documents">
<TreeItem id="weekly" textValue="Weekly Report.pdf">
<TreeItemContent>
Weekly Report.pdf
<PermissionPicker label="Weekly Report.pdf permission" />
</TreeItemContent>
</TreeItem>
<TreeItem id="budget" textValue="Budget.xlsx">
<TreeItemContent>
Budget.xlsx
<PermissionPicker label="Budget.xlsx permission" />
</TreeItemContent>
</TreeItem>
</TreeItem>
<TreeItem id="photos" title="Photos">
<TreeItem id="sunset" textValue="Sunset.jpg">
<TreeItemContent>
Sunset.jpg
<PermissionPicker label="Sunset.jpg permission" />
</TreeItemContent>
</TreeItem>
</TreeItem>
</Tree>
```

## Drag and drop

Tree supports drag and drop interactions when the `dragAndDropHooks` prop is provided using the <TypeLink links={docs.links} type={docs.exports.useDragAndDrop} /> hook. Users can drop data on the list as a whole, on individual items, insert new items between existing ones, or reorder items. React Aria supports drag and drop via mouse, touch, keyboard, and screen reader interactions. See the [drag and drop guide](dnd?component=Tree) to learn more.
Expand Down
Loading