UNPKG

azure-devops-ui

Version:

React components for building web UI in Azure DevOps

146 lines (145 loc) 7.98 kB
import "../../CommonImports"; import "../../Core/core.css"; import "./Button.css"; import "./ExpandableButton.css"; import * as React from "react"; import { FocusGroupContext } from '../../FocusGroup'; import { FocusZoneContext } from '../../FocusZone'; import { Icon, IconSize } from '../../Icon'; import { Tooltip } from '../../TooltipEx'; import { childCount, css, getSafeId, KeyCode } from '../../Util'; import { getTabIndex } from '../../Utilities/Focus'; export class Button extends React.Component { constructor() { super(...arguments); this.buttonElement = React.createRef(); this.onClick = (event) => { if (!this.props.disabled) { const { onClick } = this.props; // @NOTE: Safari doesnt set focus to buttons when they are clicked, we need this // to help manage the focus state for callouts. this.focus(); if (onClick) { onClick(event); } } else { event.preventDefault(); } }; this.onFocus = (event) => { if (this.props.onFocus) { this.props.onFocus(event); } if (this.props.id) { // @NOTE: Due to test issues using React.16.3.2 we MUST validate the onFocus method. this.context.onFocus && this.context.onFocus(this.props.id); } }; this.onKeyDown = (event) => { if (!event.defaultPrevented && !this.props.disabled) { if (event.which === KeyCode.enter || event.which === KeyCode.space) { if (this.props.onClick) { this.props.onClick(event); } if (!this.props.href) { event.preventDefault(); } } else if (event.which === KeyCode.escape) { // Prevent ESC from bubbling up to parent components to avoid unintended dismissals // Remove focus from the button when ESC is pressed if (this.buttonElement.current) { this.buttonElement.current.blur(); document.dispatchEvent(new CustomEvent('vss-telemetry-proxy', { detail: { area: "vss-widgets", component: "Button", feature: "VssWidgets.Button", level: 3, method: "onKeyDown", message: "Remove focus from button on Escape key press" }, bubbles: true })); } event.preventDefault(); event.stopPropagation(); } else if (this.props.onKeyDown) { this.props.onKeyDown(event); } } }; this.onMouseDown = (event) => { if (!event.defaultPrevented) { if (this.props.disabled) { event.preventDefault(); } } const { onMouseDown } = this.props; if (onMouseDown) { onMouseDown(event); } }; this.onMouseLeave = (event) => { if (!this.props.disabled) { const { onMouseLeave } = this.props; if (onMouseLeave) { onMouseLeave(event); } } }; this.onMouseOver = (event) => { if (!this.props.disabled) { const { onMouseOver } = this.props; if (onMouseOver) { onMouseOver(event); } } }; } render() { if (false) { if (this.props.danger && this.props.primary) { console.warn("primary and danger props are both set to true on Button, only one should be set to true at a time."); } } // Determine if the button is an iconOnly button. const iconOnly = this.props.iconProps && !this.props.text && childCount(this.props.children) === 0; const tooltipProps = this.props.tooltipProps !== undefined ? this.props.tooltipProps : iconOnly && this.props.ariaLabel ? { text: this.props.ariaLabel } : undefined; return (React.createElement(FocusZoneContext.Consumer, null, (zoneContext) => { const ButtonType = this.props.href ? "a" : "button"; // @TODO (line-height): remove the body-m from the text once the line-height is applied globally. let role = this.props.role || (this.props.href ? "link" : "button"); let button = ( // @ts-ignore TypeScript no longer works with dynamic intrinsic component types. React.createElement(ButtonType, { autoFocus: !this.props.href ? this.props.autoFocus : undefined, "aria-controls": getSafeId(this.props.ariaControls), "aria-describedby": getSafeId(this.props.ariaDescribedBy), "aria-disabled": this.props.disabled || this.props.ariaDisabled, "aria-expanded": this.props.ariaExpanded, "aria-haspopup": this.props.ariaHasPopup, "aria-hidden": this.props.ariaHidden, "aria-label": this.props.ariaLabel, "aria-labelledby": this.props.ariaLabelledBy, "aria-setsize": this.props.ariaSetSize, "aria-posinset": this.props.ariaPosInSet, "aria-selected": this.props.ariaSelected, "aria-checked": this.props.ariaChecked, "aria-pressed": this.props.ariaPressed, "aria-roledescription": this.props.ariaRoleDescription, className: css(this.props.className, "bolt-button", this.props.href && "bolt-link-button", this.props.iconProps && "bolt-icon-button", this.props.danger && "danger", this.props.disabled ? "disabled" : "enabled", this.props.primary && "primary", this.props.subtle && "subtle", iconOnly && "icon-only", "bolt-focus-treatment"), "data-focuszone": !this.props.disabled && !this.props.excludeFocusZone ? css(this.props.focusZoneId, zoneContext.focuszoneId) : undefined, "data-index": this.props.dataIndex, "data-is-focusable": !this.props.excludeFocusZone, href: !this.props.disabled ? this.props.href : undefined, id: getSafeId(this.props.id), onBlur: this.props.onBlur, onClick: this.onClick, onMouseLeave: this.onMouseLeave, onMouseOver: this.onMouseOver, onKeyDown: this.onKeyDown, onMouseDown: this.onMouseDown, onFocus: this.onFocus, rel: this.props.rel, role: role, style: this.props.style, tabIndex: getTabIndex(this.props, this.context), target: this.props.target, type: this.props.type ? this.props.type : !this.props.href ? "button" : undefined, ref: this.buttonElement }, this.props.iconProps && Icon(Object.assign(Object.assign({ size: IconSize.medium }, this.props.iconProps), { className: css(this.props.iconProps.className, "left-icon") })), this.props.text && React.createElement("span", { className: "bolt-button-text body-m" }, this.props.text), this.props.children)); if (tooltipProps) { button = (React.createElement(Tooltip, Object.assign({ addAriaDescribedBy: true }, tooltipProps), button)); } return button; })); } focus() { if (this.buttonElement.current) { this.buttonElement.current.focus(); } } setTabIndex(index) { if (this.buttonElement.current) { this.buttonElement.current.tabIndex = index; } } } Button.contextType = FocusGroupContext;