1
1
import type { ComponentPropsWithRef } from 'react'
2
- import React , { forwardRef , useMemo } from 'react'
2
+ import React , { forwardRef } from 'react'
3
3
import type { ForwardRefComponent as PolymorphicForwardRefComponent } from '../utils/polymorphic'
4
4
import Box from '../Box'
5
- import type { BetterSystemStyleObject } from '../sx'
6
- import { merge } from '../sx'
7
- import { useTheme } from '../ThemeProvider'
8
5
import type { ButtonProps } from './types'
9
- import { StyledButton } from './types'
10
- import { getVariantStyles , getButtonStyles , getAlignContentSize } from './styles'
6
+ import { getAlignContentSize } from './styles'
11
7
import { useRefObjectAsForwardedRef } from '../hooks/useRefObjectAsForwardedRef'
12
8
import { defaultSxProp } from '../utils/defaultSxProp'
13
9
import { VisuallyHidden } from '../VisuallyHidden'
@@ -18,20 +14,8 @@ import {ConditionalWrapper} from '../internal/components/ConditionalWrapper'
18
14
import { AriaStatus } from '../live-region'
19
15
import { clsx } from 'clsx'
20
16
import classes from './ButtonBase.module.css'
21
- import { useFeatureFlag } from '../FeatureFlags'
22
17
import { isElement } from 'react-is'
23
18
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
-
35
19
const renderModuleVisual = (
36
20
Visual : React . ElementType | React . ReactElement ,
37
21
loading : boolean ,
@@ -70,17 +54,9 @@ const ButtonBase = forwardRef(
70
54
...rest
71
55
} = props
72
56
73
- const enabled = useFeatureFlag ( 'primer_react_css_modules_ga' )
74
57
const innerRef = React . useRef < HTMLButtonElement > ( null )
75
58
useRefObjectAsForwardedRef ( forwardedRef , innerRef )
76
59
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 ] )
84
60
const uuid = useId ( id )
85
61
const loadingAnnouncementID = `${ uuid } -loading-announcement`
86
62
@@ -104,131 +80,7 @@ const ButtonBase = forwardRef(
104
80
} , [ innerRef ] )
105
81
}
106
82
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 ) {
232
84
return (
233
85
< ConditionalWrapper
234
86
// If anything is passsed to `loading`, we need the wrapper:
@@ -239,10 +91,11 @@ const ButtonBase = forwardRef(
239
91
className = { block ? classes . ConditionalWrapper : undefined }
240
92
data-loading-wrapper
241
93
>
242
- < Component
94
+ < Box
95
+ as = { Component }
96
+ sx = { sxProp }
243
97
aria-disabled = { loading ? true : undefined }
244
98
{ ...rest }
245
- // @ts -ignore temporary disable as we migrate to css modules, until we remove PolymorphicForwardRefComponent
246
99
ref = { innerRef }
247
100
className = { clsx ( classes . ButtonBase , className ) }
248
101
data-block = { block ? 'block' : null }
@@ -262,7 +115,6 @@ const ButtonBase = forwardRef(
262
115
loading ? [ `${ uuid } -label` , ariaLabelledBy ] . filter ( labelID => Boolean ( labelID ) ) . join ( ' ' ) : ariaLabelledBy
263
116
}
264
117
id = { id }
265
- // @ts -ignore temporary disable as we migrate to css modules, until we remove PolymorphicForwardRefComponent
266
118
onClick = { loading ? undefined : onClick }
267
119
>
268
120
{ Icon ? (
@@ -275,7 +127,12 @@ const ButtonBase = forwardRef(
275
127
)
276
128
) : (
277
129
< >
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
+ >
279
136
{
280
137
/* If there are no leading/trailing visuals/actions to replace with a loading spinner,
281
138
render a loading spiner in place of the button content. */
@@ -287,7 +144,7 @@ const ButtonBase = forwardRef(
287
144
}
288
145
{
289
146
/* 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. */
291
148
LeadingVisual && renderModuleVisual ( LeadingVisual , Boolean ( loading ) , 'leadingVisual' , false )
292
149
}
293
150
{ children && (
@@ -322,7 +179,7 @@ const ButtonBase = forwardRef(
322
179
)
323
180
: null
324
181
}
325
- </ span >
182
+ </ Box >
326
183
{
327
184
/* If there is a trailing action, render it unless the button is in a loading state
328
185
and there is no leading or trailing visual to replace with a loading spinner. */
@@ -336,7 +193,7 @@ const ButtonBase = forwardRef(
336
193
}
337
194
</ >
338
195
) }
339
- </ Component >
196
+ </ Box >
340
197
{ loading && (
341
198
< VisuallyHidden >
342
199
< AriaStatus id = { loadingAnnouncementID } > { loadingAnnouncement } </ AriaStatus >
@@ -345,29 +202,28 @@ const ButtonBase = forwardRef(
345
202
</ ConditionalWrapper >
346
203
)
347
204
}
348
-
349
205
return (
350
206
< ConditionalWrapper
351
207
// If anything is passsed to `loading`, we need the wrapper:
352
208
// If we just checked for `loading` as a boolean, the wrapper wouldn't be rendered
353
209
// when `loading` is `false`.
354
210
// Then, the component re-renders in a way that the button will lose focus when switching between loading states.
355
211
if = { typeof loading !== 'undefined' }
356
- sx = { { display : block ? 'block' : 'inline-block' } }
212
+ className = { block ? classes . ConditionalWrapper : undefined }
357
213
data-loading-wrapper
358
214
>
359
- < StyledButton
360
- as = { Component }
361
- sx = { sxStyles }
215
+ < Component
362
216
aria-disabled = { loading ? true : undefined }
363
217
{ ...rest }
218
+ // @ts -ignore temporary disable as we migrate to css modules, until we remove PolymorphicForwardRefComponent
364
219
ref = { innerRef }
365
- className = { className }
220
+ className = { clsx ( classes . ButtonBase , className ) }
366
221
data-block = { block ? 'block' : null }
367
222
data-inactive = { inactive ? true : undefined }
368
223
data-loading = { Boolean ( loading ) }
369
224
data-no-visuals = { ! LeadingVisual && ! TrailingVisual && ! TrailingAction ? true : undefined }
370
225
data-size = { size }
226
+ data-variant = { variant }
371
227
data-label-wrap = { labelWrap }
372
228
aria-describedby = { [ loadingAnnouncementID , ariaDescribedBy ]
373
229
. filter ( descriptionID => Boolean ( descriptionID ) )
@@ -379,6 +235,7 @@ const ButtonBase = forwardRef(
379
235
loading ? [ `${ uuid } -label` , ariaLabelledBy ] . filter ( labelID => Boolean ( labelID ) ) . join ( ' ' ) : ariaLabelledBy
380
236
}
381
237
id = { id }
238
+ // @ts -ignore temporary disable as we migrate to css modules, until we remove PolymorphicForwardRefComponent
382
239
onClick = { loading ? undefined : onClick }
383
240
>
384
241
{ Icon ? (
@@ -391,23 +248,23 @@ const ButtonBase = forwardRef(
391
248
)
392
249
) : (
393
250
< >
394
- < Box as = " span" data-component = "buttonContent" sx = { getAlignContentSize ( alignContent ) } >
251
+ < span data-component = "buttonContent" data-align = { alignContent } className = { classes . ButtonContent } >
395
252
{
396
253
/* If there are no leading/trailing visuals/actions to replace with a loading spinner,
397
254
render a loading spiner in place of the button content. */
398
255
loading &&
399
256
! LeadingVisual &&
400
257
! TrailingVisual &&
401
258
! TrailingAction &&
402
- renderVisual ( Spinner , loading , 'loadingSpinner' )
259
+ renderModuleVisual ( Spinner , loading , 'loadingSpinner' , false )
403
260
}
404
261
{
405
262
/* Render a leading visual unless the button is in a loading state.
406
263
Then replace the leading visual with a loading spinner. */
407
- LeadingVisual && renderVisual ( LeadingVisual , Boolean ( loading ) , 'leadingVisual' )
264
+ LeadingVisual && renderModuleVisual ( LeadingVisual , Boolean ( loading ) , 'leadingVisual' , false )
408
265
}
409
266
{ children && (
410
- < span data-component = "text" id = { loading ? `${ uuid } -label` : undefined } >
267
+ < span data-component = "text" className = { classes . Label } id = { loading ? `${ uuid } -label` : undefined } >
411
268
{ children }
412
269
</ span >
413
270
) }
@@ -419,25 +276,35 @@ const ButtonBase = forwardRef(
419
276
- there is no leading visual to replace with a loading spinner
420
277
*/
421
278
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
+ ) ,
424
285
Boolean ( loading ) && ! LeadingVisual ,
425
286
'trailingVisual' ,
287
+ true ,
426
288
)
427
289
: TrailingVisual
428
- ? renderVisual ( TrailingVisual , Boolean ( loading ) && ! LeadingVisual , 'trailingVisual' )
290
+ ? renderModuleVisual ( TrailingVisual , Boolean ( loading ) && ! LeadingVisual , 'trailingVisual' , false )
429
291
: null
430
292
}
431
- </ Box >
293
+ </ span >
432
294
{
433
295
/* If there is a trailing action, render it unless the button is in a loading state
434
296
and there is no leading or trailing visual to replace with a loading spinner. */
435
297
TrailingAction &&
436
- renderVisual ( TrailingAction , Boolean ( loading ) && ! LeadingVisual && ! TrailingVisual , 'trailingAction' )
298
+ renderModuleVisual (
299
+ TrailingAction ,
300
+ Boolean ( loading ) && ! LeadingVisual && ! TrailingVisual ,
301
+ 'trailingAction' ,
302
+ false ,
303
+ )
437
304
}
438
305
</ >
439
306
) }
440
- </ StyledButton >
307
+ </ Component >
441
308
{ loading && (
442
309
< VisuallyHidden >
443
310
< AriaStatus id = { loadingAnnouncementID } > { loadingAnnouncement } </ AriaStatus >
0 commit comments