diff --git a/static/app/components/core/button/index.tsx b/static/app/components/core/button/index.tsx index 396d665f5941d6..5f3960cbbde327 100644 --- a/static/app/components/core/button/index.tsx +++ b/static/app/components/core/button/index.tsx @@ -1,102 +1,23 @@ import isPropValid from '@emotion/is-prop-valid'; -import type {SerializedStyles, Theme} from '@emotion/react'; -import {css} from '@emotion/react'; import styled from '@emotion/styled'; -import {type LinkButtonProps} from 'sentry/components/core/button/linkButton'; -import {Tooltip, type TooltipProps} from 'sentry/components/core/tooltip'; +import {Tooltip} from 'sentry/components/core/tooltip'; import InteractionStateLayer from 'sentry/components/interactionStateLayer'; -import type {SVGIconProps} from 'sentry/icons/svgIcon'; import {IconDefaultsProvider} from 'sentry/icons/useIconDefaults'; -import HookStore from 'sentry/stores/hookStore'; import {space} from 'sentry/styles/space'; -import {getChonkButtonStyles} from './index.chonk'; +import { + DO_NOT_USE_BUTTON_ICON_SIZES as BUTTON_ICON_SIZES, + DO_NOT_USE_getButtonStyles as getButtonStyles, +} from './styles'; +import {DO_NOT_USE_getChonkButtonStyles as getChonkButtonStyles} from './styles.chonk'; +import type { + DO_NOT_USE_ButtonProps as ButtonProps, + DO_NOT_USE_CommonButtonProps as CommonButtonProps, +} from './types'; +import {useButtonFunctionality} from './useButtonFunctionality'; -// We do not want people using this type as it should only be used -// internally by the different button implementations -// eslint-disable-next-line @typescript-eslint/naming-convention -export interface DO_NOT_USE_CommonButtonProps { - /** - * Used when you want to overwrite the default Reload event key for analytics - */ - analyticsEventKey?: string; - /** - * Used when you want to send an Amplitude Event. By default, Amplitude events are not sent so - * you must pass in a eventName to send an Amplitude event. - */ - analyticsEventName?: string; - /** - * Adds extra parameters to the analytics tracking - */ - analyticsParams?: Record; - /** - * Removes borders from the button. - */ - borderless?: boolean; - /** - * Indicates that the button is "doing" something. - */ - busy?: boolean; - /** - * The icon to render inside of the button. The size will be set - * appropriately based on the size of the button. - */ - icon?: React.ReactNode; - /** - * The semantic "priority" of the button. Use `primary` when the action is - * contextually the primary action, `danger` if the button will do something - * destructive, `link` for visual similarity to a link. - */ - priority?: 'default' | 'primary' | 'danger' | 'link'; - /** - * The size of the button - */ - size?: 'zero' | 'xs' | 'sm' | 'md'; - /** - * Display a tooltip for the button. - */ - title?: TooltipProps['title']; - /** - * Additional properites for the Tooltip when `title` is set. - */ - tooltipProps?: Omit; - /** - * Userful in scenarios where the border of the button should blend with the - * background behind the button. - */ - translucentBorder?: boolean; -} - -/** - * Helper type to extraxct the HTML element props for use in button prop - * interfaces. - * - * XXX(epurkhiser): Right now all usages of this use ButtonElement, but in the - * future ButtonElement should go away and be replaced with HTMLButtonElement - * and HTMLAnchorElement respectively - */ -type ButtonElementProps = Omit< - React.ButtonHTMLAttributes, - 'label' | 'size' | 'title' ->; - -interface BaseButtonProps extends DO_NOT_USE_CommonButtonProps, ButtonElementProps { - href?: never; - ref?: React.Ref; - to?: never; -} - -interface ButtonPropsWithoutAriaLabel extends BaseButtonProps { - children: React.ReactNode; -} - -interface ButtonPropsWithAriaLabel extends BaseButtonProps { - 'aria-label': string; - children?: never; -} - -export type ButtonProps = ButtonPropsWithoutAriaLabel | ButtonPropsWithAriaLabel; +export type {ButtonProps}; export function Button({ size = 'md', @@ -134,7 +55,7 @@ export function Button({ {props.icon && ( - + {props.icon} @@ -147,266 +68,15 @@ export function Button({ } export const StyledButton = styled('button')` - ${p => - p.theme.isChonk - ? getChonkButtonStyles(p as any) - : DO_NOT_USE_getButtonStyles(p as any)} + ${p => (p.theme.isChonk ? getChonkButtonStyles(p as any) : getButtonStyles(p as any))} `; -export const useButtonFunctionality = (props: ButtonProps | LinkButtonProps) => { - // Fallbacking aria-label to string children is not necessary as screen - // readers natively understand that scenario. Leaving it here for a bunch of - // our tests that query by aria-label. - const accessibleLabel = - props['aria-label'] ?? - (typeof props.children === 'string' ? props.children : undefined); - - const useButtonTrackingLogger = () => { - const hasAnalyticsDebug = window.localStorage?.getItem('DEBUG_ANALYTICS') === '1'; - const hasCustomAnalytics = - props.analyticsEventName || props.analyticsEventKey || props.analyticsParams; - if (!hasCustomAnalytics || !hasAnalyticsDebug) { - return () => {}; - } - - return () => { - // eslint-disable-next-line no-console - console.log('buttonAnalyticsEvent', { - eventKey: props.analyticsEventKey, - eventName: props.analyticsEventName, - priority: props.priority, - href: 'href' in props ? props.href : undefined, - ...props.analyticsParams, - }); - }; - }; - - const useButtonTracking = - HookStore.get('react-hook:use-button-tracking')[0] ?? useButtonTrackingLogger; - - const buttonTracking = useButtonTracking({ - analyticsEventName: props.analyticsEventName, - analyticsEventKey: props.analyticsEventKey, - analyticsParams: { - priority: props.priority, - href: 'href' in props ? props.href : undefined, - ...props.analyticsParams, - }, - 'aria-label': accessibleLabel || '', - }); - - const handleClick = (e: React.MouseEvent) => { - // Don't allow clicks when disabled or busy - if (props.disabled || props.busy) { - e.preventDefault(); - e.stopPropagation(); - return; - } - - buttonTracking(); - // @ts-expect-error at this point, we don't know if the button is a button or a link - props.onClick?.(e); - }; - - const hasChildren = Array.isArray(props.children) - ? props.children.some(child => !!child || String(child) === '0') - : !!props.children || String(props.children) === '0'; - - // Buttons come in 4 flavors: , , , and