Search code examples
reactjsdrop-down-menublueprintjs

Add elements to Blueprint Select component popover?


Blueprint's Select component is exactly what I need for my current React project, with one exception: I need to add some elements to its popover and don't see any way to do it.

Specifically, I'd like to add a title (e.g. an H2 element) above the filter input, and a bar of buttons (e.g., some Button components in a DIV) below the list. Select seems highly configurable but I see no way to add elements inside the popover...what am I missing?


Solution

  • Jordan's suggestions above, plus a little experimentation, ultimately yielded a workable answer:

    1. Set filterable to false to hide the built-in filter input.
    2. Use itemListRenderer to render not only the dropdown items, but also an InputGroup to serve as a replacement filter.
    3. Use InputGroup's inputRef prop to capture a ref to the underlying HTML input. Use that to focus the input when it appears, via the onOpening property of Select's popoverProps prop.

    Here's a simple component implementing the above:

    // Extends Blueprint's Select component with header and footer props that
    // can be any arbitrary elements or components
    class ExtendedSelect extends Component {
      constructor(props) {
        super(props);
        this.inputRef = null;
        this.state = {query: ""};
      }
    
      handleInputChanged = event => {
        this.setState({query: event.target.value});
      }
    
      receiveInputRef = (ref) => {
        this.inputRef = ref;
      }
    
      handlePopoverOpening = () => {
        if (this.inputRef) {
          this.inputRef.focus();
        }
      }
    
      listRenderer = ({filteredItems, renderItem}) => {
        // Apply the supplied item renderer to the filtered list of items
        const renderedItems = filteredItems.map(renderItem);
    
        return (
          <div>
            {this.props.header}
            <InputGroup inputRef={this.receiveInputRef} value={this.state.query} onChange={this.handleInputChanged} leftIcon="search" />
            <Menu>
              {renderedItems}
            </Menu>
            {this.props.footer}
          </div>
        );
      }
    
      render() {
        return (
            <Select
              items={this.props.items}
              filterable={false}
              query={this.state.query}
              itemListRenderer={this.listRenderer}
              itemPredicate={this.props.itemPredicate}
              itemRenderer={this.props.itemRenderer}
              popoverProps={{onOpening:this.handlePopoverOpening}}
              onItemSelect={this.props.onItemSelect}
              >
    
              {this.props.children}
            </Select>
        );
      }
    }
    

    (Note that I'm only passing some of Select's props into the custom component—I suspect I'd know a way to pass them all were I a more experienced React developer.)

    This works surprisingly well—e.g., other than a little work to focus the input when it appears, all of Select's other built-in behavior works as expected, like keyboard navigation of the menu while the input is focused.