Support (or document) `trigger="contextMenu"` on collection items (GridListItem / TreeItem / Row)
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 52/100
- issue の種類
- 機能追加
- 明瞭さ
- おおむね明確
- 活発さ
- 静か
- 技術スタック
- react, typescript
調査の方向性
useContextMenu と MenuTrigger から始め、その後 useSelectableItem.ts:409 と usePress.ts:112/1010 にある collection-item のイベント処理を比較します。issue で名前が挙げられている Menu の stories とテストを調べ、サポートされる結果がドキュメント化なのか API の変更なのかを判断します。collection の行に、ドキュメント化された動作とカバレッジを備えた、サポート対象で警告のないコンテキストメニューの経路があれば完了です。
索引モデルが issue の本文から書いたものです。
説明
Provide a general summary of the feature here
trigger="contextMenu" (new in v1.20.0) works well for a standalone trigger, but there doesn't appear to be a supported way to use it on a collection item — a GridListItem, TreeItem, or table Row. Right-clicking a row to open the same menu its kebab button opens seems like one of the most common reasons to want a context menu, so I suspect this is either a gap or a pattern I've failed to find.
Either a supported API for it, or docs guidance on the intended approach, would help.
🤔 Expected Behavior?
Being able to attach a context menu to a collection item, so that right-click / long-press / Shift+F10 on a row opens the same menu the row's kebab button opens.
Collection items already look like valid trigger candidates: the docs say MenuTrigger "works with any pressable React Aria component", and rows are pressable —
useGridListItem→useSelectableItem→usePress(useSelectableItem.ts:409)usePressreadsPressResponderContext(usePress.ts:112) and merges the remaining DOM props into its result (usePress.ts:1010)
so a row would consume MenuTrigger's onContextMenu / onKeyDown correctly if it could receive them.
😯 Current Behavior
1. MenuTrigger can't wrap a collection item. MenuTrigger must render as an ancestor of its trigger, but inside a collection it hits the useIsHidden guard and returns null during the collection-building pass, so the item never registers. Wrapping a TreeItem removes every row from the tree — the collection renders empty.
2. Wrapping the row's content in <Pressable> works, but warns once per row. The Pressable child ends up being a div with no interactive ARIA role. The row itself is role="row" (RAC's Tree is a treegrid), which isn't in Pressable's allowed-role list, so there's nothing valid to put the role on:
<Pressable> child must have an interactive ARIA role.
Adding excludeFromTabOrder just swaps it for the other warning:
<Pressable> child must be focusable. Please ensure the tabIndex prop is passed through.
Putting a fake interactive role on a div inside a treegrid row seems worse than the warning, so neither option feels right.
3. Every example uses a standalone <Button>. packages/react-aria-components/stories/Menu.stories.tsx:532, packages/@react-spectrum/s2/stories/Menu.stories.tsx:409, and packages/react-aria-components/test/Menu.test.tsx:652 / :701, plus both docs sites. As a side note, the docs example makes a non-interactive 250×150 region into a <button> purely to satisfy the pressable requirement — it's announced as a button but does nothing when pressed, which reads a little odd as the canonical example.
💁 Possible Solution
useContextMenu works today and looks like the intended escape hatch — contextMenuProps spread straight onto a TreeItem / GridListItem keeps all the platform handling, with no wrapper and no warnings. If that's the recommended pattern for collections, documenting it on the Menu page (or on GridList / Tree / Table) would settle it.
The one thing it loses versus MenuTrigger is positioning the menu at the pointer, which needs getTargetRect — that isn't part of Popover's public types, so it currently needs a @ts-expect-error. Exposing it, or otherwise providing a supported way to anchor a popover to a point, would close that gap.
🔦 Context
We have a collections sidebar built on Tree, where each row has a kebab menu (rename, share, delete, …). We wanted right-click on the row to open that same menu, keeping the kebab button as-is. The kebab stays either way — the context menu is an addition, not a replacement.
We landed on <Pressable> around the row content, which works, but it restructures the row purely to host the trigger and logs a warning per row in development, so it doesn't feel like the intended shape.
💻 Examples
Wrapping the item — collection renders empty:
// Every row disappears: MenuTrigger returns null during the collection build,
// so no TreeItem is ever registered.
<Tree aria-label="Collections">
<MenuTrigger trigger="contextMenu">
<TreeItem textValue="Design">
<TreeItemContent>Design</TreeItemContent>
</TreeItem>
<Popover>
<Menu>
<MenuItem>Rename…</MenuItem>
<MenuItem>Delete…</MenuItem>
</Menu>
</Popover>
</MenuTrigger>
</Tree>
Wrapping the content — works, but warns once per row:
<TreeItem textValue="Design">
<TreeItemContent>
<MenuTrigger trigger="contextMenu">
{/* warns: child must have an interactive ARIA role */}
<Pressable><div className="row-content">Design</div></Pressable>
<Popover>
<Menu>
<MenuItem>Rename…</MenuItem>
<MenuItem>Delete…</MenuItem>
</Menu>
</Popover>
</MenuTrigger>
</TreeItemContent>
</TreeItem>
Possibly related: #7197 (pass onContextMenu in RAC Tab), #7987 (useLongPress with a Table Row — event and labelling props get filtered out), #9863 (right-click a table row containing a link).
Versions: [email protected], [email protected], [email protected], React 19.
🧢 Your Company/Team
Metaview
- 主要言語
- TypeScript
- スター
- 16k
- フォーク
- 1.6k
- 平均マージ
- 3日 9時間
- マージ済み PR(30日)
- 45
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
adobe/react-spectrum のほかの issue
-
accessibility bug PreviewTrigger
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
adobe/react-spectrum#10917 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
S2 quiet Picker menu is always 192px wide: its width calc() has no unit対応中かも @darshpatel1052 が 2 日前に担当しました。 オープンwaiting on author
難易度 2/5 1時間未満 初心者へのやさしさ 88/100
adobe/react-spectrum#10909 · コメント 3 件 ·
メンテナーはふだん 1 日以内に返信
-
DatePicker omits timezone for initially empty fields despite zoned placeholderValue対応中かも @mrlexcoder が 2 日前に担当しました。 オープンbug
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
adobe/react-spectrum#10900 · コメント 2 件 ·
メンテナーはふだん 1 日以内に返信
-
Separator adds a redundant role="separator" to its default <hr>対応中かも @Janani-bn が 3 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
adobe/react-spectrum#10894 · コメント 2 件 ·
メンテナーはふだん 1 日以内に返信
-
ComboBox with selectionMode="multiple": Cmd/Ctrl+A does nothing while the popover is open対応中かも @mangeshraut712 が 9 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
adobe/react-spectrum#10866 ·
メンテナーはふだん 1 日以内に返信
adobe/react-spectrum の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 83/100
appandflow/stim#5258 ·
メンテナーはふだん 1 日以内に返信
-
area/docs framework/hugo help wanted language/markdown
難易度 1/5 1時間未満 初心者へのやさしさ 88/100
メンテナーはふだん 1 日以内に返信
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
難易度 1/5 1時間未満 初心者へのやさしさ 68/100
lingdojo/kana-dojo#33660 · コメント 1 件 · リアクション 5 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
drizzle-team/drizzle-orm#6639 ·
メンテナーはふだん 4 日以内に返信
-
enhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100