TeamSearchField: filterSelectedOptions filters MUI's internal value, not the actual selection
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 137
- Forks
- 239
- Avg merge
- 4d 14h
- Merged PRs (30d)
- 5
Description
Summary
TeamSearchField keeps its real selection in parent state (teamsData) while leaving the MUI Autocomplete uncontrolled, and then relies on the Autocomplete's internal value for filterSelectedOptions. The two disagree, so the filtering is partial and does not survive a refetch.
Not a regression - it is the shape the component is left in after layer5io/sistent#1780 removed a value={inputValue} prop that bound a string to an object-valued Autocomplete. That prop was a type error masked by a file-level @ts-nocheck; removing the suppression forced it out and switched the component from (broken) controlled to uncontrolled, which is what made filterSelectedOptions start working at all. Keeping the now-live filter was a deliberate call - it restores what the author evidently intended and the UX is better. This issue is about finishing the job properly.
The mismatch
Three facts that do not compose:
- The Autocomplete is single-select and uncontrolled. There is no
multipleand novalue. MUI's internal value is therefore the last selected option, not the selection set. - The real selection set lives in the parent, as
teamsData, appended byhandleAddand rendered as Chips outside the Autocomplete. filterSelectedOptionsfilters against fact 1, not fact 2. So at most one team - whichever was picked most recently - is hidden from the dropdown. Every other already-selected team stays listed.
On top of that, isOptionEqualToValue={(option, value) => option === value} is reference equality, and options is replaced wholesale on every fetchSuggestions(...) call (onInputChange, and the useEffect on mount / orgID change). After any refetch the retained value is no longer reference-equal to the new option objects, so even that single team reappears.
Net: the filter applies to one team, sometimes, until the next keystroke. What actually prevents a duplicate selection is the explicit guard in handleAdd, which sets the "Team Already Selected" error.
Why fix it
The component currently works by accident on two levels - correctness is carried by the handleAdd duplicate guard, while the visible filtering is incidental. Anyone who later needs to control the field programmatically (clear it after a successful invite, preselect teams when editing) will reach for value and land straight back on the bug that @ts-nocheck was hiding.
Suggested resolution
Either of these, not both:
- Derive the filter from the real selection. Drop
filterSelectedOptionsand filteroptionsagainstteamsDatadirectly, so every selected team is hidden, deterministically, regardless of refetches. Smallest change, keeps the component uncontrolled. - Make it properly controlled. Give the Autocomplete a correctly-typed object value (or
multiplewithvalue={teamsData}), which makesfilterSelectedOptionsmean what it says and makes programmatic control possible.
In either case replace the reference comparator with an id comparison - (option, value) => option.id === value.id - since options identities are not stable across fetches.
Worth checking InviteUserModal's sibling pickers for the same pattern while in here.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in TeamSearchField by tracing teamsData, handleAdd, filterSelectedOptions, isOptionEqualToValue, and the fetchSuggestions calls. Compare the two suggested approaches and inspect InviteUserModal's sibling pickers for the same pattern. Done means every selected team stays hidden after refetches, comparisons remain correct across replaced option objects, and the existing duplicate guard still works.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100