Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 14 additions & 6 deletions packages/@react-aria/gridlist/src/useGridListItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export function useGridListItem<T>(props: AriaGridListItemOptions, state: ListSt
linkBehavior
});

const INPUT_TYPES = new Set(['color', 'date', 'datetime', 'datetime-local', 'email', 'month', 'number', 'password', 'range', 'search', 'tel', 'text', 'time', 'url', 'week', 'video', 'audio', 'contenteditable']);
Copy link
Member

Choose a reason for hiding this comment

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

video/audio/contenteditable don't apply to HTMLInputElement, they are their own elements or props on divs

let onKeyDown = (e: ReactKeyboardEvent) => {
if (!e.currentTarget.contains(e.target as Element)) {
return;
Expand Down Expand Up @@ -166,6 +167,8 @@ export function useGridListItem<T>(props: AriaGridListItemOptions, state: ListSt
}
}
}
} else if (e.target instanceof HTMLInputElement && INPUT_TYPES.has(e.target.type)) {
e.stopPropagation();
}
break;
}
Expand Down Expand Up @@ -195,15 +198,19 @@ export function useGridListItem<T>(props: AriaGridListItemOptions, state: ListSt
}
}
}
} else if (e.target instanceof HTMLInputElement && INPUT_TYPES.has(e.target.type)) {
e.stopPropagation();
}
break;
}
case 'ArrowUp':
case 'ArrowDown':
// Prevent this event from reaching row children, e.g. menu buttons. We want arrow keys to navigate
// to the row above/below instead. We need to re-dispatch the event from a higher parent so it still
// bubbles and gets handled by useSelectableCollection.
if (!e.altKey && ref.current.contains(e.target as Element)) {
if ((keyboardNavigationBehavior === 'tab' && e.target instanceof HTMLInputElement && INPUT_TYPES.has(e.target.type))) {
e.stopPropagation();
} else if ((!e.altKey && ref.current.contains(e.target as Element))) {
// Prevent this event from reaching row children, e.g. menu buttons. We want arrow keys to navigate
// to the row above/below instead. We need to re-dispatch the event from a higher parent so it still
// bubbles and gets handled by useSelectableCollection.
e.stopPropagation();
e.preventDefault();
ref.current.parentElement.dispatchEvent(
Expand All @@ -226,7 +233,7 @@ export function useGridListItem<T>(props: AriaGridListItemOptions, state: ListSt
}
};

let onFocus = (e) => {
let onFocus = (e: FocusEvent) => {
keyWhenFocused.current = node.key;
if (e.target !== ref.current) {
// useSelectableItem only handles setting the focused key when
Expand Down Expand Up @@ -256,7 +263,8 @@ export function useGridListItem<T>(props: AriaGridListItemOptions, state: ListSt

let rowProps: DOMAttributes = mergeProps(itemProps, linkProps, {
role: 'row',
onKeyDownCapture: onKeyDown,
onKeyDownCapture: keyboardNavigationBehavior === 'arrow' ? onKeyDown : undefined,
onKeyDown: keyboardNavigationBehavior === 'tab' ? onKeyDown : undefined,
onFocus,
// 'aria-label': [(node.textValue || undefined), rowAnnouncement].filter(Boolean).join(', '),
'aria-label': node.textValue || undefined,
Expand Down
24 changes: 23 additions & 1 deletion packages/@react-aria/selection/src/useSelectableCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,17 @@ export function useSelectableCollection(options: AriaSelectableCollectionOptions
if (element) {
// This prevents a flash of focus on the first/last element in the collection, or the collection itself.
if (!element.contains(document.activeElement)) {
focusWithoutScrolling(element);
if (e.relatedTarget != null && ref.current.compareDocumentPosition(e.relatedTarget as Node) & Node.DOCUMENT_POSITION_FOLLOWING) {
let treeWalker = getFocusableTreeWalker(element);
let lastCellChild = last(treeWalker);
if (lastCellChild != null) {
focusWithoutScrolling(lastCellChild);
} else {
focusWithoutScrolling(element);
}
} else {
focusWithoutScrolling(element);
}
}

let modality = getInteractionModality();
Expand Down Expand Up @@ -488,3 +498,15 @@ export function useSelectableCollection(options: AriaSelectableCollectionOptions
}
};
}

function last(walker: TreeWalker) {
let next: FocusableElement;
let last: FocusableElement;
do {
last = walker.lastChild() as FocusableElement;
if (last) {
next = last;
}
} while (last);
return next;
}
46 changes: 25 additions & 21 deletions packages/react-aria-components/stories/GridList.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,27 +22,31 @@ export default {
};

export const GridListExample = (args) => (
<GridList
{...args}
className={styles.menu}
aria-label="test gridlist"
style={{
width: 300,
height: 300,
display: 'grid',
gridTemplate: args.layout === 'grid' ? 'repeat(3, 1fr) / repeat(3, 1fr)' : 'auto / 1fr',
gridAutoFlow: 'row'
}}>
<MyGridListItem>1,1 <Button>Actions</Button></MyGridListItem>
<MyGridListItem>1,2 <Button>Actions</Button></MyGridListItem>
<MyGridListItem>1,3 <Button>Actions</Button></MyGridListItem>
<MyGridListItem>2,1 <Button>Actions</Button></MyGridListItem>
<MyGridListItem>2,2 <Button>Actions</Button></MyGridListItem>
<MyGridListItem>2,3 <Button>Actions</Button></MyGridListItem>
<MyGridListItem>3,1 <Button>Actions</Button></MyGridListItem>
<MyGridListItem>3,2 <Button>Actions</Button></MyGridListItem>
<MyGridListItem>3,3 <Button>Actions</Button></MyGridListItem>
</GridList>
<>
<input />
<GridList
{...args}
className={styles.menu}
aria-label="test gridlist"
style={{
width: 300,
height: 300,
display: 'grid',
gridTemplate: args.layout === 'grid' ? 'repeat(3, 1fr) / repeat(3, 1fr)' : 'auto / 1fr',
gridAutoFlow: 'row'
}}>
<MyGridListItem>1,1 <input /></MyGridListItem>
<MyGridListItem>1,2 <Button>Actions</Button></MyGridListItem>
<MyGridListItem>1,3 <Button>Actions</Button></MyGridListItem>
<MyGridListItem>2,1 <Button>Actions</Button></MyGridListItem>
<MyGridListItem>2,2 <Button>Actions</Button></MyGridListItem>
<MyGridListItem>2,3 <Button>Actions</Button></MyGridListItem>
<MyGridListItem>3,1 <Button>Actions</Button></MyGridListItem>
<MyGridListItem>3,2 <Button>Actions</Button></MyGridListItem>
<MyGridListItem>3,3 <Button>Actions</Button></MyGridListItem>
</GridList>
<input />
</>
);

const MyGridListItem = (props: GridListItemProps) => {
Expand Down