How to use Tab instead of Enter in Downshift to select current menu item?

2.2k views Asked by At

This is regarding:

https://github.com/paypal/downshift

I set the onKeyDown handler to recognize Tab key press and I am able to get the highlightedIndex value correctly but how to use that to change the inputValue?

import React from 'react'
import Downshift from 'downshift'
import {
  Label,
  Menu,
  ControllerButton,
  Input,
  Item,
  ArrowIcon,
  XIcon,
  css,
  itemToString,
  getItems,
} from '../shared'

class App extends React.Component {
  render() {
    return (
      <div
        {...css({
          display: 'flex',
          flexDirection: 'column',
          marginTop: 50,
        })}
      >
        <Downshift
          onChange={selection =>
            alert(
              selection
                ? `You selected ${itemToString(selection)}`
                : 'selection cleared',
            )
          }
          itemToString={itemToString}
        >
          {({
            getLabelProps,
            getInputProps,
            getToggleButtonProps,
            getMenuProps,
            getItemProps,
            isOpen,
            clearSelection,
            selectedItem,
            inputValue,
            highlightedIndex,
          }) => (
            <div {...css({width: 250, margin: 'auto'})}>
              <Label {...getLabelProps()}>Find a Star Wars character</Label>
              <div {...css({position: 'relative'})}>
                <Input
                  {...getInputProps({
                    isOpen,
                    placeholder: 'Enter a name',
                    **onKeyDown: event => {
                      console.log(event.key)
                      if (event.key === 'Tab') {
                        console.log(highlightedIndex)
                      }
                    },**
                  })}
                />
                {selectedItem ? (
                  <ControllerButton
                    onClick={clearSelection}
                    aria-label="clear selection"
                  >
                    <XIcon />
                  </ControllerButton>
                ) : (
                  <ControllerButton {...getToggleButtonProps()}>
                    <ArrowIcon isOpen={isOpen} />
                  </ControllerButton>
                )}
              </div>
              <div {...css({position: 'relative'})}>
                <Menu {...getMenuProps({isOpen})}>
                  {isOpen
                    ? getItems(inputValue).map((item, index) => (
                        <Item
                          key={item.id}
                          {...getItemProps({
                            item,
                            index,
                            isActive: highlightedIndex === index,
                            isSelected: selectedItem === item,
                          })}
                        >
                          {itemToString(item)}
                        </Item>
                      ))
                    : null}
                </Menu>
              </div>
            </div>
          )}
        </Downshift>
      </div>
    )
  }
}

export default App
1

There are 1 answers

0
Steve On

I have found that the stateReducer helps in this situation.

stateReducer={(_, changes) => {
    if (changes.type === Downshift.stateChangeTypes.blurInput) {
        return {
            ...changes,
            inputValue: your value,
            isOpen: false
        }
    }

    return changes
}}

paired with the inputs getInputProps.onKeyDown event

<Input {...getInputProps({
    onKeyDown: event => {
        switch (event.key) {
            case 'Tab': {
                // use the highlighted or first value
                // update the state
                break;
            }
            default:
                break;
        }
    }
})} />