Skip to content

Define selection constants (select all 1)#13794

Merged
sfc-gh-dmatthews merged 1 commit intodevelopfrom
02-02-multiselect-selection-plumbing
Feb 16, 2026
Merged

Define selection constants (select all 1)#13794
sfc-gh-dmatthews merged 1 commit intodevelopfrom
02-02-multiselect-selection-plumbing

Conversation

@sfc-gh-dmatthews
Copy link
Copy Markdown
Contributor

@sfc-gh-dmatthews sfc-gh-dmatthews commented Feb 3, 2026

Describe your changes

This introduces two constants that will be used to create a select-all feature for st.multiselect. This is not functionally connected and not available to the frontend. This will be implemented in the next PR in the stack.

Screenshot or video (only for visual changes)

GitHub Issue Link (if applicable)

Testing Plan

  • Explanation of why no additional tests are needed
  • Unit Tests (JS and/or Python)
  • E2E Tests
  • Any manual testing needed?

Contribution License Agreement

By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.

@snyk-io
Copy link
Copy Markdown
Contributor

snyk-io bot commented Feb 3, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 3, 2026

✅ PR preview is ready!

Name Link
📦 Wheel file https://core-previews.s3-us-west-2.amazonaws.com/pr-13794/streamlit-1.54.0-py3-none-any.whl
📦 @streamlit/component-v2-lib Download from artifacts
🕹️ Preview app pr-13794.streamlit.app (☁️ Deploy here if not accessible)

@sfc-gh-dmatthews sfc-gh-dmatthews changed the title Define selection constants Define selection constants for select-all feature Feb 3, 2026
@sfc-gh-dmatthews sfc-gh-dmatthews changed the title Define selection constants for select-all feature Define selection constants (select all 1) Feb 3, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 3, 2026

📉 Frontend coverage change detected

The frontend unit test (vitest) coverage has decreased by 0.0600%

  • Current PR: 86.9500% (14013 lines, 1828 missed)
  • Latest develop: 87.0100% (13997 lines, 1817 missed)

💡 Consider adding more unit tests to maintain or improve coverage.

📊 View detailed coverage comparison

@sfc-gh-dmatthews sfc-gh-dmatthews added security-assessment-completed impact:internal PR changes only affect internal code change:feature PR contains new feature or enhancement implementation ai-review If applied to PR or issue will run AI review workflow labels Feb 3, 2026
@github-actions github-actions bot added do-not-merge PR is blocked from merging and removed ai-review If applied to PR or issue will run AI review workflow labels Feb 3, 2026
@sfc-gh-dmatthews sfc-gh-dmatthews added ai-review If applied to PR or issue will run AI review workflow and removed do-not-merge PR is blocked from merging labels Feb 3, 2026
@github-actions github-actions bot removed the ai-review If applied to PR or issue will run AI review workflow label Feb 3, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 3, 2026

Summary

This PR introduces two constants (SELECT_ALL_ID and SELECT_MATCHES_ID) and handler logic for a future "select all" feature in the Multiselect widget. The changes are part of a stacked PR series where this is the foundational plumbing work. The constants define special option IDs that will be used to identify "Select All" and "Select X Matches" dropdown options.

Files changed:

  • frontend/lib/src/components/shared/Dropdown/VirtualDropdown.tsx - Added constant definitions
  • frontend/lib/src/components/shared/Dropdown/index.ts - Exported the new constants
  • frontend/lib/src/components/widgets/Multiselect/Multiselect.tsx - Added handler logic and a ref for filtered matches

Code Quality

The code follows TypeScript and React best practices:

  1. Constants naming: Well-named with double underscore prefix (__SELECT_ALL__, __SELECT_MATCHES__) to clearly indicate internal/special IDs that won't conflict with user data.

  2. Proper exports: Constants are correctly exported through the index barrel file for clean imports.

  3. Handler logic (Multiselect.tsx:148-174): The generateNewState function properly handles the new special IDs with:

    • Correct filtering of unselected values
    • Proper respect for maxSelections limit
    • Clean, readable code with helpful comments
  4. Dependency array update (Multiselect.tsx:184): The useCallback dependency array is correctly updated to include element.maxSelections and element.options.

  5. Ref naming: The new ref follows the naming convention with Ref suffix (selectMatchesRef).

Minor observation: The selectMatchesRef is initialized but never populated in this PR. This is intentional per the PR description ("This will be implemented in the next PR in the stack"), so currently if SELECT_MATCHES_ID were triggered, it would add an empty array. This is acceptable given the stacked PR approach.

