11import type { ComponentPropsWithRef } from 'react'
2- import React , { forwardRef , useMemo } from 'react'
2+ import React , { forwardRef } from 'react'
33import type { ForwardRefComponent as PolymorphicForwardRefComponent } from '../utils/polymorphic'
44import Box from '../Box'
5- import type { BetterSystemStyleObject } from '../sx'
6- import { merge } from '../sx'
7- import { useTheme } from '../ThemeProvider'
85import type { ButtonProps } from './types'
9- import { StyledButton } from './types'
10- import { getVariantStyles , getButtonStyles , getAlignContentSize } from './styles'
6+ import { getAlignContentSize } from './styles'
117import { useRefObjectAsForwardedRef } from '../hooks/useRefObjectAsForwardedRef'
128import { defaultSxProp } from '../utils/defaultSxProp'
139import { VisuallyHidden } from '../VisuallyHidden'
@@ -18,20 +14,8 @@ import {ConditionalWrapper} from '../internal/components/ConditionalWrapper'
1814import { AriaStatus } from '../live-region'
1915import { clsx } from 'clsx'
2016import classes from './ButtonBase.module.css'
21- import { useFeatureFlag } from '../FeatureFlags'
2217import { isElement } from 'react-is'
2318
24- const iconWrapStyles = {
25- display : 'flex' ,
26- pointerEvents : 'none' ,
27- }
28-
29- const renderVisual = ( Visual : React . ElementType | React . ReactElement , loading : boolean , visualName : string ) => (
30- < Box as = "span" data-component = { visualName } sx = { { ...iconWrapStyles } } >
31- { loading ? < Spinner size = "small" /> : isElement ( Visual ) ? Visual : < Visual /> }
32- </ Box >
33- )
34-
3519const renderModuleVisual = (
3620 Visual : React . ElementType | React . ReactElement ,
3721 loading : boolean ,
@@ -70,17 +54,9 @@ const ButtonBase = forwardRef(
7054 ...rest
7155 } = props
7256
73- const enabled = useFeatureFlag ( 'primer_react_css_modules_ga' )
7457 const innerRef = React . useRef < HTMLButtonElement > ( null )
7558 useRefObjectAsForwardedRef ( forwardedRef , innerRef )
7659
77- const { theme} = useTheme ( )
78- const baseStyles = useMemo ( ( ) => {
79- return merge . all ( [ getButtonStyles ( theme ) , getVariantStyles ( variant , theme ) ] )
80- } , [ theme , variant ] )
81- const sxStyles = useMemo ( ( ) => {
82- return merge < BetterSystemStyleObject > ( baseStyles , sxProp )
83- } , [ baseStyles , sxProp ] )
8460 const uuid = useId ( id )
8561 const loadingAnnouncementID = `${ uuid } -loading-announcement`
8662
@@ -104,131 +80,7 @@ const ButtonBase = forwardRef(
10480 } , [ innerRef ] )
10581 }
10682
107- if ( enabled ) {
108- if ( sxProp !== defaultSxProp ) {
109- return (
110- < ConditionalWrapper
111- // If anything is passsed to `loading`, we need the wrapper:
112- // If we just checked for `loading` as a boolean, the wrapper wouldn't be rendered
113- // when `loading` is `false`.
114- // Then, the component re-renders in a way that the button will lose focus when switching between loading states.
115- if = { typeof loading !== 'undefined' }
116- className = { block ? classes . ConditionalWrapper : undefined }
117- data-loading-wrapper
118- >
119- < Box
120- as = { Component }
121- sx = { sxProp }
122- aria-disabled = { loading ? true : undefined }
123- { ...rest }
124- ref = { innerRef }
125- className = { clsx ( classes . ButtonBase , className ) }
126- data-block = { block ? 'block' : null }
127- data-inactive = { inactive ? true : undefined }
128- data-loading = { Boolean ( loading ) }
129- data-no-visuals = { ! LeadingVisual && ! TrailingVisual && ! TrailingAction ? true : undefined }
130- data-size = { size }
131- data-variant = { variant }
132- data-label-wrap = { labelWrap }
133- aria-describedby = { [ loadingAnnouncementID , ariaDescribedBy ]
134- . filter ( descriptionID => Boolean ( descriptionID ) )
135- . join ( ' ' ) }
136- // aria-labelledby is needed because the accessible name becomes unset when the button is in a loading state.
137- // We only set it when the button is in a loading state because it will supercede the aria-label when the screen
138- // reader announces the button name.
139- aria-labelledby = {
140- loading
141- ? [ `${ uuid } -label` , ariaLabelledBy ] . filter ( labelID => Boolean ( labelID ) ) . join ( ' ' )
142- : ariaLabelledBy
143- }
144- id = { id }
145- onClick = { loading ? undefined : onClick }
146- >
147- { Icon ? (
148- loading ? (
149- < Spinner size = "small" />
150- ) : isElement ( Icon ) ? (
151- Icon
152- ) : (
153- < Icon />
154- )
155- ) : (
156- < >
157- < Box
158- as = "span"
159- data-component = "buttonContent"
160- sx = { getAlignContentSize ( alignContent ) }
161- className = { classes . ButtonContent }
162- >
163- {
164- /* If there are no leading/trailing visuals/actions to replace with a loading spinner,
165- render a loading spiner in place of the button content. */
166- loading &&
167- ! LeadingVisual &&
168- ! TrailingVisual &&
169- ! TrailingAction &&
170- renderModuleVisual ( Spinner , loading , 'loadingSpinner' , false )
171- }
172- {
173- /* Render a leading visual unless the button is in a loading state.
174- Then replace the leading visual with a loading spinner. */
175- LeadingVisual && renderModuleVisual ( LeadingVisual , Boolean ( loading ) , 'leadingVisual' , false )
176- }
177- { children && (
178- < span data-component = "text" className = { classes . Label } id = { loading ? `${ uuid } -label` : undefined } >
179- { children }
180- </ span >
181- ) }
182- {
183- /* If there is a count, render a counter label unless there is a trailing visual.
184- Then render the counter label as a trailing visual.
185- Replace the counter label or the trailing visual with a loading spinner if:
186- - the button is in a loading state
187- - there is no leading visual to replace with a loading spinner
188- */
189- count !== undefined && ! TrailingVisual
190- ? renderModuleVisual (
191- ( ) => (
192- < CounterLabel className = { classes . CounterLabel } data-component = "ButtonCounter" >
193- { count }
194- </ CounterLabel >
195- ) ,
196- Boolean ( loading ) && ! LeadingVisual ,
197- 'trailingVisual' ,
198- true ,
199- )
200- : TrailingVisual
201- ? renderModuleVisual (
202- TrailingVisual ,
203- Boolean ( loading ) && ! LeadingVisual ,
204- 'trailingVisual' ,
205- false ,
206- )
207- : null
208- }
209- </ Box >
210- {
211- /* If there is a trailing action, render it unless the button is in a loading state
212- and there is no leading or trailing visual to replace with a loading spinner. */
213- TrailingAction &&
214- renderModuleVisual (
215- TrailingAction ,
216- Boolean ( loading ) && ! LeadingVisual && ! TrailingVisual ,
217- 'trailingAction' ,
218- false ,
219- )
220- }
221- </ >
222- ) }
223- </ Box >
224- { loading && (
225- < VisuallyHidden >
226- < AriaStatus id = { loadingAnnouncementID } > { loadingAnnouncement } </ AriaStatus >
227- </ VisuallyHidden >
228- ) }
229- </ ConditionalWrapper >
230- )
231- }
83+ if ( sxProp !== defaultSxProp ) {
23284 return (
23385 < ConditionalWrapper
23486 // If anything is passsed to `loading`, we need the wrapper:
@@ -239,10 +91,11 @@ const ButtonBase = forwardRef(
23991 className = { block ? classes . ConditionalWrapper : undefined }
24092 data-loading-wrapper
24193 >
242- < Component
94+ < Box
95+ as = { Component }
96+ sx = { sxProp }
24397 aria-disabled = { loading ? true : undefined }
24498 { ...rest }
245- // @ts -ignore temporary disable as we migrate to css modules, until we remove PolymorphicForwardRefComponent
24699 ref = { innerRef }
247100 className = { clsx ( classes . ButtonBase , className ) }
248101 data-block = { block ? 'block' : null }
@@ -262,7 +115,6 @@ const ButtonBase = forwardRef(
262115 loading ? [ `${ uuid } -label` , ariaLabelledBy ] . filter ( labelID => Boolean ( labelID ) ) . join ( ' ' ) : ariaLabelledBy
263116 }
264117 id = { id }
265- // @ts -ignore temporary disable as we migrate to css modules, until we remove PolymorphicForwardRefComponent
266118 onClick = { loading ? undefined : onClick }
267119 >
268120 { Icon ? (
@@ -275,7 +127,12 @@ const ButtonBase = forwardRef(
275127 )
276128 ) : (
277129 < >
278- < span data-component = "buttonContent" data-align = { alignContent } className = { classes . ButtonContent } >
130+ < Box
131+ as = "span"
132+ data-component = "buttonContent"
133+ sx = { getAlignContentSize ( alignContent ) }
134+ className = { classes . ButtonContent }
135+ >
279136 {
280137 /* If there are no leading/trailing visuals/actions to replace with a loading spinner,
281138 render a loading spiner in place of the button content. */
@@ -287,7 +144,7 @@ const ButtonBase = forwardRef(
287144 }
288145 {
289146 /* Render a leading visual unless the button is in a loading state.
290- Then replace the leading visual with a loading spinner. */
147+ Then replace the leading visual with a loading spinner. */
291148 LeadingVisual && renderModuleVisual ( LeadingVisual , Boolean ( loading ) , 'leadingVisual' , false )
292149 }
293150 { children && (
@@ -322,7 +179,7 @@ const ButtonBase = forwardRef(
322179 )
323180 : null
324181 }
325- </ span >
182+ </ Box >
326183 {
327184 /* If there is a trailing action, render it unless the button is in a loading state
328185 and there is no leading or trailing visual to replace with a loading spinner. */
@@ -336,7 +193,7 @@ const ButtonBase = forwardRef(
336193 }
337194 </ >
338195 ) }
339- </ Component >
196+ </ Box >
340197 { loading && (
341198 < VisuallyHidden >
342199 < AriaStatus id = { loadingAnnouncementID } > { loadingAnnouncement } </ AriaStatus >
@@ -345,29 +202,28 @@ const ButtonBase = forwardRef(
345202 </ ConditionalWrapper >
346203 )
347204 }
348-
349205 return (
350206 < ConditionalWrapper
351207 // If anything is passsed to `loading`, we need the wrapper:
352208 // If we just checked for `loading` as a boolean, the wrapper wouldn't be rendered
353209 // when `loading` is `false`.
354210 // Then, the component re-renders in a way that the button will lose focus when switching between loading states.
355211 if = { typeof loading !== 'undefined' }
356- sx = { { display : block ? 'block' : 'inline-block' } }
212+ className = { block ? classes . ConditionalWrapper : undefined }
357213 data-loading-wrapper
358214 >
359- < StyledButton
360- as = { Component }
361- sx = { sxStyles }
215+ < Component
362216 aria-disabled = { loading ? true : undefined }
363217 { ...rest }
218+ // @ts -ignore temporary disable as we migrate to css modules, until we remove PolymorphicForwardRefComponent
364219 ref = { innerRef }
365- className = { className }
220+ className = { clsx ( classes . ButtonBase , className ) }
366221 data-block = { block ? 'block' : null }
367222 data-inactive = { inactive ? true : undefined }
368223 data-loading = { Boolean ( loading ) }
369224 data-no-visuals = { ! LeadingVisual && ! TrailingVisual && ! TrailingAction ? true : undefined }
370225 data-size = { size }
226+ data-variant = { variant }
371227 data-label-wrap = { labelWrap }
372228 aria-describedby = { [ loadingAnnouncementID , ariaDescribedBy ]
373229 . filter ( descriptionID => Boolean ( descriptionID ) )
@@ -379,6 +235,7 @@ const ButtonBase = forwardRef(
379235 loading ? [ `${ uuid } -label` , ariaLabelledBy ] . filter ( labelID => Boolean ( labelID ) ) . join ( ' ' ) : ariaLabelledBy
380236 }
381237 id = { id }
238+ // @ts -ignore temporary disable as we migrate to css modules, until we remove PolymorphicForwardRefComponent
382239 onClick = { loading ? undefined : onClick }
383240 >
384241 { Icon ? (
@@ -391,23 +248,23 @@ const ButtonBase = forwardRef(
391248 )
392249 ) : (
393250 < >
394- < Box as = " span" data-component = "buttonContent" sx = { getAlignContentSize ( alignContent ) } >
251+ < span data-component = "buttonContent" data-align = { alignContent } className = { classes . ButtonContent } >
395252 {
396253 /* If there are no leading/trailing visuals/actions to replace with a loading spinner,
397254 render a loading spiner in place of the button content. */
398255 loading &&
399256 ! LeadingVisual &&
400257 ! TrailingVisual &&
401258 ! TrailingAction &&
402- renderVisual ( Spinner , loading , 'loadingSpinner' )
259+ renderModuleVisual ( Spinner , loading , 'loadingSpinner' , false )
403260 }
404261 {
405262 /* Render a leading visual unless the button is in a loading state.
406263 Then replace the leading visual with a loading spinner. */
407- LeadingVisual && renderVisual ( LeadingVisual , Boolean ( loading ) , 'leadingVisual' )
264+ LeadingVisual && renderModuleVisual ( LeadingVisual , Boolean ( loading ) , 'leadingVisual' , false )
408265 }
409266 { children && (
410- < span data-component = "text" id = { loading ? `${ uuid } -label` : undefined } >
267+ < span data-component = "text" className = { classes . Label } id = { loading ? `${ uuid } -label` : undefined } >
411268 { children }
412269 </ span >
413270 ) }
@@ -419,25 +276,35 @@ const ButtonBase = forwardRef(
419276 - there is no leading visual to replace with a loading spinner
420277 */
421278 count !== undefined && ! TrailingVisual
422- ? renderVisual (
423- ( ) => < CounterLabel data-component = "ButtonCounter" > { count } </ CounterLabel > ,
279+ ? renderModuleVisual (
280+ ( ) => (
281+ < CounterLabel className = { classes . CounterLabel } data-component = "ButtonCounter" >
282+ { count }
283+ </ CounterLabel >
284+ ) ,
424285 Boolean ( loading ) && ! LeadingVisual ,
425286 'trailingVisual' ,
287+ true ,
426288 )
427289 : TrailingVisual
428- ? renderVisual ( TrailingVisual , Boolean ( loading ) && ! LeadingVisual , 'trailingVisual' )
290+ ? renderModuleVisual ( TrailingVisual , Boolean ( loading ) && ! LeadingVisual , 'trailingVisual' , false )
429291 : null
430292 }
431- </ Box >
293+ </ span >
432294 {
433295 /* If there is a trailing action, render it unless the button is in a loading state
434296 and there is no leading or trailing visual to replace with a loading spinner. */
435297 TrailingAction &&
436- renderVisual ( TrailingAction , Boolean ( loading ) && ! LeadingVisual && ! TrailingVisual , 'trailingAction' )
298+ renderModuleVisual (
299+ TrailingAction ,
300+ Boolean ( loading ) && ! LeadingVisual && ! TrailingVisual ,
301+ 'trailingAction' ,
302+ false ,
303+ )
437304 }
438305 </ >
439306 ) }
440- </ StyledButton >
307+ </ Component >
441308 { loading && (
442309 < VisuallyHidden >
443310 < AriaStatus id = { loadingAnnouncementID } > { loadingAnnouncement } </ AriaStatus >
0 commit comments