Define selection constants (select all 1)#13794
Conversation
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
✅ PR preview is ready!
|
📉 Frontend coverage change detectedThe frontend unit test (vitest) coverage has decreased by 0.0600%
💡 Consider adding more unit tests to maintain or improve coverage. |
SummaryThis PR introduces two constants ( Files changed:
Code QualityThe code follows TypeScript and React best practices:
Minor observation: The Test CoverageThe PR adds functional handler logic in Recommendation: Consider adding unit tests for the new handler logic in
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 CompatibilityNo breaking changes. The changes are purely additive:
Security & RiskNo security concerns identified:
Low regression risk:
Recommendations
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. VerdictAPPROVED: 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 |
8096ee9 to
7132679
Compare
7132679 to
44abccb
Compare
There was a problem hiding this comment.
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.maxSelectionsandelement.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 |
|
|
||
| // Respect maxSelections limit | ||
| if (element.maxSelections > 0) { | ||
| const remainingSlots = element.maxSelections - value.length | ||
| return [...value, ...filteredValues.slice(0, remainingSlots)] | ||
| } | ||
|
|
||
| return [...value, ...filteredValues] |
There was a problem hiding this comment.
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.
| // 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] |
There was a problem hiding this comment.
This seems like a legitimate bug. Have you had a chance to investigate this @sfc-gh-dmatthews ?
There was a problem hiding this comment.
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.
| const remainingSlots = element.maxSelections - value.length | ||
| return [...value, ...unselectedValues.slice(0, remainingSlots)] |
There was a problem hiding this comment.
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.
93808e8 to
0adf09d
Compare
7336765 to
f184585
Compare
de31a73 to
904afcb
Compare
904afcb to
ab76d8e
Compare
Merge activity
|
ab76d8e to
9465966
Compare
## 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.

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
Contribution License Agreement
By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.