Test Coverage

The PR adds functional handler logic in generateNewState but no unit tests are included. While the PR description states "This is not functionally connected and not available to the frontend," the handler logic is present and could be tested independently.

Recommendation: Consider adding unit tests for the new handler logic in Multiselect.test.tsx:

  • Test that SELECT_ALL_ID adds all unselected options
  • Test that SELECT_ALL_ID respects maxSelections limit
  • Test that SELECT_MATCHES_ID uses values from the ref
  • Test that SELECT_MATCHES_ID respects maxSelections limit

Example test structure:

describe("generateNewState with special selection IDs", () => {
  it("selects all unselected options when SELECT_ALL_ID is selected", async () => {
    // Setup and verify all options are selected
  })

  it("respects maxSelections when SELECT_ALL_ID is selected", async () => {
    // Verify only remaining slots are filled
  })
})

However, given this is explicitly part 1 of a multi-PR feature stack and the UI to trigger these IDs isn't exposed yet, deferring tests to subsequent PRs is a reasonable approach.

Backwards Compatibility

No breaking changes. The changes are purely additive:

  • New constants added (no impact on existing code)
  • New ref added (no impact on existing behavior)
  • Handler logic only triggers for specific new IDs that don't exist in current dropdown options
  • Existing generateNewState behavior for "remove", "clear", and regular "select" is unchanged

Security & Risk

No security concerns identified:

  • Constants are client-side only and used for UI logic
  • The double underscore prefix ensures these won't conflict with user-provided option values
  • No user input is directly used in security-sensitive operations

Low regression risk:

  • Changes are isolated to the Multiselect component
  • Existing functionality is not modified
  • The special IDs are not yet exposed through the UI

Recommendations

  1. Consider adding unit tests for the new generateNewState handler logic, even if the full feature isn't connected yet. This ensures the foundational logic is correct before building on top of it. (Optional for this PR, could be addressed in subsequent PRs)

  2. Minor code suggestion: The remainingSlots calculation appears twice with identical logic. Consider extracting to a helper:

const getRemainingSlots = (): number => 
  element.maxSelections > 0 ? element.maxSelections - value.length : Infinity

// Then use: unselectedValues.slice(0, getRemainingSlots())

This is a minor suggestion and not required for approval.

Verdict

APPROVED: The code is well-structured, follows best practices, and correctly implements the foundational plumbing for a select-all feature. The lack of tests is noted but acceptable given this is part of a stacked PR workflow where the feature isn't yet exposed to users. The changes are backwards compatible with no security or regression risks.


This is an automated AI review using opus-4.5-thinking. Please verify the feedback and use your judgment.

@streamlit streamlit deleted a comment from github-actions bot Feb 3, 2026
@sfc-gh-bnisco sfc-gh-bnisco force-pushed the 02-02-multiselect-selection-plumbing branch from 8096ee9 to 7132679 Compare February 3, 2026 17:11
@sfc-gh-dmatthews sfc-gh-dmatthews force-pushed the 02-02-multiselect-selection-plumbing branch from 7132679 to 44abccb Compare February 3, 2026 18:38
@sfc-gh-dmatthews sfc-gh-dmatthews marked this pull request as ready for review February 3, 2026 18:39
Copilot AI review requested due to automatic review settings February 3, 2026 18:39
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces foundational constants and logic for a select-all feature in the multiselect widget. It defines two special option IDs (SELECT_ALL_ID and SELECT_MATCHES_ID) and implements the selection logic for these options within the generateNewState callback, including proper handling of maxSelections limits.

Changes:

  • Defines two constants (SELECT_ALL_ID, SELECT_MATCHES_ID) for special dropdown options
  • Implements selection logic for "select all" and "select matches" functionality with maxSelections support
  • Adds a ref (selectMatchesRef) to store filtered matches for future use
  • Updates dependency array to include element.maxSelections and element.options

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
frontend/lib/src/components/shared/Dropdown/VirtualDropdown.tsx Defines two special constants for select-all option IDs
frontend/lib/src/components/shared/Dropdown/index.ts Exports the new constants alongside VirtualDropdown
frontend/lib/src/components/widgets/Multiselect/Multiselect.tsx Imports constants, adds ref for filtered matches, implements select-all logic in generateNewState with maxSelections handling

Comment on lines +166 to +173

