Skip to content

Keep a newly-typed value in a multiple select using a key_generator - #6198

Open
evnchn wants to merge 4 commits into
zauberzeug:mainfrom
evnchn:midnight/select-108
Open

Keep a newly-typed value in a multiple select using a key_generator#6198
evnchn wants to merge 4 commits into
zauberzeug:mainfrom
evnchn:midnight/select-108

Conversation

@evnchn

@evnchn evnchn commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Motivation

ui.select({'a':'A'}, multiple=True, new_value_mode='add', key_generator=itertools.count(100)); user selects 'a', then types 'Banana' and presses Enter. QSelect emits [{'value':0,'label':'A'}, 'Banana']. The new option 100:'Banana' is added to options and appears in the dropdown, but the resulting value stays ['a'] -- the just-created chip vanishes from the selection instead of becoming ['a', 100].

Minimal reproduction (nicegui/elements/select.py:108) — the exact test/script that was run to reproduce it:

import itertools

from nicegui import ui
from nicegui.testing import User


def _model_value_listener(select):
    """The id of the element's 'update:model-value' listener (the one the client fires)."""
    return next(l.id for l in select._event_listeners.values() if l.type == 'update:modelValue')


async def test_multiple_select_drops_new_value_with_key_generator(user: User) -> None:
    """QSelect(multiple, new_value_mode='add', dict options + key_generator):
    selecting an existing option AND typing a NEW value adds the option but drops it from the value."""
    single = multiple = None

    @ui.page('/')
    def page():
        nonlocal single, multiple
        single = ui.select({'a': 'A'}, multiple=False, new_value_mode='add', key_generator=itertools.count(100))
        multiple = ui.select({'a': 'A'}, multiple=True, new_value_mode='add', key_generator=itertools.count(200))

    await user.open('/')

    # --- single-select control (WORKS): QSelect emits the raw typed string ---
    single._handle_event({'listener_id': _model_value_listener(single), 'args': 'Banana'})
    assert single.options == {'a': 'A', 100: 'Banana'}
    assert single.value == 100  # new key becomes the value -- correct

    # --- multiple-select (BROKEN): QSelect emits [existing-as-dict, new-as-str] ---
    multiple._handle_event({'listener_id': _model_value_listener(multiple),
                            'args': [{'value': 0, 'label': 'A'}, 'Banana']})
    assert multiple.options == {'a': 'A', 200: 'Banana'}  # option IS added (visible in dropdown)
    # BUG: value is ['a'] -- the just-created chip vanished. Should be ['a', 200].
    assert multiple.value == ['a', 200], f"new value dropped: got {multiple.value!r}, expected ['a', 200]"

F [100%]

Implementation

One-line fix in Select._event_args_to_value multiple branch: capture _handle_new_value's returned key into args[i] (mirrors single-select branch), so a newly-typed value with dict options + key_generator is kept in the selection.

Verification
  • Fail-first A/B verified: FAILS on origin/main (['a'] != ['a',200]), PASSES with fix.
  • Local gates: pre-commit · mypy ./nicegui · pylint 10.00/10 · pytest (touched) — all green.

Progress

  • The PR title is a short phrase starting with a verb.
  • The implementation is complete.
  • This PR does not address a security issue.
  • Pytests have been added/updated.
  • Documentation is not necessary.
  • No breaking changes to the public API.

evnchn and others added 2 commits July 25, 2026 00:14
DateInput applied .props('no-parent-event') to the inner QDate, but
Quasar 2.18.5's QDate has no such prop (noParentEvent belongs to the
anchor-props mixin behind QMenu/QTooltip). It was an inert no-op
attribute; removing it changes no behavior (open/close is driven by the
parent QMenu).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
In a multiple-select with dict options and a key_generator, `_handle_new_value`
generates and stores the new option under a fresh key but its return value was
discarded, so the raw typed string (not the key) stayed in the resulting value
and got filtered out. Capture the generated key into `args`, mirroring the
single-select branch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@evnchn evnchn added bug Type/scope: Incorrect behavior in existing functionality review Status: PR is open and needs review labels Jul 26, 2026
@falkoschindler falkoschindler self-assigned this Jul 26, 2026
@falkoschindler
falkoschindler self-requested a review July 26, 2026 20:37
falkoschindler and others added 2 commits July 28, 2026 22:23
The test now types a new value into a multiple select and checks what the
element ends up holding, mirroring `test_id_generator` above it. Reverting
the fix makes it fail on the symptom: the typed value is dropped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Type/scope: Incorrect behavior in existing functionality review Status: PR is open and needs review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants