Remove sticky headers in shared components grouped list (#32908)

* fix: use a flat list instead of grouped virtuoso for the grouped list

* test: update virtualized list

* test: update snapshots
This commit is contained in:
Florian Duros
2026-03-25 15:33:41 +01:00
committed by GitHub
parent d4fb08b392
commit 3764224fc2
3 changed files with 99 additions and 160 deletions
@@ -8283,16 +8283,18 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
tabindex="0" tabindex="0"
> >
<div <div
style="position: sticky; top: 0px; width: 100%; z-index: 1; margin-top: 0px;" data-viewport-type="element"
style="height: 100%; position: absolute; top: 0px; width: 100%;"
> >
<div <div
data-testid="virtuoso-top-item-list" data-testid="virtuoso-item-list"
style="box-sizing: border-box; margin-top: 0px; padding-bottom: 3432px; padding-top: 0px;"
> >
<div <div
data-index="0" data-index="0"
data-item-index="0" data-item-index="0"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none; position: sticky; z-index: 1;" style="overflow-anchor: none;"
> >
<div <div
aria-expanded="true" aria-expanded="true"
@@ -8334,20 +8336,9 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</button> </button>
</div> </div>
</div> </div>
</div>
</div>
<div
data-viewport-type="element"
style="height: 100%; position: absolute; top: 0px; width: 100%;"
>
<div
data-testid="virtuoso-item-list"
style="box-sizing: border-box; margin-top: 0px; padding-bottom: 3432px; padding-top: 52px;"
>
<div <div
data-index="1" data-index="1"
data-item-group-index="0" data-item-index="1"
data-item-index="0"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -8501,8 +8492,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="2" data-index="2"
data-item-group-index="0" data-item-index="2"
data-item-index="1"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -8629,8 +8619,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="3" data-index="3"
data-item-group-index="0" data-item-index="3"
data-item-index="2"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -8763,8 +8752,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="4" data-index="4"
data-item-group-index="0" data-item-index="4"
data-item-index="3"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -8891,8 +8879,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="5" data-index="5"
data-item-group-index="0" data-item-index="5"
data-item-index="4"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -9025,8 +9012,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="6" data-index="6"
data-item-group-index="0" data-item-index="6"
data-item-index="5"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -9176,8 +9162,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="7" data-index="7"
data-item-group-index="0" data-item-index="7"
data-item-index="6"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -9310,8 +9295,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="8" data-index="8"
data-item-group-index="0" data-item-index="8"
data-item-index="7"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -9438,8 +9422,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="9" data-index="9"
data-item-group-index="0" data-item-index="9"
data-item-index="8"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -9572,8 +9555,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="10" data-index="10"
data-item-group-index="0" data-item-index="10"
data-item-index="9"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -9700,8 +9682,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="11" data-index="11"
data-item-group-index="0" data-item-index="11"
data-item-index="10"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -9857,8 +9838,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="12" data-index="12"
data-item-group-index="0" data-item-index="12"
data-item-index="11"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -9985,8 +9965,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="13" data-index="13"
data-item-group-index="0" data-item-index="13"
data-item-index="12"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -10119,8 +10098,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="14" data-index="14"
data-item-group-index="0" data-item-index="14"
data-item-index="13"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -10247,8 +10225,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="15" data-index="15"
data-item-group-index="0" data-item-index="15"
data-item-index="14"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -10381,8 +10358,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="16" data-index="16"
data-item-group-index="0" data-item-index="16"
data-item-index="15"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -10532,8 +10508,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="17" data-index="17"
data-item-group-index="0" data-item-index="17"
data-item-index="16"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -10666,8 +10641,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="18" data-index="18"
data-item-group-index="0" data-item-index="18"
data-item-index="17"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -10794,8 +10768,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="19" data-index="19"
data-item-group-index="0" data-item-index="19"
data-item-index="18"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -10928,8 +10901,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="20" data-index="20"
data-item-group-index="0" data-item-index="20"
data-item-index="19"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -11056,8 +11028,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="21" data-index="21"
data-item-group-index="0" data-item-index="21"
data-item-index="20"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -11213,8 +11184,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="22" data-index="22"
data-item-group-index="0" data-item-index="22"
data-item-index="21"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -11341,8 +11311,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="23" data-index="23"
data-item-group-index="0" data-item-index="23"
data-item-index="22"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -11475,9 +11444,9 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="24" data-index="24"
data-item-index="1" data-item-index="24"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none; position: sticky; z-index: 1;" style="overflow-anchor: none;"
> >
<div <div
aria-expanded="true" aria-expanded="true"
@@ -11521,8 +11490,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="25" data-index="25"
data-item-group-index="1" data-item-index="25"
data-item-index="23"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -11649,8 +11617,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="26" data-index="26"
data-item-group-index="1" data-item-index="26"
data-item-index="24"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -11783,8 +11750,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="27" data-index="27"
data-item-group-index="1" data-item-index="27"
data-item-index="25"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -11934,8 +11900,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="28" data-index="28"
data-item-group-index="1" data-item-index="28"
data-item-index="26"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -12068,8 +12033,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="29" data-index="29"
data-item-group-index="1" data-item-index="29"
data-item-index="27"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -12196,8 +12160,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="30" data-index="30"
data-item-group-index="1" data-item-index="30"
data-item-index="28"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -12330,8 +12293,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="31" data-index="31"
data-item-group-index="1" data-item-index="31"
data-item-index="29"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -12458,8 +12420,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="32" data-index="32"
data-item-group-index="1" data-item-index="32"
data-item-index="30"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -12615,8 +12576,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="33" data-index="33"
data-item-group-index="1" data-item-index="33"
data-item-index="31"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -12743,8 +12703,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="34" data-index="34"
data-item-group-index="1" data-item-index="34"
data-item-index="32"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -12877,8 +12836,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="35" data-index="35"
data-item-group-index="1" data-item-index="35"
data-item-index="33"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -13005,8 +12963,7 @@ exports[`<RoomListView /> > renders LargeSectionList story 1`] = `
</div> </div>
<div <div
data-index="36" data-index="36"
data-item-group-index="1" data-item-index="36"
data-item-index="34"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -13626,16 +13583,18 @@ exports[`<RoomListView /> > renders SmallSectionList story 1`] = `
tabindex="0" tabindex="0"
> >
<div <div
style="position: sticky; top: 0px; width: 100%; z-index: 1; margin-top: 0px;" data-viewport-type="element"
style="height: 100%; position: absolute; top: 0px; width: 100%;"
> >
<div <div
data-testid="virtuoso-top-item-list" data-testid="virtuoso-item-list"
style="box-sizing: border-box; margin-top: 0px; padding-bottom: 0px; padding-top: 0px;"
> >
<div <div
data-index="0" data-index="0"
data-item-index="0" data-item-index="0"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none; position: sticky; z-index: 1;" style="overflow-anchor: none;"
> >
<div <div
aria-expanded="true" aria-expanded="true"
@@ -13677,20 +13636,9 @@ exports[`<RoomListView /> > renders SmallSectionList story 1`] = `
</button> </button>
</div> </div>
</div> </div>
</div>
</div>
<div
data-viewport-type="element"
style="height: 100%; position: absolute; top: 0px; width: 100%;"
>
<div
data-testid="virtuoso-item-list"
style="box-sizing: border-box; margin-top: 0px; padding-bottom: 0px; padding-top: 52px;"
>
<div <div
data-index="1" data-index="1"
data-item-group-index="0" data-item-index="1"
data-item-index="0"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -13844,8 +13792,7 @@ exports[`<RoomListView /> > renders SmallSectionList story 1`] = `
</div> </div>
<div <div
data-index="2" data-index="2"
data-item-group-index="0" data-item-index="2"
data-item-index="1"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none;" style="overflow-anchor: none;"
> >
@@ -13972,9 +13919,9 @@ exports[`<RoomListView /> > renders SmallSectionList story 1`] = `
</div> </div>
<div <div
data-index="3" data-index="3"
data-item-index="1" data-item-index="3"
data-known-size="52" data-known-size="52"
style="overflow-anchor: none; position: sticky; z-index: 1;" style="overflow-anchor: none;"
> >
<div <div
aria-expanded="true" aria-expanded="true"
@@ -6,7 +6,7 @@
*/ */
import React, { type JSX, useCallback, useMemo } from "react"; import React, { type JSX, useCallback, useMemo } from "react";
import { GroupedVirtuoso } from "react-virtuoso"; import { Virtuoso } from "react-virtuoso";
import { useVirtualizedList, type VirtualizedListContext, type VirtualizedListProps } from "../virtualized-list"; import { useVirtualizedList, type VirtualizedListContext, type VirtualizedListProps } from "../virtualized-list";
@@ -102,7 +102,7 @@ export interface GroupedVirtualizedListProps<Header, Item, Context> extends Omit
} }
/** /**
* A generic grouped virtualized list component built on top of react-virtuoso's GroupedVirtuoso. * A generic grouped virtualized list component built on top of react-virtuoso's Virtuoso.
* Provides keyboard navigation (including group headers) and virtualized rendering for * Provides keyboard navigation (including group headers) and virtualized rendering for
* performance with large lists. * performance with large lists.
* *
@@ -129,10 +129,8 @@ export function GroupedVirtualizedList<Header, Item, Context>(
...restProps ...restProps
} = props; } = props;
const groupCounts = useMemo(() => groups.map((group) => group.items.length), [groups]); // Build a flat array interleaving group headers with items.
const items = useMemo(() => groups.flatMap((group) => group.items), [groups]); // Each entry is either { header } or { item }.
// Build a flat navigation array interleaving group headers with items.
const flatEntries = useMemo( const flatEntries = useMemo(
() => () =>
groups.flatMap<NavigationEntry<Header, Item>>((group) => [ groups.flatMap<NavigationEntry<Header, Item>>((group) => [
@@ -142,34 +140,12 @@ export function GroupedVirtualizedList<Header, Item, Context>(
[groups], [groups],
); );
// Build both index-mapping functions in a single pass over the flat entries. // Pre-compute a lookup from flat index to group index.
// mapScrollIndex: flat index → GroupedVirtuoso item index (headers map to their // Each group contributes 1 header + N items, all mapped to the same group index.
// first item so scrollIntoView makes the sticky header visible). const flatIndexToGroupIndex = useMemo(
// mapRangeIndex: GroupedVirtuoso item index → flat index (translates visible-range () => groups.flatMap((group, groupIdx) => new Array(1 + group.items.length).fill(groupIdx)),
// indices back so the hook's PageUp/PageDown and focus-restore logic works). [groups],
const { mapScrollIndex, mapRangeIndex } = useMemo(() => { );
// Map each flat index to the corresponding virtuoso item index.
// Headers map to the first item of their group so scrollIntoView shows the sticky header.
const flatIndexToVirtuosoIndex: number[] = [];
// Map the Item index (from virtuoso) to their position in the flat list
const virtuosoIndexToFlatIndex: number[] = [];
let virtuosoIndex = 0;
for (let i = 0; i < flatEntries.length; i++) {
flatIndexToVirtuosoIndex.push(virtuosoIndex);
if ("item" in flatEntries[i]) {
virtuosoIndexToFlatIndex.push(i);
virtuosoIndex++;
}
}
return {
mapScrollIndex: (flatIndex: number): number => flatIndexToVirtuosoIndex[flatIndex] ?? 0,
mapRangeIndex: (virtuosoIndex: number): number => virtuosoIndexToFlatIndex[virtuosoIndex] ?? 0,
};
}, [flatEntries]);
// Wrap getItemKey: dispatch to getHeaderKey or getItemKey based on entry type // Wrap getItemKey: dispatch to getHeaderKey or getItemKey based on entry type
const wrappedGetEntryKey = useCallback( const wrappedGetEntryKey = useCallback(
@@ -178,7 +154,7 @@ export function GroupedVirtualizedList<Header, Item, Context>(
[getHeaderKey, getItemKey], [getHeaderKey, getItemKey],
); );
// Wrap isItemFocusable: headers use isHeaderFocusable (default: always true), items use isItemFocusable // Wrap isItemFocusable: headers use isGroupHeaderFocusable, items use isItemFocusable
const wrappedIsEntryFocusable = useCallback( const wrappedIsEntryFocusable = useCallback(
(entry: NavigationEntry<Header, Item>): boolean => (entry: NavigationEntry<Header, Item>): boolean =>
"header" in entry ? isGroupHeaderFocusable(entry.header) : isItemFocusable(entry.item), "header" in entry ? isGroupHeaderFocusable(entry.header) : isItemFocusable(entry.item),
@@ -194,8 +170,6 @@ export function GroupedVirtualizedList<Header, Item, Context>(
items: flatEntries, items: flatEntries,
isItemFocusable: wrappedIsEntryFocusable, isItemFocusable: wrappedIsEntryFocusable,
getItemKey: wrappedGetEntryKey, getItemKey: wrappedGetEntryKey,
mapScrollIndex,
mapRangeIndex,
}, },
); );
@@ -215,27 +189,44 @@ export function GroupedVirtualizedList<Header, Item, Context>(
[onFocusForGetItemComponent], [onFocusForGetItemComponent],
); );
const getItemComponentInternal = useCallback( // Unified item renderer that dispatches to group header or item component
(index: number, groupIndex: number, _item: unknown, context: VirtualizedListContext<Context>): JSX.Element => // based on the entry type at the given flat index.
getItemComponent(index, items[index], context, onFocusForItem, groupIndex), const itemContent = useCallback(
[items, getItemComponent, onFocusForItem], (
); flatIndex: number,
_entry: NavigationEntry<Header, Item>,
context: VirtualizedListContext<Context>,
): JSX.Element => {
const entry = flatEntries[flatIndex];
const groupIndex = flatIndexToGroupIndex[flatIndex];
const getGroupHeaderComponentInternal = useCallback( if ("header" in entry) {
(groupIndex: number, context: VirtualizedListContext<Context>): JSX.Element => return getGroupHeaderComponent(groupIndex, entry.header, context, onFocusForHeader);
getGroupHeaderComponent(groupIndex, groups[groupIndex].header, context, onFocusForHeader), }
[getGroupHeaderComponent, onFocusForHeader, groups],
// Item index in the flattened (non-header) items array:
// flatIndex minus the number of headers before it (groupIndex + 1).
const itemIndex = flatIndex - (groupIndex + 1);
return getItemComponent(itemIndex, entry.item, context, onFocusForItem, groupIndex);
},
[
flatEntries,
flatIndexToGroupIndex,
getGroupHeaderComponent,
getItemComponent,
onFocusForItem,
onFocusForHeader,
],
); );
return ( return (
<GroupedVirtuoso <Virtuoso
// note that either the container of direct children must be focusable to be axe // note that either the container of direct children must be focusable to be axe
// compliant, so we leave tabIndex as the default so the container can be focused // compliant, so we leave tabIndex as the default so the container can be focused
// (virtuoso wraps the children inside another couple of elements so setting it // (virtuoso wraps the children inside another couple of elements so setting it
// on those doesn't seem to work, unfortunately) // on those doesn't seem to work, unfortunately)
groupCounts={groupCounts} itemContent={itemContent}
itemContent={getItemComponentInternal} data={flatEntries}
groupContent={getGroupHeaderComponentInternal}
{...virtuosoProps} {...virtuosoProps}
/> />
); );
@@ -379,12 +379,13 @@ describe.each<ListTestVariant>([flatVariant, groupedVariant])("$name", (variant)
// Then press PageUp to jump up by viewport size // Then press PageUp to jump up by viewport size
fireEvent.keyDown(container, { code: "PageUp" }); fireEvent.keyDown(container, { code: "PageUp" });
// Verify focus moved up // Verify focus moved up – use the variant's navigable selector so
const items = container.querySelectorAll(".mx_item"); // group headers (which are also navigable) are included.
// PageUp should move back to the first item since we only have 4 items const allNav = container.querySelectorAll(variant.navigableSelector);
expectTabIndex(items[0], "0"); // PageUp should move back to the first navigable element since we only have a few items
const lastIndex = items.length - 1; expectTabIndex(allNav[0], "0");
expectTabIndex(items[lastIndex], "-1"); const lastIndex = allNav.length - 1;
expectTabIndex(allNav[lastIndex], "-1");
}); });
it("should not handle keyboard navigation when modifier keys are pressed", () => { it("should not handle keyboard navigation when modifier keys are pressed", () => {