public render()

in packages/react/src/components/Button/BaseButton.tsx [116:263]


  public render(): JSX.Element {
    const {
      ariaDescription,
      ariaLabel,
      ariaHidden,
      className,
      disabled,
      allowDisabledFocus,
      primaryDisabled,
      // eslint-disable-next-line deprecation/deprecation
      secondaryText = this.props.description,
      href,
      iconProps,
      menuIconProps,
      styles,
      checked,
      variantClassName,
      theme,
      toggle,
      getClassNames,
      role,
    } = this.props;

    const { menuHidden } = this.state;

    // Button is disabled if the whole button (in case of splitButton is disabled) or if the primary action is disabled
    const isPrimaryButtonDisabled = disabled || primaryDisabled;

    this._classNames = getClassNames
      ? getClassNames(
          theme!,
          className!,
          variantClassName!,
          iconProps && iconProps.className,
          menuIconProps && menuIconProps.className,
          isPrimaryButtonDisabled!,
          checked!,
          !menuHidden,
          !!this.props.menuProps,
          this.props.split,
          !!allowDisabledFocus,
        )
      : getBaseButtonClassNames(
          theme!,
          styles!,
          className!,
          variantClassName!,
          iconProps && iconProps.className,
          menuIconProps && menuIconProps.className,
          isPrimaryButtonDisabled!,
          !!this.props.menuProps,
          checked!,
          !menuHidden,
          this.props.split,
        );

    const { _ariaDescriptionId, _labelId, _descriptionId } = this;
    // Anchor tag cannot be disabled hence in disabled state rendering
    // anchor button as normal button
    const renderAsAnchor: boolean = !isPrimaryButtonDisabled && !!href;
    const tag = renderAsAnchor ? 'a' : 'button';

    const nativeProps = getNativeProps(
      // eslint-disable-next-line deprecation/deprecation
      assign(renderAsAnchor ? {} : { type: 'button' }, this.props.rootProps, this.props),
      renderAsAnchor ? anchorProperties : buttonProperties,
      [
        'disabled', // let disabled buttons be focused and styled as disabled.
      ],
    );

    // Check for ariaLabel passed in via Button props, and fall back to aria-label passed in via native props
    const resolvedAriaLabel = ariaLabel || (nativeProps as any)['aria-label'];

    // Check for ariaDescription, secondaryText or aria-describedby in the native props to determine source of
    // aria-describedby. Otherwise default to undefined so property does not appear in output.
    let ariaDescribedBy = undefined;
    if (ariaDescription) {
      ariaDescribedBy = _ariaDescriptionId;
    } else if (secondaryText && this.props.onRenderDescription !== nullRender) {
      // for buttons like CompoundButton with a valid onRenderDescription, we need to set an ariaDescribedBy
      // for buttons that do not render anything (via nullRender), we should not set an ariaDescribedBy
      ariaDescribedBy = _descriptionId;
    } else if ((nativeProps as any)['aria-describedby']) {
      ariaDescribedBy = (nativeProps as any)['aria-describedby'];
    }

    // If an explicit aria-labelledby is given, use that and we're done.
    // If any kind of description is given (which will end up as an aria-describedby attribute)
    // and no ariaLabel is specified, set the labelledby element.
    // Otherwise, the button is labeled implicitly by the descendent text on the button (if it exists).
    let ariaLabelledBy = undefined;
    if ((nativeProps as any)['aria-labelledby']) {
      ariaLabelledBy = (nativeProps as any)['aria-labelledby'];
    } else if (ariaDescribedBy && !resolvedAriaLabel) {
      ariaLabelledBy = this._hasText() ? _labelId : undefined;
    }

    const dataIsFocusable =
      (this.props as any)['data-is-focusable'] === false || (disabled && !allowDisabledFocus) || this._isSplitButton
        ? false
        : true;

    const isCheckboxTypeRole = role === 'menuitemcheckbox' || role === 'checkbox';
    // if isCheckboxTypeRole, always return a checked value.
    // Otherwise only return checked value if toggle is set to true.
    // This is because role="checkbox" always needs to have an aria-checked value
    // but our checked prop only sets aria-pressed if we mark the button as a toggle="true"
    const checkedOrPressedValue = isCheckboxTypeRole ? !!checked : toggle === true ? !!checked : undefined;

    const buttonProps = assign(nativeProps, {
      className: this._classNames.root,
      // eslint-disable-next-line deprecation/deprecation
      ref: this._mergedRef(this.props.elementRef, this._buttonElement),
      disabled: isPrimaryButtonDisabled && !allowDisabledFocus,
      onKeyDown: this._onKeyDown,
      onKeyPress: this._onKeyPress,
      onKeyUp: this._onKeyUp,
      onMouseDown: this._onMouseDown,
      onMouseUp: this._onMouseUp,
      onClick: this._onClick,
      'aria-label': resolvedAriaLabel,
      'aria-labelledby': ariaLabelledBy,
      'aria-describedby': ariaDescribedBy,
      'aria-disabled': isPrimaryButtonDisabled,
      'data-is-focusable': dataIsFocusable,
      // aria-pressed attribute should only be present for toggle buttons
      // aria-checked attribute should only be present for toggle buttons with checkbox type role
      [isCheckboxTypeRole ? 'aria-checked' : 'aria-pressed']: checkedOrPressedValue,
    });

    if (ariaHidden) {
      buttonProps['aria-hidden'] = true;
    }

    if (this._isSplitButton) {
      return this._onRenderSplitButtonContent(tag, buttonProps);
    } else if (this.props.menuProps) {
      const { id = `${this._labelId}-menu` } = this.props.menuProps;
      assign(buttonProps, {
        'aria-expanded': !menuHidden,
        'aria-controls': !menuHidden ? id : null,
        'aria-haspopup': true,
      });
    }

    return this._onRenderContent(tag, buttonProps);
  }