Skip to content

picker: already picked items are not excluded from search results #4268

Description

@devbymadde

Already picked items are never excluded from a multiple picker's search results, so the same item can be picked twice.

handleSearchResult filters with !values.includes(item) — reference identity — but limel-list emits a copy of the item that was picked ({ ...item, selected: true }), so what ends up in value is never the object the searcher returned. The filter therefore excludes nothing.

Picking the same item twice appends a second entry to the emitted value, which becomes a second chip with the same id. Since limel-chip-set removes chips by id, removing either one removes both.

The filter was added in 2018 (f3acb17a2, "feat(picker): only allow unique values to be selected") and stopped matching when limel-list began emitting a copy in 2019 (df81ce40f), so the intent is documented and this is a regression rather than a new feature.

Worth knowing before fixing

  • Match by value id, not by reference, and use the same comparison the chips use. Comparing value ids directly treats 1 and '1' as different items while the chip ids they produce collide, which reintroduces the duplicate-chip problem from the other direction. Reproduced: value holding {text: 'One', value: '1'} with allItems carrying value: 1
    "One" was still offered, picking it produced two "One" chips, and removing one emitted [].
  • Items with no value id have nothing to match on. Keeping them is probably right — hiding every value-less item as soon as one is picked is worse than offering one twice.
  • The exclusion must run before the default searcher's 20-item cap, or the cap gets spent on items that are then filtered out. A picker with 100 items and 20 picked showed an empty dropdown.
  • A filter that actually removes things can leave a result holding only ListSeparator headers, which renders as section headings with nothing beneath them. getDropdownContent checking items.length is not enough.

Blast radius

This changes the dropdown for every multiple consumer, not only those that filter allItems themselves. A consumer whose searcher deliberately returns picked items in order to render them as selected loses that.

A custom searcher returning a fixed page (top-N, server paging) is the awkward case: a page where everything is already picked filters down to nothing and shows a false "No results matching". The searcher only receives the query, so it cannot compensate. Reproduced with a top-2 page for "ann" where both hits were picked and 23 more matches existed beyond the page.
Either pass the picked ids to the searcher, or offer an opt-out.

Found while reviewing #4223. Reproductions by @john-traas.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions