feat(select): multiselect tag handling improvements, story updates, visual fixes #587#613
feat(select): multiselect tag handling improvements, story updates, visual fixes #587#613
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughAdds bulk-selection utilities and UI (global and per-group), single-row multiselect tag measurement/overflow with contextual hiding, keyboard-mode focus behavior, new single-value/tag helpers and contexts, expanded stories/tests, and related styling updates. Changes
Sequence DiagramssequenceDiagram
participant User
participant SelectMenuList
participant BulkHelpers
participant SelectRoot
User->>SelectMenuList: Click "Select all" checkbox
SelectMenuList->>BulkHelpers: toggleBulkSelection(currentValue, enabledOptions)
BulkHelpers-->>SelectMenuList: newSelection
SelectMenuList->>SelectRoot: props.setValue(newSelection, "select-option" or "deselect-option")
SelectRoot-->>User: UI updates with new selection
sequenceDiagram
participant Browser
participant SelectValueContainer
participant DOM
participant SelectTagsContext
participant SelectMultiValue
Browser->>SelectValueContainer: Mount or selection changes
SelectValueContainer->>DOM: render tags + ref
SelectValueContainer->>DOM: measure child widths (layout effect / ResizeObserver)
DOM-->>SelectValueContainer: measured widths
SelectValueContainer->>SelectValueContainer: compute visibleCount & hiddenCount
SelectValueContainer->>SelectTagsContext: provide {isSingleRow, visibleCount}
SelectMultiValue->>SelectTagsContext: useSelectTagsContext()
SelectMultiValue-->>Browser: render or hide tag based on index vs visibleCount
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 0/1 reviews remaining, refill in 60 minutes.Comment |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/tedi/components/form/select/components/select-multi-value.tsx (1)
40-63:⚠️ Potential issue | 🟡 MinorMissing
displayNameon exported component.Per coding guidelines, all exported components must have a
displayNameproperty set.Proposed fix
export const SelectMultiValue = ({ isTagRemovable, children, removeProps, ...props }: MultiValueType): JSX.Element | null => { // ... component body }; + +SelectMultiValue.displayName = 'SelectMultiValue';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/tedi/components/form/select/components/select-multi-value.tsx` around lines 40 - 63, The exported React component SelectMultiValue is missing a displayName; add a static displayName property to the component after its declaration (e.g., set SelectMultiValue.displayName = 'SelectMultiValue') so the exported symbol SelectMultiValue has a readable displayName for debugging and tooling.
🧹 Nitpick comments (2)
src/tedi/components/form/select/components/select-group-heading.tsx (1)
15-62: Set adisplayNamefor this exported component.This component is exported from
src/tedi/components, so it should declaredisplayNameconsistently with the TEDI component conventions.As per coding guidelines, "All exported components must have a `displayName` property set".Suggested change
export const SelectGroupHeading = ({ optionGroupHeadingText, ...props }: GroupHeadingType): ReactElement => { const textSettings = props.data.text || optionGroupHeadingText; ... }; + +SelectGroupHeading.displayName = 'SelectGroupHeading';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/tedi/components/form/select/components/select-group-heading.tsx` around lines 15 - 62, The exported React component SelectGroupHeading is missing a displayName; add a displayName assignment after the component declaration (e.g., set SelectGroupHeading.displayName = 'SelectGroupHeading' or the TEDI naming convention used elsewhere) so the exported symbol has a declared displayName property consistent with other TEDI components; ensure this is added in the same file immediately after the SelectGroupHeading declaration.src/tedi/components/form/select/select.spec.tsx (1)
568-616: Consider extractingSelectMultiValueRemovetests to a dedicated spec file.These unit tests for
SelectMultiValueRemoveare nested inside theSelectdescribe block, but they test the component in isolation. Moving them toselect-multi-value-remove.spec.tsxwould improve test organization and align with the project's convention of placing tests incomponent-name.spec.tsxfiles.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/tedi/components/form/select/select.spec.tsx` around lines 568 - 616, Extract the SelectMultiValueRemove block into its own spec file: create select-multi-value-remove.spec.tsx and move the entire describe('SelectMultiValueRemove', ...) suite (including the helper renderRemove and all its tests) out of the larger Select spec; ensure the new file imports React, render, fireEvent, jest utilities, and the SelectMultiValueRemove component, and remove the duplicated tests from the original select.spec.tsx so only the isolated suite remains in the new file (keep the helper name renderRemove and test assertions unchanged).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/tedi/components/form/select/components/select-bulk-helpers.ts`:
- Around line 29-35: getGroupEnabledOptions currently looks up the group by
label which breaks when multiple GroupBase<ISelectOption> share the same label;
change the helper (getGroupEnabledOptions) to accept the group object or its
options array directly (e.g., signature accepting GroupBase<ISelectOption> or
ISelectOption[] instead of groupLabel) and filter that array for non-disabled
items, then update callers to pass the current group (or group.options) rather
than the rendered label; keep the isGroupedOptions check only where callers
still need to guard for grouped vs flat options.
In `@src/tedi/components/form/select/components/select-group-heading.tsx`:
- Around line 42-55: The wrapper div and the Checkbox both call handleToggle,
causing duplicate toggles because clicks bubble; to fix, stop the click/change
event from bubbling when the Checkbox handles the toggle: change the Checkbox
onChange prop to a handler that calls event.stopPropagation() then invokes
handleToggle (e.g., onChange={(e) => { e.stopPropagation(); handleToggle(); }}),
ensuring the wrapper's onClick can remain for clicks outside the actual input
while checkbox activations only trigger one toggle; reference Checkbox and
handleToggle in your change.
- Around line 19-37: Replace direct usage of selectProps.value and
selectProps.onChange with react-select helpers: read current selection with
props.getValue() and update with props.setValue(...) inside handleToggle (use
same bulk-selection logic: getGroupEnabledOptions, toggleBulkSelection,
areAllSelected, isIndeterminate). Remove the wrapper element's onClick so only
the Checkbox's onChange calls handleToggle to avoid double firing. Ensure
handleToggle uses props.setValue to apply the new value and uses
props.getValue() to compute selected; keep references to selectableGroups,
isMulti and options as needed. Finally, add a displayName property to the
exported component to match the codebase convention.
In `@src/tedi/components/form/select/components/select-menu-list.tsx`:
- Around line 14-74: Add a displayName to the exported React component
SelectMenuList to satisfy the coding guideline; locate the export const
SelectMenuList declaration and, immediately after the component definition, set
its displayName property (e.g., SelectMenuList.displayName = 'SelectMenuList')
so the component has a readable name in devtools and warnings.
- Around line 48-65: The wrapper div and the Checkbox both call handleSelectAll
causing a double-toggle; fix by preventing duplicate invocation: either remove
the Checkbox prop onChange={handleSelectAll} so only the div's onClick invokes
handleSelectAll, or keep the Checkbox onChange and modify its handler to stop
propagation (e.g., call event.stopPropagation() before invoking handleSelectAll)
so the div's onClick won't also run; update references around the JSX containing
the div with class tedi-select__select-all, the Checkbox component and the
handleSelectAll function accordingly.
In `@src/tedi/components/form/select/components/select-value-container.tsx`:
- Around line 17-110: The exported React component SelectValueContainer is
missing a displayName; add a displayName property after its declaration like
SelectValueContainer.displayName = 'SelectValueContainer' so the component
follows the guideline and shows a readable name in React DevTools and error
messages; place this statement directly below the SelectValueContainer function
export in the same file.
- Around line 92-97: Exported component SelectValueContainer is missing a
displayName; add SelectValueContainer.displayName = 'SelectValueContainer' after
the component definition/export to satisfy the coding guideline. Locate the
SelectValueContainer component (references: SelectValueContainer, containerRef,
innerProps) and append the displayName assignment before exporting or
immediately after its declaration.
---
Outside diff comments:
In `@src/tedi/components/form/select/components/select-multi-value.tsx`:
- Around line 40-63: The exported React component SelectMultiValue is missing a
displayName; add a static displayName property to the component after its
declaration (e.g., set SelectMultiValue.displayName = 'SelectMultiValue') so the
exported symbol SelectMultiValue has a readable displayName for debugging and
tooling.
---
Nitpick comments:
In `@src/tedi/components/form/select/components/select-group-heading.tsx`:
- Around line 15-62: The exported React component SelectGroupHeading is missing
a displayName; add a displayName assignment after the component declaration
(e.g., set SelectGroupHeading.displayName = 'SelectGroupHeading' or the TEDI
naming convention used elsewhere) so the exported symbol has a declared
displayName property consistent with other TEDI components; ensure this is added
in the same file immediately after the SelectGroupHeading declaration.
In `@src/tedi/components/form/select/select.spec.tsx`:
- Around line 568-616: Extract the SelectMultiValueRemove block into its own
spec file: create select-multi-value-remove.spec.tsx and move the entire
describe('SelectMultiValueRemove', ...) suite (including the helper renderRemove
and all its tests) out of the larger Select spec; ensure the new file imports
React, render, fireEvent, jest utilities, and the SelectMultiValueRemove
component, and remove the duplicated tests from the original select.spec.tsx so
only the isolated suite remains in the new file (keep the helper name
renderRemove and test assertions unchanged).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 1e0beb85-d491-413a-b7ea-3ab609e97161
📒 Files selected for processing (14)
src/tedi/components/form/select/components/select-bulk-helpers.spec.tssrc/tedi/components/form/select/components/select-bulk-helpers.tssrc/tedi/components/form/select/components/select-group-heading.tsxsrc/tedi/components/form/select/components/select-menu-list.tsxsrc/tedi/components/form/select/components/select-multi-value.spec.tssrc/tedi/components/form/select/components/select-multi-value.tsxsrc/tedi/components/form/select/components/select-single-value.tsxsrc/tedi/components/form/select/components/select-tags-context.tssrc/tedi/components/form/select/components/select-value-container.tsxsrc/tedi/components/form/select/select.module.scsssrc/tedi/components/form/select/select.spec.tsxsrc/tedi/components/form/select/select.stories.tsxsrc/tedi/components/form/select/select.tsxsrc/tedi/providers/label-provider/labels-map.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/tedi/components/form/select/components/select-value-container.tsx (1)
55-57: Consider deriving the class selector fromprops.selectProps.classNamePrefixfor robustness.Currently, the selector
.select__input-containerat lines 55-57 assumesclassNamePrefixis"select". While this is hardcoded in the Select component today, deriving the selector fromprops.selectProps.classNamePrefixwould make the code resilient to future changes. SinceselectPropsis already available via the component's props, this is a straightforward defensive improvement.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/tedi/components/form/select/components/select-value-container.tsx` around lines 55 - 57, The code currently queries '.select__input-container' assuming classNamePrefix "select"; update the selector construction to derive the prefix from props.selectProps.classNamePrefix (falling back to "select" if undefined) before calling container.querySelector, and use that computed prefix to build the input container selector (the code around inputEl / inputMin / available in select-value-container.tsx). Ensure you still parse computed minWidth and compute available the same way after changing the selector.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/tedi/components/form/select/components/select-group-heading.tsx`:
- Around line 55-57: The generated checkbox id currently concatenates
props.selectProps.instanceId with props.data.label which can collide when labels
repeat; change the id generation in the SelectGroupHeading component to use a
stable unique group identifier (e.g., props.data.id or props.data.value)
combined with props.selectProps.instanceId (and as a fallback use the group
index or a short generated uid) instead of props.data.label, and ensure the
matching label's htmlFor/for attribute uses that same id so the input/label
association remains correct.
---
Nitpick comments:
In `@src/tedi/components/form/select/components/select-value-container.tsx`:
- Around line 55-57: The code currently queries '.select__input-container'
assuming classNamePrefix "select"; update the selector construction to derive
the prefix from props.selectProps.classNamePrefix (falling back to "select" if
undefined) before calling container.querySelector, and use that computed prefix
to build the input container selector (the code around inputEl / inputMin /
available in select-value-container.tsx). Ensure you still parse computed
minWidth and compute available the same way after changing the selector.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 3bcc1204-21f4-4460-aa63-a60c854d0a1e
📒 Files selected for processing (9)
src/tedi/components/form/select/components/select-bulk-helpers.spec.tssrc/tedi/components/form/select/components/select-bulk-helpers.tssrc/tedi/components/form/select/components/select-group-bulk-context.tssrc/tedi/components/form/select/components/select-group-heading.tsxsrc/tedi/components/form/select/components/select-group.tsxsrc/tedi/components/form/select/components/select-menu-list.tsxsrc/tedi/components/form/select/components/select-single-option.tsxsrc/tedi/components/form/select/components/select-value-container.tsxsrc/tedi/components/form/select/select.module.scss
✅ Files skipped from review due to trivial changes (1)
- src/tedi/components/form/select/components/select-bulk-helpers.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/tedi/components/form/select/select.module.scss
ly-tempel-bitweb
left a comment
There was a problem hiding this comment.
Please check the dark mode as well.
| export const Examples: Story = { | ||
| render: () => ( | ||
| <VerticalSpacing> | ||
| <Select |
There was a problem hiding this comment.
- Second option is always focused when opening select and the focus ring is only around checkbox when opening it for the first time. Shouldn't the focus be on the first option when nothing is selected and otherwise on the first selected item?
- "Select all" becomes unselected when all options are selected.
Screen.Recording.2026-05-05.at.12.59.18.mov
- It's not possible to move to "Select all" option with arrows.
Screen.Recording.2026-05-05.at.13.01.10.mov
There was a problem hiding this comment.
This focus being visible only after the second opening seems to be more general for Select component.
| isClearable={false} | ||
| isSearchable={false} | ||
| /> | ||
| <Select |
There was a problem hiding this comment.
In Angular, for Searchable select "Enter" has to be used to choose an option, with "Space" it writes to the input.
In React, it's possible to use "Space" for selecting when input is empty or predefined value is selected, otherwise it writes to the input as well.
I don't know, which behavior is correct, though. 😄
| /> | ||
| <Select | ||
| id="examples-grouped-selectable" | ||
| label="Grouped multiselect with selectable groups" |
There was a problem hiding this comment.
Selectable groups are not accessible with arrows.
| renderOption={renderHorizontalMetaOption} | ||
| isClearable={false} | ||
| /> | ||
| <Select |
| showRadioButtons | ||
| isClearable={false} | ||
| /> | ||
| <Select |
There was a problem hiding this comment.
Is it intentional that this example doesn't have checkboxes unlike Angular example?
| placeholder="Text value" | ||
| isClearable={false} | ||
| /> | ||
| <Select |
There was a problem hiding this comment.
Is this intended behavior?
Screen.Recording.2026-05-05.at.13.18.10.mov
There was a problem hiding this comment.
Also, in React it's possible to delete values with Backspace, but in Angular it's not.
| }, | ||
| }; | ||
|
|
||
| export const AsyncSelect: Story = { |
| ), | ||
| }; | ||
|
|
||
| export const States: Story = { |
| isTagRemovable | ||
| isClearable | ||
| /> | ||
| <Select |
There was a problem hiding this comment.
The first selected options aren't always fully visible.



Summary by CodeRabbit
New Features
Behavior
Style
Documentation
Tests
Labels