Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto-highlight only option #77

Closed
gugurete opened this issue Sep 7, 2016 · 11 comments
Closed

Auto-highlight only option #77

gugurete opened this issue Sep 7, 2016 · 11 comments

Comments

@gugurete
Copy link

gugurete commented Sep 7, 2016

It would be handy to select the first value in the dropdown on pressing ENTER even if that value is not selected

  case _keyCode.RETURN:
    // Prevent submitting forms.
    e.preventDefault();

    if (this.state.showMenu) {
      var selected = options[activeIndex] || options[0];
      selected && this._handleAddOption(selected);
    }
    break;
@ericgio
Copy link
Owner

ericgio commented Sep 8, 2016

This is probably not something I'd want to add, for a couple reasons.

First, in the single-selection case, this functionality essentially already exists via hinting and pressing either tab or the right arrow key to autocomplete.

Second, I don't think it's common or expected behavior in a typeahead. Typically, hitting 'enter' when partial text has been typed would trigger an operation based on that text alone. For example, when I do the following Google search, hitting 'enter' will search for 'where' not 'where am i':

image

Furthermore, this component is based on Twitter's Typeahead.js, which is in turn based on their own search functionality and behavior. Given that the functionality you describe is not reflected in the library, I take it as a further sign that it's not canonical behavior.

The one case where I could see this potentially being valid behavior is if the allowNew prop is set, since any text entered is implicitly a valid entry. In any case, I'll keep this issue open for now to see if anyone else comments with use cases or opinions.

@gugurete
Copy link
Author

gugurete commented Sep 8, 2016

Fair point, it should not be part of the standard behaviour.
Maybe exposing the event handling functions would make it easier for users of the component to implement custom behavior ? In my use case I replaced the whole _handleKeydown function:
typeahead.getInstance()._handleKeydown = function() {...}.
But that is accessing private internals of react-bootstrap-typeahead :-)

@ericgio
Copy link
Owner

ericgio commented Sep 8, 2016

I'm starting to look at ways I might pull some of these behaviors out into higher-order components to separate them from the rendering piece. This could potentially provide a way to expose a public API for customizing behaviors. Probably not something that's coming in the immediate future, though.

@daschi
Copy link

daschi commented Sep 28, 2016

What are your thoughts on whether it's canonical behavior to hit the ENTER key to select a value when it is the last option in the list? Would it be possible to implement that with the current api?

@ericgio
Copy link
Owner

ericgio commented Sep 28, 2016

It seems reasonable to auto-highlight a selection if it's the only option, which would then allow the user to hit enter to select it.

@daschi
Copy link

daschi commented Oct 3, 2016

In case it's helpful to anyone or if anyone has a better idea, here's how we implemented that:

$('.bootstrap-typeahead-input-main').keydown(function(event) {
      let menuLength = $('.bootstrap-typeahead-menu > li').length
      if(event.keyCode == 13 && menuLength == 1) {
        $('.bootstrap-typeahead-menu li:first-child > a').first()[0].click()
      }
})

@o-evin
Copy link

o-evin commented Oct 28, 2016

+1 for that feature. This is quite common functionality for autocomplete controls. Since there is no customizable autocomplete tokenizer I found yet, I think that would be nice to have some boolean property 'autocomplete' which enables first option selection on enter pressed. You can find similar behavior when selecting users from predefined list. For example while sharing some note in https://keep.google.com.

@ericgio ericgio changed the title select on RETURN Auto-highlight first option Dec 12, 2016
@mvolkmann
Copy link

mvolkmann commented Jan 6, 2017

Thanks @daschi, that was very helpful!
Here's a similar implementation that doesn't require jQuery.

  fixTypeahead() {
    const {container} = this;
    if (!container) return;

    const input = container.querySelector('input');
    if (input.onkeydown) return; // don't need to set this up again

    input.onkeydown = event => {
      if (event.keyCode !== 13) return;
      const items = container.querySelectorAll('.bootstrap-typeahead-menu > li');
      if (items.length === 1) items[0].querySelector('a').click();
    };
  }

I call this from componentDidMount and componentDidUpdate.

I wrap my Typeahead in a div like this:

<div ref={saveTypeahead}> ... </div>

And here is saveTypeahead:

const saveTypeahead = c => this.container = c;

@sorahn
Copy link

sorahn commented Mar 30, 2017

@mvolkmann This is great. I was able to just make a <FixedTypeahead /> component which does this work! This should be in the docs or a FAQ somewhere.

@larrydahooster
Copy link

larrydahooster commented Jul 11, 2017

This is how I solved it for a Typeahead that has multiple: true and allowNew: true. It is not tested for any other combination of these props and will with a high chance break.


class TokenBasedSearchBar extends Component {

    constructor(props) {
        super(props);
        this.handlOnInputChange = this.handlOnInputChange.bind(this);
        this.state = { textValue: '' };
    }

    componentDidUpdate(_, prevState) {
        if (this.state.textValue !== prevState.textValue) {
            const instance = this.typeahead.getInstance();
            const { initialItem } = instance.state;
            instance.setState({ activeIndex: 0, activeItem: initialItem });
        }
    }

    handlOnInputChange(text) {
        this.setState({ textValue: text });
    }

    render() {
        const { tags, onChange, selected, labelKey, minLength, searchFor, autoFocus } = this.props;

        return (
            <Typeahead
              ref={(c) => { this.typeahead = c; } }
              onChange={onChange}
              options={tags}
              labelKey={labelKey}
              multiple
              allowNew
              selected={selected}
              renderToken={renderToken}
              renderMenu={renderMenu}
              onInputChange={this.handlOnInputChange}
            />
        );
    }
}

One thing I like to add. It is mandatory to put the handling of the ativeIdex/activeItem into the componentDidUpdate hook, cause the onInputChange gets fired before the result processing takes place. I am not sure about race-conditions, but I heavily tested it and I had no problems so far.

@ericgio ericgio changed the title Auto-highlight first option Auto-highlight only option Jul 14, 2017
ericgio added a commit that referenced this issue Jul 15, 2017
@ericgio ericgio added this to the 2.0 milestone Jul 20, 2017
@ericgio
Copy link
Owner

ericgio commented Jul 21, 2017

highlightOnlyResult prop added in v2.0.0-alpha.1

@ericgio ericgio closed this as completed Jul 21, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants