Skip to content

feat: support selectionMode="replace" in grid collection test utils #8028

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Jun 4, 2025

Conversation

LFDanLu
Copy link
Member

@LFDanLu LFDanLu commented Apr 2, 2025

Closes #7884

✅ Pull Request Checklist:

  • Included link to corresponding React Spectrum GitHub Issue.
  • Added/updated unit tests and storybook for this change (for new code or code which already has tests).
  • Filled out test instructions.
  • Updated documentation (if it already exists for this component).
  • Looked at the Accessibility Practices for this feature - Aria Practices

📝 Test Instructions:

🧢 Your Project:

@LFDanLu LFDanLu changed the base branch from main to test_util_bug_fixes April 2, 2025 22:31
@rspbot
Copy link

rspbot commented Apr 3, 2025

@LFDanLu LFDanLu changed the title feat: (WIP) enhance selectionMode="replace" support in grid collection test utils feat: support selectionMode="replace" in grid collection test utils Apr 3, 2025
Comment on lines 107 to 108
// TODO: perhaps this should just be shouldUseModifierKeys?
selectionBehavior = 'toggle'
Copy link
Member Author

Choose a reason for hiding this comment

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

This API naming feels unclear TBH, people may be confused as to when they would apply it. When set to selectionBehavior="replace" this causes the alt/meta key to be used when keyboard navigating/pressing to select a row so that it doesn't cause your previous selection to be replaced on focus/click. I wasn't certain if something like shouldUseModifierKeys was clear either since a user may not know what the "modifier keys" even do

Copy link
Member

Choose a reason for hiding this comment

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

I think selectionBehavior is fine or gridListSelectionBehavior if you want to be clear it should match, then the user profiles/interactions should handle most things behind the scenes

@LFDanLu LFDanLu marked this pull request as ready for review April 8, 2025 17:05
Base automatically changed from test_util_bug_fixes to main April 8, 2025 21:31
@rspbot
Copy link

rspbot commented Apr 14, 2025

Comment on lines 107 to 108
// TODO: perhaps this should just be shouldUseModifierKeys?
selectionBehavior = 'toggle'
Copy link
Member

Choose a reason for hiding this comment

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

I think selectionBehavior is fine or gridListSelectionBehavior if you want to be clear it should match, then the user profiles/interactions should handle most things behind the scenes

@rspbot
Copy link

rspbot commented May 19, 2025

@@ -140,7 +159,14 @@ export class TreeTester {
// Note that long press interactions with rows is strictly touch only for grid rows
await triggerLongPress({element: cell, advanceTimer: this._advanceTimer, pointerOpts: {pointerType: 'touch'}});
} else {
await pressElement(this.user, cell, interactionType);
// TODO add modifiers here? Maybe move into pressElement if we get more cases for different types of modifier keys
Copy link
Member

Choose a reason for hiding this comment

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

i don't quite follow this todo

Copy link
Member Author

Choose a reason for hiding this comment

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

this was more of a musing around perhaps consolidating all of these "trigger a press while holding a modifier key" into pressElement itself if it turns out that we have a lot of these kinds of interactions in our components

};

/**
* Toggles the selection for the specified tree row. Defaults to using the interaction type set on the tree tester.
* Note that this will endevor to always add/remove JUST the provided row to the set of selected rows.
Copy link
Member

Choose a reason for hiding this comment

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

maybe an option shouldExpandCurrentSelection?: boolean

Copy link
Member Author

Choose a reason for hiding this comment

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

for now I want to avoid that because having shouldExpandCurrentSelection=false in a multiselect tree that has selectionBehavior='toggle' would mean the user would expect all rows to be unselected (via click/keyboard/etc) and then their target row toggled on which adds to the complexity here, especially for a virtualized case.

@@ -137,7 +137,7 @@ export class TabsTester {
}

if (interactionType === 'keyboard') {
if (document.activeElement !== this._tablist || !this._tablist.contains(document.activeElement)) {
if (document.activeElement !== this._tablist && !this._tablist.contains(document.activeElement)) {
Copy link
Member

Choose a reason for hiding this comment

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

instead of calling focus if it's not on the tablist, perhaps we should be throwing an error sayings, "navigate to the tablist"?
I know you didn't change that behaviour here, so more of a future topic
I'm just worried about the case that came up recently where calling focus directly was problematic because it didn't follow user interactions closely enough, skipped some steps, which caused the wrong state in the UI

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah its a bit tricky, trying to balance usability (forcing the user to simulate all the interactions required to go from where ever they are in their test to the target TabList) vs realism. I think I'd lean towards the latter to avoid the issues you mentioned but will see about do that in a follow up.

@@ -137,7 +136,7 @@ export class TreeTester {
// this would be better than the check to do nothing in events.ts
// also, it'd be good to be able to trigger selection on the row instead of having to go to the checkbox directly
if (interactionType === 'keyboard' && (!checkboxSelection || !rowCheckbox)) {
await this.keyboardNavigateToRow({row, useAltKey: selectionBehavior === 'replace'});
await this.keyboardNavigateToRow({row, avoidSelectionOnNav: selectionBehavior === 'replace'});
Copy link
Member

Choose a reason for hiding this comment

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

"avoid" feels a bit, "i'll try my best, but it might happen" to me, maybe one of these instead?
noSelectionOnNav
shouldNotSelectOnNav

Copy link
Member Author

Choose a reason for hiding this comment

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

haha, I was thinking about exactly this when naming the option. I actually did want to give that kind of impression since using noSelectionOnNav or shouldNotSelectOnNav would mean it would sound like selection SHOULD change on nav if those were false, something which wouldn't happen for a collection with selectionBehavior: 'toggle' without me firing Enter after every ArrowUp/Down.

avoidSelectionOnNav is more of the following: if true we endeavor to avoid selection on keyboard navigation via the Alt modifier, otherwise if false we won't do anything special when keyboard navigating and if something gets selected as you keyboard navigate that is expected due to the selectionBehavior of your collection

Copy link
Member

Choose a reason for hiding this comment

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

hrmmmmm..... sounds tricky to document. maybe instead of a boolean, it should be
selectionOnNav: 'default' | 'none' and it defaults to 'default'

Copy link
Member Author

Choose a reason for hiding this comment

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

that sounds good, will update

@rspbot
Copy link

rspbot commented May 19, 2025

@rspbot
Copy link

rspbot commented May 23, 2025

snowystinger
snowystinger previously approved these changes Jun 2, 2025
Comment on lines +168 to +169
// Issue with how we don't render the contextual help button in the fake DOM since PressResponder isn't using createHideableComponent
let warn = jest.spyOn(global.console, 'warn').mockImplementation();
Copy link
Member Author

Choose a reason for hiding this comment

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

Gross that this is a thing, I remember discussing this last month or so but don't think we landed on anything...

Copy link
Member Author

Choose a reason for hiding this comment

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

to be clear, it warns that the PressResponder doesn't have a button in the fake DOM

Copy link
Member

Choose a reason for hiding this comment

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

bleh, we'll need to do something about this, it also cropped up in #8151

@rspbot
Copy link

rspbot commented Jun 4, 2025

Build successful! 🎉

@LFDanLu LFDanLu added this pull request to the merge queue Jun 4, 2025
Merged via the queue into main with commit 21d1950 Jun 4, 2025
30 checks passed
@LFDanLu LFDanLu deleted the highlight_selection_test_util_support branch June 4, 2025 18:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Update Tree/Table/etc test utils to handle selectionBehavior="replace"
4 participants