render()

in packages/eui/src/components/combo_box/combo_box.tsx [722:922]


  render() {
    const {
      'data-test-subj': dataTestSubj,
      async,
      className,
      compressed,
      customOptionText,
      fullWidth,
      id,
      inputRef,
      isCaseSensitive,
      isClearable,
      isDisabled,
      isInvalid,
      isLoading,
      noSuggestions,
      onBlur,
      onChange,
      onCreateOption,
      onSearchChange,
      options,
      placeholder,
      renderOption,
      rowHeight,
      selectedOptions,
      singleSelection,
      prepend,
      sortMatchesBy,
      delimiter,
      append,
      autoFocus,
      truncationProps,
      inputPopoverProps,
      optionMatcher,
      'aria-label': ariaLabel,
      'aria-labelledby': ariaLabelledby,
      ...rest
    } = this.props;
    const {
      activeOptionIndex,
      hasFocus,
      isListOpen,
      searchValue,
      matchingOptions,
    } = this.state;

    // Make sure we have a valid ID if users don't pass one as a prop
    const inputId = id ?? this.rootId('_eui-combobox-id');

    // Visually indicate the combobox is in an invalid state if it has lost focus but there is text entered in the input.
    // When custom options are disabled and the user leaves the combo box after entering text that does not match any
    // options, this tells the user that they've entered invalid input.
    const markAsInvalid = !!(
      isInvalid ||
      ((hasFocus === false || isListOpen === false) && searchValue)
    );

    const classes = classNames('euiComboBox', className, {
      'euiComboBox-isDisabled': isDisabled,
      'euiComboBox-isInvalid': markAsInvalid,
      'euiComboBox-isOpen': isListOpen,
    });

    const value = selectedOptions
      .map((selectedOption) => selectedOption.label)
      .join(', ');

    let optionsList: React.ReactElement;

    if (!noSuggestions && isListOpen) {
      const optionsListDataTestSubj = dataTestSubj
        ? `${dataTestSubj}-optionsList`
        : undefined;

      optionsList = (
        <EuiI18n
          token="euiComboBox.listboxAriaLabel"
          default="Choose from the following options"
        >
          {(listboxAriaLabel: string) => (
            <EuiComboBoxOptionsList
              activeOptionIndex={this.state.activeOptionIndex}
              areAllOptionsSelected={this.areAllOptionsSelected()}
              customOptionText={customOptionText}
              data-test-subj={optionsListDataTestSubj}
              fullWidth={fullWidth}
              isCaseSensitive={isCaseSensitive}
              isLoading={isLoading}
              listRef={this.listRefCallback}
              matchingOptions={matchingOptions}
              onCloseList={this.closeList}
              onCreateOption={onCreateOption}
              onOptionClick={this.onOptionClick}
              onOptionEnterKey={this.onOptionEnterKey}
              onScroll={this.onOptionListScroll}
              options={options}
              singleSelection={singleSelection}
              renderOption={renderOption}
              rootId={this.rootId}
              rowHeight={rowHeight}
              scrollToIndex={activeOptionIndex}
              searchValue={searchValue}
              selectedOptions={selectedOptions}
              delimiter={delimiter}
              getSelectedOptionForSearchValue={getSelectedOptionForSearchValue}
              listboxAriaLabel={listboxAriaLabel}
              truncationProps={truncationProps}
            />
          )}
        </EuiI18n>
      );
    }

    return (
      /**
       * EuiComboBox follows the WAI-ARIA 1.2 spec for editable comboboxes
       * with list autocomplete. This pattern is an improvement on the user
       * experience for screen readers over the WAI-ARIA 1.1 pattern.
       *
       * https://www.w3.org/TR/wai-aria-practices-1.2/examples/combobox/combobox-autocomplete-list.html
       */
      <RenderWithEuiTheme>
        {(euiTheme) => {
          const cssStyles = [
            styles.euiComboBox,
            fullWidth
              ? styles.fullWidth
              : logicalStyle('max-width', euiFormMaxWidth(euiTheme)),
          ];
          return (
            <div
              css={cssStyles}
              {...rest}
              className={classes}
              data-test-subj={dataTestSubj}
              onKeyDown={this.onKeyDown}
              onBlur={this.onContainerBlur}
              ref={this.comboBoxRefCallback}
            >
              <EuiInputPopover
                fullWidth={fullWidth}
                panelPaddingSize="none"
                disableFocusTrap={true}
                closeOnScroll={true}
                {...inputPopoverProps}
                isOpen={isListOpen}
                closePopover={this.closeList}
                /* we don't want content changes to be announced via aria-live 
                because ComboBox uses a virtualized list that updates itself
                on scroll and would result in unexpected screen reader output */
                aria-live="off"
                input={
                  <EuiComboBoxInput
                    compressed={compressed}
                    focusedOptionId={
                      this.hasActiveOption()
                        ? this.rootId(`_option-${this.state.activeOptionIndex}`)
                        : undefined
                    }
                    fullWidth={fullWidth}
                    hasSelectedOptions={selectedOptions.length > 0}
                    id={inputId}
                    inputRef={this.searchInputRefCallback}
                    isDisabled={isDisabled}
                    isListOpen={isListOpen}
                    noIcon={!!noSuggestions}
                    onChange={this.onSearchChange}
                    onClear={
                      isClearable && !isDisabled
                        ? this.clearSelectedOptions
                        : undefined
                    }
                    onClick={this.onComboBoxClick}
                    onCloseListClick={this.closeList}
                    onFocus={this.onComboBoxFocus}
                    onOpenListClick={this.onOpenListClick}
                    onRemoveOption={this.onRemoveOption}
                    placeholder={placeholder}
                    rootId={this.rootId}
                    searchValue={searchValue}
                    selectedOptions={selectedOptions}
                    singleSelection={singleSelection}
                    value={value}
                    append={singleSelection ? append : undefined}
                    prepend={singleSelection ? prepend : undefined}
                    isLoading={isLoading}
                    isInvalid={markAsInvalid}
                    autoFocus={autoFocus}
                    aria-label={ariaLabel}
                    aria-labelledby={ariaLabelledby}
                  />
                }
              >
                {optionsList}
              </EuiInputPopover>
            </div>
          );
        }}
      </RenderWithEuiTheme>
    );
  }