reactjsreact-bootstrap-typeahead

React-Bootstrap-Typeahead: Using custom input hides selected tokens


I'm trying to use a custom input and I've copied the example from the RBT Rendering example. My main goals are to add a test-id to the input, to remove the hint, and to use a custom component for the input. For some reason, when I select an option from the menu, it does not appear in my custom input like in the example. It's clearly set into state as it disappears from the available options. Do I need to wrap something else around my Form.Control input?

Here's a working sandbox, but to avoid link-rot, I'll paste the component code below as well.

<Typeahead
  id={`${testIdPrefix}-medal`}
  labelKey="medal"
  multiple
  placeholder="Choose up to 2"
  options={[
    { medal: "Gold" },
    { medal: "Silver" },
    { medal: "Bronze" },
    { medal: "Tin" }
  ]}
  selected={multiSelections}
  onChange={(selections) => setMultiSelections(selections.slice(-2))}
  renderInput={({ inputRef, referenceElementRef, ...inputProps }) => (
    <Form.Control
      test-id={`${testIdPrefix}-medal`}
      {...inputProps}
      ref={(node) => {
        inputRef(node);
        referenceElementRef(node);
      }}
    />
  )}
  renderToken={(option, { onRemove }, index) => (
    <Token
      key={index}
      onRemove={onRemove}
      option={option}
      className="medal-token"
    >
      {`${option.medal}`}
    </Token>
  )}
/>

To reproduce:

Note: this also happens without the custom Token but I included it in case that's relevant to the solution. (I need the solution to work with a custom token.)


Solution

  • Yes, multiple={true} is the issue. renderToken is a hook into the built-in multi-selection component to customize token rendering, but won't be called if you use renderInput. In that case, you need to handle rendering the selections as tokens yourself.

    As a quick example/test, you should be able to simply render the selections below your input in renderInput:

    renderInput={({ inputRef, referenceElementRef, onRemove, selected ...inputProps }) => (
      <>
        <Form.Control
          test-id={`${testIdPrefix}-medal`}
          {...inputProps}
          ref={(node) => {
            inputRef(node);
            referenceElementRef(node);
          }}
        />
        {selected.map((option, idx) => (
          <Token
            key={idx}
            onRemove={onRemove}
            option={option}
            className="medal-token"
          >
            {option.medal}
          </Token>
        ))}
      </>
    )}