// Respect maxSelections limit
if (element.maxSelections > 0) {
const remainingSlots = element.maxSelections - value.length
return [...value, ...filteredValues.slice(0, remainingSlots)]
}

return [...value, ...filteredValues]
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

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

The logic for SELECT_MATCHES_ID doesn't filter out already-selected values, which could result in duplicate values in the returned array. The filteredValues from the ref should be filtered to exclude values already in the current selection.

Compare with the SELECT_ALL_ID case on lines 150-152, which correctly filters to get only unselected values. The SELECT_MATCHES_ID case should follow the same pattern:

const unselectedMatches = filteredValues.filter(opt => !value.includes(opt))

Then use unselectedMatches instead of filteredValues when building the return value.

Suggested change
// Respect maxSelections limit
if (element.maxSelections > 0) {
const remainingSlots = element.maxSelections - value.length
return [...value, ...filteredValues.slice(0, remainingSlots)]
}
return [...value, ...filteredValues]
const unselectedMatches = filteredValues.filter(
opt => !value.includes(opt)
)
// Respect maxSelections limit
if (element.maxSelections > 0) {
const remainingSlots = element.maxSelections - value.length
return [...value, ...unselectedMatches.slice(0, remainingSlots)]
}
return [...value, ...unselectedMatches]

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This seems like a legitimate bug. Have you had a chance to investigate this @sfc-gh-dmatthews ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think this is just an imperfect separation of the backend and frontend logic. In the next PR, the filtered options are initialized from an already filtered list that excludes selected items.

Comment on lines +156 to +157
const remainingSlots = element.maxSelections - value.length
return [...value, ...unselectedValues.slice(0, remainingSlots)]
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

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

When maxSelections is set and value.length equals maxSelections, remainingSlots will be 0. However, if there's a bug where value.length somehow exceeds maxSelections, remainingSlots becomes negative, and slice(0, negativeNumber) would still return values (slice with negative end index counts from the end of the array).

Consider adding a Math.max guard to ensure remainingSlots is never negative:

const remainingSlots = Math.max(0, element.maxSelections - value.length)

This same issue exists on lines 156 and 169.

Copilot uses AI. Check for mistakes.
@sfc-gh-dmatthews sfc-gh-dmatthews force-pushed the 02-02-multiselect-selection-plumbing branch 2 times, most recently from 93808e8 to 0adf09d Compare February 3, 2026 23:55
@sfc-gh-dmatthews sfc-gh-dmatthews force-pushed the 02-02-multiselect-selection-plumbing branch 10 times, most recently from 7336765 to f184585 Compare February 6, 2026 02:15
@sfc-gh-dmatthews sfc-gh-dmatthews force-pushed the 02-02-multiselect-selection-plumbing branch 5 times, most recently from de31a73 to 904afcb Compare February 13, 2026 06:35
@sfc-gh-dmatthews sfc-gh-dmatthews force-pushed the 02-02-multiselect-selection-plumbing branch from 904afcb to ab76d8e Compare February 15, 2026 04:33
Copy link
Copy Markdown
Contributor Author

sfc-gh-dmatthews commented Feb 16, 2026

Merge activity

  • Feb 16, 9:33 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Feb 16, 9:34 PM UTC: Graphite couldn't merge this PR because it had merge conflicts.
  • Feb 16, 11:04 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Feb 16, 11:05 PM UTC: @sfc-gh-dmatthews merged this pull request with Graphite.

@sfc-gh-dmatthews sfc-gh-dmatthews force-pushed the 02-02-multiselect-selection-plumbing branch from ab76d8e to 9465966 Compare February 16, 2026 21:53
@sfc-gh-dmatthews sfc-gh-dmatthews merged commit 351cfb6 into develop Feb 16, 2026
43 checks passed
@sfc-gh-dmatthews sfc-gh-dmatthews deleted the 02-02-multiselect-selection-plumbing branch February 16, 2026 23:05
lukasmasuch pushed a commit that referenced this pull request Feb 20, 2026
## Describe your changes
This introduces two constants that will be used to create a select-all feature for `st.multiselect`. This is not functionally connected and not available to the frontend. This will be implemented in the next PR in the stack.

## Screenshot or video (only for visual changes)

## GitHub Issue Link (if applicable)

## Testing Plan

- Explanation of why no additional tests are needed
- Unit Tests (JS and/or Python)
- E2E Tests
- Any manual testing needed?

---

**Contribution License Agreement**

By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:feature PR contains new feature or enhancement implementation impact:internal PR changes only affect internal code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants