render()

in packages/eui/src/components/combo_box/combo_box_input/combo_box_input.tsx [223:415]


  render() {
    const {
      compressed,
      focusedOptionId,
      fullWidth,
      hasSelectedOptions,
      id,
      isDisabled,
      isListOpen,
      noIcon,
      onChange,
      onClear,
      onClick,
      onFocus,
      onCloseListClick,
      onOpenListClick,
      placeholder,
      rootId,
      searchValue,
      selectedOptions,
      singleSelection,
      value,
      prepend,
      append,
      isLoading,
      isInvalid,
      autoFocus,
      'aria-label': ariaLabel,
      'aria-labelledby': ariaLabelledby,
    } = this.props;

    let removeOptionMessage: ReactNode;
    let removeOptionMessageId;

    if (this.state.hasFocus) {
      const readPlaceholder = placeholder ? `${placeholder}.` : '';
      const removeOptionMessageContent =
        `Combo box. Selected. ${
          searchValue ? `${searchValue}. Selected. ` : ''
        }${
          selectedOptions && selectedOptions.length > 0
            ? `${value}. Press Backspace to delete ${
                selectedOptions[selectedOptions.length - 1].label
              }. `
            : ''
        }Combo box input. ${readPlaceholder} Type some text or, to display a list of choices, press Down Arrow. ` +
        'To exit the list of choices, press Escape.';

      removeOptionMessageId = rootId('removeOptionMessage');

      // aria-live="assertive" will read this message aloud immediately once it enters the DOM.
      // We'll render to the DOM when the input gains focus and remove it when the input loses focus.
      // We'll use aria-hidden to prevent default aria information from being read by the screen
      // reader.
      removeOptionMessage = (
        <EuiScreenReaderOnly>
          <span aria-live="polite" id={removeOptionMessageId}>
            {removeOptionMessageContent}
          </span>
        </EuiScreenReaderOnly>
      );
    }

    const isInGroup = singleSelection && (prepend || append);
    const showPlaceholder =
      placeholder && !selectedOptions?.length && !searchValue;

    const clickProps: EuiFormControlLayoutIconsProps = {};
    if (!isDisabled && onClear && hasSelectedOptions) {
      clickProps.clear = {
        'data-test-subj': 'comboBoxClearButton',
        onClick: onClear,
      };
    }

    let icon: EuiFormControlLayoutIconsProps['icon'];
    if (!noIcon && !isDisabled) {
      icon = {
        'aria-label': isListOpen
          ? 'Close list of options'
          : 'Open list of options',
        'data-test-subj': 'comboBoxToggleListButton',
        disabled: isDisabled,
        onClick: isListOpen && !isDisabled ? onCloseListClick : onOpenListClick,
        side: 'right',
        tabIndex: -1,
        type: 'arrowDown',
      };
    }

    const wrapClasses = classNames('euiComboBox__inputWrap', {
      'euiComboBox__inputWrap--plainText': this.asPlainText,
    });

    return (
      <RenderWithEuiStylesMemoizer>
        {(stylesMemoizer) => {
          const styles = stylesMemoizer(euiComboBoxInputStyles);
          const cssStyles = [
            styles.euiComboBoxInputWrapper,
            !singleSelection && styles.multiSelect,
            compressed ? styles.compressed : styles.uncompressed,
            ...(this.asPlainText || showPlaceholder
              ? [
                  styles.plainText.plainText,
                  compressed
                    ? styles.plainText.compressed
                    : styles.plainText.uncompressed,
                ]
              : []),
            isDisabled
              ? styles.disabled
              : isInvalid
              ? styles.invalid
              : isListOpen
              ? styles.open
              : undefined,
            isInGroup && styles.inGroup,
          ];
          const formLayoutStyles = [
            styles.formLayout.euiComboBox__formControlLayout,
            !singleSelection && styles.formLayout.multiSelect,
          ];

          return (
            <EuiFormControlLayout
              icon={icon}
              {...clickProps}
              inputId={id}
              isLoading={isLoading}
              isInvalid={isInvalid}
              isDisabled={isDisabled}
              compressed={compressed}
              fullWidth={fullWidth}
              prepend={prepend}
              append={append}
              css={formLayoutStyles}
            >
              <div
                css={cssStyles}
                className={wrapClasses}
                data-test-subj="comboBoxInput"
                onClick={onClick}
                tabIndex={-1} // becomes onBlur event's relatedTarget, otherwise relatedTarget is null when clicking on this div
              >
                {this.renderPills()}
                <EuiComboBoxOptionAppendPrepend
                  option={this.asPlainText ? selectedOptions?.[0] : undefined}
                  classNamePrefix="euiComboBoxPlainTextSelection"
                  marginSize="xxs"
                >
                  <input
                    aria-activedescendant={focusedOptionId}
                    aria-autocomplete="list"
                    aria-controls={isListOpen ? rootId('listbox') : ''}
                    aria-expanded={isListOpen}
                    aria-label={ariaLabel}
                    aria-labelledby={ariaLabelledby}
                    aria-invalid={isInvalid}
                    aria-haspopup="listbox"
                    css={styles.euiComboBoxInput}
                    className="euiComboBox__input"
                    data-test-subj="comboBoxSearchInput"
                    disabled={isDisabled}
                    id={id}
                    onBlur={this.onBlur}
                    onChange={(event) => onChange(event.target.value)}
                    onFocus={this.onFocus}
                    onKeyDown={this.onKeyDown}
                    ref={this.inputRefCallback}
                    role="combobox"
                    style={{
                      inlineSize:
                        this.asPlainText || showPlaceholder
                          ? '100%'
                          : this.state.inputWidth,
                    }}
                    placeholder={showPlaceholder ? placeholder : undefined}
                    value={this.searchValue}
                    autoFocus={autoFocus}
                    autoComplete="off"
                    // Force the menu to re-open on every input click - only necessary when plain text
                    onClick={this.asPlainText ? (onFocus as any) : undefined} // Type shenanigans - event should be mostly the same
                  />
                </EuiComboBoxOptionAppendPrepend>
                {removeOptionMessage}
              </div>
            </EuiFormControlLayout>
          );
        }}
      </RenderWithEuiStylesMemoizer>
    );
  }