diff --git a/.changeset/tender-queens-juggle.md b/.changeset/tender-queens-juggle.md new file mode 100644 index 00000000000..4f1977e2bcd --- /dev/null +++ b/.changeset/tender-queens-juggle.md @@ -0,0 +1,5 @@ +--- +"@primer/react": minor +--- + +feat(ButtonBase): Remove css modules feature flag from ButtonBase diff --git a/packages/react/src/Button/ButtonBase.tsx b/packages/react/src/Button/ButtonBase.tsx index 752525b1ad9..d49124a624f 100644 --- a/packages/react/src/Button/ButtonBase.tsx +++ b/packages/react/src/Button/ButtonBase.tsx @@ -1,13 +1,9 @@ import type {ComponentPropsWithRef} from 'react' -import React, {forwardRef, useMemo} from 'react' +import React, {forwardRef} from 'react' import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' import Box from '../Box' -import type {BetterSystemStyleObject} from '../sx' -import {merge} from '../sx' -import {useTheme} from '../ThemeProvider' import type {ButtonProps} from './types' -import {StyledButton} from './types' -import {getVariantStyles, getButtonStyles, getAlignContentSize} from './styles' +import {getAlignContentSize} from './styles' import {useRefObjectAsForwardedRef} from '../hooks/useRefObjectAsForwardedRef' import {defaultSxProp} from '../utils/defaultSxProp' import {VisuallyHidden} from '../VisuallyHidden' @@ -18,20 +14,8 @@ import {ConditionalWrapper} from '../internal/components/ConditionalWrapper' import {AriaStatus} from '../live-region' import {clsx} from 'clsx' import classes from './ButtonBase.module.css' -import {useFeatureFlag} from '../FeatureFlags' import {isElement} from 'react-is' -const iconWrapStyles = { - display: 'flex', - pointerEvents: 'none', -} - -const renderVisual = (Visual: React.ElementType | React.ReactElement, loading: boolean, visualName: string) => ( - - {loading ? : isElement(Visual) ? Visual : } - -) - const renderModuleVisual = ( Visual: React.ElementType | React.ReactElement, loading: boolean, @@ -70,17 +54,9 @@ const ButtonBase = forwardRef( ...rest } = props - const enabled = useFeatureFlag('primer_react_css_modules_ga') const innerRef = React.useRef(null) useRefObjectAsForwardedRef(forwardedRef, innerRef) - const {theme} = useTheme() - const baseStyles = useMemo(() => { - return merge.all([getButtonStyles(theme), getVariantStyles(variant, theme)]) - }, [theme, variant]) - const sxStyles = useMemo(() => { - return merge(baseStyles, sxProp) - }, [baseStyles, sxProp]) const uuid = useId(id) const loadingAnnouncementID = `${uuid}-loading-announcement` @@ -104,131 +80,7 @@ const ButtonBase = forwardRef( }, [innerRef]) } - if (enabled) { - if (sxProp !== defaultSxProp) { - return ( - - Boolean(descriptionID)) - .join(' ')} - // aria-labelledby is needed because the accessible name becomes unset when the button is in a loading state. - // We only set it when the button is in a loading state because it will supercede the aria-label when the screen - // reader announces the button name. - aria-labelledby={ - loading - ? [`${uuid}-label`, ariaLabelledBy].filter(labelID => Boolean(labelID)).join(' ') - : ariaLabelledBy - } - id={id} - onClick={loading ? undefined : onClick} - > - {Icon ? ( - loading ? ( - - ) : isElement(Icon) ? ( - Icon - ) : ( - - ) - ) : ( - <> - - { - /* If there are no leading/trailing visuals/actions to replace with a loading spinner, - render a loading spiner in place of the button content. */ - loading && - !LeadingVisual && - !TrailingVisual && - !TrailingAction && - renderModuleVisual(Spinner, loading, 'loadingSpinner', false) - } - { - /* Render a leading visual unless the button is in a loading state. - Then replace the leading visual with a loading spinner. */ - LeadingVisual && renderModuleVisual(LeadingVisual, Boolean(loading), 'leadingVisual', false) - } - {children && ( - - {children} - - )} - { - /* If there is a count, render a counter label unless there is a trailing visual. - Then render the counter label as a trailing visual. - Replace the counter label or the trailing visual with a loading spinner if: - - the button is in a loading state - - there is no leading visual to replace with a loading spinner - */ - count !== undefined && !TrailingVisual - ? renderModuleVisual( - () => ( - - {count} - - ), - Boolean(loading) && !LeadingVisual, - 'trailingVisual', - true, - ) - : TrailingVisual - ? renderModuleVisual( - TrailingVisual, - Boolean(loading) && !LeadingVisual, - 'trailingVisual', - false, - ) - : null - } - - { - /* If there is a trailing action, render it unless the button is in a loading state - and there is no leading or trailing visual to replace with a loading spinner. */ - TrailingAction && - renderModuleVisual( - TrailingAction, - Boolean(loading) && !LeadingVisual && !TrailingVisual, - 'trailingAction', - false, - ) - } - - )} - - {loading && ( - - {loadingAnnouncement} - - )} - - ) - } + if (sxProp !== defaultSxProp) { return ( - Boolean(labelID)).join(' ') : ariaLabelledBy } id={id} - // @ts-ignore temporary disable as we migrate to css modules, until we remove PolymorphicForwardRefComponent onClick={loading ? undefined : onClick} > {Icon ? ( @@ -275,7 +127,12 @@ const ButtonBase = forwardRef( ) ) : ( <> - + { /* If there are no leading/trailing visuals/actions to replace with a loading spinner, render a loading spiner in place of the button content. */ @@ -287,7 +144,7 @@ const ButtonBase = forwardRef( } { /* Render a leading visual unless the button is in a loading state. - Then replace the leading visual with a loading spinner. */ + Then replace the leading visual with a loading spinner. */ LeadingVisual && renderModuleVisual(LeadingVisual, Boolean(loading), 'leadingVisual', false) } {children && ( @@ -322,7 +179,7 @@ const ButtonBase = forwardRef( ) : null } - + { /* If there is a trailing action, render it unless the button is in a loading state and there is no leading or trailing visual to replace with a loading spinner. */ @@ -336,7 +193,7 @@ const ButtonBase = forwardRef( } )} - + {loading && ( {loadingAnnouncement} @@ -345,7 +202,6 @@ const ButtonBase = forwardRef( ) } - return ( - Boolean(descriptionID)) @@ -379,6 +235,7 @@ const ButtonBase = forwardRef( loading ? [`${uuid}-label`, ariaLabelledBy].filter(labelID => Boolean(labelID)).join(' ') : ariaLabelledBy } id={id} + // @ts-ignore temporary disable as we migrate to css modules, until we remove PolymorphicForwardRefComponent onClick={loading ? undefined : onClick} > {Icon ? ( @@ -391,7 +248,7 @@ const ButtonBase = forwardRef( ) ) : ( <> - + { /* If there are no leading/trailing visuals/actions to replace with a loading spinner, render a loading spiner in place of the button content. */ @@ -399,15 +256,15 @@ const ButtonBase = forwardRef( !LeadingVisual && !TrailingVisual && !TrailingAction && - renderVisual(Spinner, loading, 'loadingSpinner') + renderModuleVisual(Spinner, loading, 'loadingSpinner', false) } { /* Render a leading visual unless the button is in a loading state. Then replace the leading visual with a loading spinner. */ - LeadingVisual && renderVisual(LeadingVisual, Boolean(loading), 'leadingVisual') + LeadingVisual && renderModuleVisual(LeadingVisual, Boolean(loading), 'leadingVisual', false) } {children && ( - + {children} )} @@ -419,25 +276,35 @@ const ButtonBase = forwardRef( - there is no leading visual to replace with a loading spinner */ count !== undefined && !TrailingVisual - ? renderVisual( - () => {count}, + ? renderModuleVisual( + () => ( + + {count} + + ), Boolean(loading) && !LeadingVisual, 'trailingVisual', + true, ) : TrailingVisual - ? renderVisual(TrailingVisual, Boolean(loading) && !LeadingVisual, 'trailingVisual') + ? renderModuleVisual(TrailingVisual, Boolean(loading) && !LeadingVisual, 'trailingVisual', false) : null } - + { /* If there is a trailing action, render it unless the button is in a loading state and there is no leading or trailing visual to replace with a loading spinner. */ TrailingAction && - renderVisual(TrailingAction, Boolean(loading) && !LeadingVisual && !TrailingVisual, 'trailingAction') + renderModuleVisual( + TrailingAction, + Boolean(loading) && !LeadingVisual && !TrailingVisual, + 'trailingAction', + false, + ) } )} - + {loading && ( {loadingAnnouncement} diff --git a/packages/react/src/Button/__tests__/__snapshots__/Button.test.tsx.snap b/packages/react/src/Button/__tests__/__snapshots__/Button.test.tsx.snap index 27d49686024..9808d784a94 100644 --- a/packages/react/src/Button/__tests__/__snapshots__/Button.test.tsx.snap +++ b/packages/react/src/Button/__tests__/__snapshots__/Button.test.tsx.snap @@ -1,315 +1,24 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Button respects block prop 1`] = ` -.c1 { - -webkit-box-pack: center; - -webkit-justify-content: center; - -ms-flex-pack: center; - justify-content: center; -} - -.c0 { - border-radius: 2; - border: 1px solid; - border-color: var(--button-default-borderColor-rest,undefined); - font-family: inherit; - font-weight: semibold; - font-size: 14px; - cursor: pointer; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - -webkit-text-decoration: none; - text-decoration: none; - text-align: center; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: justify; - -webkit-justify-content: space-between; - -ms-flex-pack: justify; - justify-content: space-between; - height: 32px; - padding: 0 12px; - gap: 8px; - min-width: -webkit-max-content; - min-width: -moz-max-content; - min-width: max-content; - -webkit-transition: 80ms cubic-bezier(0.65,0,0.35,1); - transition: 80ms cubic-bezier(0.65,0,0.35,1); - -webkit-transition-property: color,fill,background-color,border-color; - transition-property: color,fill,background-color,border-color; - color: btn.text; - background-color: btn.bg; - box-shadow: undefined,undefined; -} - -.c0:focus:not(:disabled) { - box-shadow: none; - outline: 2px solid; - outline-offset: -2px; -} - -.c0:focus:not(:disabled):not(:focus-visible) { - outline: solid 1px transparent; -} - -.c0:focus-visible:not(:disabled) { - box-shadow: none; - outline: 2px solid; - outline-offset: -2px; -} - -.c0[href] { - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; -} - -.c0[href]:hover { - -webkit-text-decoration: none; - text-decoration: none; -} - -.c0:hover { - -webkit-transition-duration: 80ms; - transition-duration: 80ms; -} - -.c0:active { - -webkit-transition: none; - transition: none; -} - -.c0[data-inactive] { - cursor: auto; -} - -.c0:disabled { - cursor: not-allowed; - box-shadow: none; - color: primer.fg.disabled; - border-color: var(--button-default-borderColor-disabled,undefined); - background-color: var(--button-default-bgColor-disabled,undefined); -} - -.c0:disabled [data-component=ButtonCounter] { - color: inherit; -} - -.c0 [data-component=ButtonCounter] { - font-size: 12px; - background-color: btn.counterBg; -} - -.c0[data-component=IconButton] { - display: inline-grid; - padding: unset; - place-content: center; - width: 32px; - min-width: unset; -} - -.c0[data-size="small"] { - padding: 0 8px; - height: 28px; - gap: 4px; - font-size: 12px; -} - -.c0[data-size="small"] [data-component="text"] { - line-height: 1.6666667; -} - -.c0[data-size="small"] [data-component=ButtonCounter] { - font-size: 12px; -} - -.c0[data-size="small"] [data-component="buttonContent"] > :not(:last-child) { - margin-right: 4px; -} - -.c0[data-size="small"][data-component=IconButton] { - width: 28px; - padding: unset; -} - -.c0[data-size="large"] { - padding: 0 16px; - height: 40px; - gap: 8px; -} - -.c0[data-size="large"] [data-component="buttonContent"] > :not(:last-child) { - margin-right: 8px; -} - -.c0[data-size="large"][data-component=IconButton] { - width: 40px; - padding: unset; -} - -.c0[data-block="block"] { - width: 100%; -} - -.c0[data-label-wrap="true"] { - min-width: -webkit-fit-content; - min-width: -moz-fit-content; - min-width: fit-content; - height: unset; - min-height: var(--control-medium-size,2rem); -} - -.c0[data-label-wrap="true"] [data-component="buttonContent"] { - -webkit-flex: 1 1 auto; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - -webkit-align-self: stretch; - -ms-flex-item-align: stretch; - align-self: stretch; - padding-block: calc(var(--control-medium-paddingBlock,0.375rem) - 2px); -} - -.c0[data-label-wrap="true"] [data-component="text"] { - white-space: unset; - word-break: break-word; -} - -.c0[data-label-wrap="true"][data-size="small"] { - height: unset; - min-height: var(--control-small-size,1.75rem); -} - -.c0[data-label-wrap="true"][data-size="small"] [data-component="buttonContent"] { - padding-block: calc(var(--control-small-paddingBlock,0.25rem) - 2px); -} - -.c0[data-label-wrap="true"][data-size="large"] { - height: unset; - min-height: var(--control-large-size,2.5rem); - padding-inline: var(--control-large-paddingInline-spacious,1rem); -} - -.c0[data-label-wrap="true"][data-size="large"] [data-component="buttonContent"] { - padding-block: calc(var(--control-large-paddingBlock,0.625rem) - 2px); -} - -.c0[data-inactive]:not([disabled]) { - background-color: var(--button-inactive-bgColor,undefined); - border-color: var(--button-inactive-bgColor,undefined); - color: var(--button-inactive-fgColor,undefined); -} - -.c0[data-inactive]:not([disabled]):focus-visible { - box-shadow: none; -} - -.c0 [data-component="leadingVisual"] { - grid-area: leadingVisual; -} - -.c0 [data-component="text"] { - grid-area: text; - line-height: 1.4285714; - white-space: nowrap; -} - -.c0 [data-component="trailingVisual"] { - grid-area: trailingVisual; -} - -.c0 [data-component="trailingAction"] { - margin-right: -4px; -} - -.c0 [data-component="buttonContent"] { - -webkit-flex: 1 0 auto; - -ms-flex: 1 0 auto; - flex: 1 0 auto; - display: grid; - grid-template-areas: "leadingVisual text trailingVisual"; - grid-template-columns: min-content minmax(0,auto) min-content; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - -webkit-align-content: center; - -ms-flex-line-pack: center; - align-content: center; -} - -.c0 [data-component="buttonContent"] > :not(:last-child) { - margin-right: 8px; -} - -.c0 [data-component="loadingSpinner"] { - grid-area: text; - margin-right: 0px !important; - place-self: center; - color: fg.muted; -} - -.c0 [data-component="loadingSpinner"] + [data-component="text"] { - visibility: hidden; -} - -.c0:hover:not([disabled]):not([data-inactive]) { - background-color: btn.hoverBg; - border-color: var(--button-default-borderColor-hover,undefined); -} - -.c0:active:not([disabled]):not([data-inactive]) { - background-color: btn.activeBg; - border-color: var(--button-default-borderColor-active,undefined); -} - -.c0[aria-expanded=true] { - background-color: btn.activeBg; - border-color: var(--button-default-borderColor-active,undefined); -} - -.c0 [data-component="leadingVisual"], -.c0 [data-component="trailingVisual"], -.c0 [data-component="trailingAction"] { - color: var(--button-color,undefined); -} - -.c0[data-component="IconButton"][data-no-visuals]:not(:disabled) { - color: fg.muted; -} - -@media (forced-colors:active) { - .c0:focus { - outline: solid 1px transparent; - } -} -