Skip to content

Commit 88b8533

Browse files
authored
refactor(FormControl): update FormControl to use CSS Modules behind flag (#5578)
* Revert "Revert "refactor(FormControl): update FormControl to use CSS Modules …" This reverts commit 43afd36. * update InputLabel * fix css variable:
1 parent d76cd26 commit 88b8533

13 files changed

+440
-167
lines changed

.changeset/gentle-stingrays-search.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@primer/react": minor
3+
---
4+
5+
Update FormControl to use CSS Modules behind feature flag

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
.ControlHorizontalLayout {
2+
display: flex;
3+
4+
&:where([data-has-leading-visual]) {
5+
align-items: center;
6+
}
7+
}
8+
9+
.ControlVerticalLayout {
10+
display: flex;
11+
flex-direction: column;
12+
align-items: flex-start;
13+
14+
& > *:not(label) + * {
15+
margin-top: var(--base-size-4);
16+
}
17+
18+
&[data-has-label] > * + * {
19+
margin-top: var(--base-size-4);
20+
}
21+
}
22+
23+
.ControlChoiceInputs > input {
24+
margin-right: 0;
25+
margin-left: 0;
26+
}
27+
28+
.LabelContainer {
29+
> * {
30+
/* stylelint-disable-next-line primer/spacing */
31+
padding-left: var(--stack-gap-condensed);
32+
}
33+
34+
> label {
35+
font-weight: var(--base-text-weight-normal);
36+
}
37+
}
38+
39+
.LeadingVisual {
40+
margin-left: var(--base-size-8);
41+
color: var(--fgColor-muted);
42+
43+
&:where([data-disabled]) {
44+
color: var(--control-fgColor-disabled);
45+
}
46+
47+
> * {
48+
min-width: var(--text-body-size-large);
49+
min-height: var(--text-body-size-large);
50+
fill: currentColor;
51+
}
52+
53+
> *:where([data-has-caption]) {
54+
min-width: var(--base-size-24);
55+
min-height: var(--base-size-24);
56+
}
57+
}

packages/react/src/FormControl/FormControl.tsx

Lines changed: 134 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
import {clsx} from 'clsx'
12
import React, {useContext} from 'react'
23
import Autocomplete from '../Autocomplete'
3-
import Box from '../Box'
44
import Checkbox from '../Checkbox'
55
import Radio from '../Radio'
66
import Select from '../Select/Select'
@@ -10,7 +10,6 @@ import TextInputWithTokens from '../TextInputWithTokens'
1010
import Textarea from '../Textarea'
1111
import {CheckboxOrRadioGroupContext} from '../internal/components/CheckboxOrRadioGroup'
1212
import ValidationAnimationContainer from '../internal/components/ValidationAnimationContainer'
13-
import {get} from '../constants'
1413
import {useSlots} from '../hooks/useSlots'
1514
import type {SxProp} from '../sx'
1615
import {useId} from '../hooks/useId'
@@ -20,6 +19,12 @@ import FormControlLeadingVisual from './FormControlLeadingVisual'
2019
import FormControlValidation from './_FormControlValidation'
2120
import {FormControlContextProvider} from './_FormControlContext'
2221
import {warning} from '../utils/warning'
22+
import styled from 'styled-components'
23+
import sx from '../sx'
24+
import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent'
25+
import {cssModulesFlag} from './feature-flags'
26+
import {useFeatureFlag} from '../FeatureFlags'
27+
import classes from './FormControl.module.css'
2328

2429
export type FormControlProps = {
2530
children?: React.ReactNode
@@ -45,6 +50,7 @@ export type FormControlProps = {
4550

4651
const FormControl = React.forwardRef<HTMLDivElement, FormControlProps>(
4752
({children, disabled: disabledProp, layout = 'vertical', id: idProp, required, sx, className}, ref) => {
53+
const enabled = useFeatureFlag(cssModulesFlag)
4854
const [slots, childrenWithoutSlots] = useSlots(children, {
4955
caption: FormControlCaption,
5056
label: FormControlLabel,
@@ -127,69 +133,62 @@ const FormControl = React.forwardRef<HTMLDivElement, FormControlProps>(
127133
}}
128134
>
129135
{isChoiceInput || layout === 'horizontal' ? (
130-
<Box
136+
<StyledHorizontalLayout
131137
ref={ref}
132-
display="flex"
133-
alignItems={slots.leadingVisual ? 'center' : undefined}
138+
data-has-leading-visual={slots.leadingVisual ? '' : undefined}
134139
sx={sx}
135-
className={className}
140+
className={clsx(className, {
141+
[classes.ControlHorizontalLayout]: enabled,
142+
})}
136143
>
137-
<Box sx={{'> input': {marginLeft: 0, marginRight: 0}}}>
138-
{React.isValidElement(InputComponent) &&
139-
React.cloneElement(
140-
InputComponent as React.ReactElement<{
141-
id: string
142-
disabled: boolean
143-
required: boolean
144-
['aria-describedby']: string
145-
}>,
146-
{
147-
id,
148-
disabled,
149-
// allow checkboxes to be required
150-
required: required && !isRadioInput,
151-
['aria-describedby']: captionId as string,
152-
},
153-
)}
144+
<StyledChoiceInputs className={classes.ControlChoiceInputs}>
145+
{React.isValidElement(InputComponent)
146+
? React.cloneElement(
147+
InputComponent as React.ReactElement<{
148+
id: string
149+
disabled: boolean
150+
required: boolean
151+
['aria-describedby']: string
152+
}>,
153+
{
154+
id,
155+
disabled,
156+
// allow checkboxes to be required
157+
required: required && !isRadioInput,
158+
['aria-describedby']: captionId as string,
159+
},
160+
)
161+
: null}
154162
{childrenWithoutSlots.filter(
155163
child =>
156164
React.isValidElement(child) &&
157165
![Checkbox, Radio].some(inputComponent => child.type === inputComponent),
158166
)}
159-
</Box>
160-
{slots.leadingVisual && (
161-
<Box
162-
color={disabled ? 'fg.muted' : 'fg.default'}
163-
sx={{
164-
'> *': {
165-
minWidth: slots.caption ? get('fontSizes.4') : get('fontSizes.2'),
166-
minHeight: slots.caption ? get('fontSizes.4') : get('fontSizes.2'),
167-
fill: 'currentColor',
168-
},
169-
}}
170-
ml={2}
167+
</StyledChoiceInputs>
168+
{slots.leadingVisual ? (
169+
<StyledLeadingVisual
170+
className={clsx({
171+
[classes.LeadingVisual]: enabled,
172+
})}
173+
data-disabled={disabled ? '' : undefined}
174+
data-has-caption={slots.caption ? '' : undefined}
171175
>
172176
{slots.leadingVisual}
173-
</Box>
174-
)}
175-
<Box
176-
sx={{
177-
'> *': {paddingLeft: 'var(--stack-gap-condensed)'},
178-
'> label': {fontWeight: 'var(--base-text-weight-normal)'},
179-
}}
180-
>
177+
</StyledLeadingVisual>
178+
) : null}
179+
<StyledLabelContainer className={classes.LabelContainer}>
181180
{slots.label}
182181
{slots.caption}
183-
</Box>
184-
</Box>
182+
</StyledLabelContainer>
183+
</StyledHorizontalLayout>
185184
) : (
186-
<Box
185+
<StyledVerticalLayout
187186
ref={ref}
188-
display="flex"
189-
flexDirection="column"
190-
alignItems="flex-start"
191-
sx={{...(isLabelHidden ? {'> *:not(label) + *': {marginTop: 1}} : {'> * + *': {marginTop: 1}}), ...sx}}
192-
className={className}
187+
data-has-label={!isLabelHidden ? '' : undefined}
188+
sx={sx}
189+
className={clsx(className, {
190+
[classes.ControlVerticalLayout]: enabled,
191+
})}
193192
>
194193
{slots.label}
195194
{React.isValidElement(InputComponent) &&
@@ -215,13 +214,96 @@ const FormControl = React.forwardRef<HTMLDivElement, FormControlProps>(
215214
<ValidationAnimationContainer show>{slots.validation}</ValidationAnimationContainer>
216215
) : null}
217216
{slots.caption}
218-
</Box>
217+
</StyledVerticalLayout>
219218
)}
220219
</FormControlContextProvider>
221220
)
222221
},
223222
)
224223

224+
const StyledHorizontalLayout = toggleStyledComponent(
225+
cssModulesFlag,
226+
'div',
227+
styled.div`
228+
display: flex;
229+
230+
&:where([data-has-leading-visual]) {
231+
align-items: center;
232+
}
233+
234+
${sx}
235+
`,
236+
)
237+
238+
const StyledChoiceInputs = toggleStyledComponent(
239+
cssModulesFlag,
240+
'div',
241+
styled.div`
242+
> input {
243+
margin-left: 0;
244+
margin-right: 0;
245+
}
246+
`,
247+
)
248+
249+
const StyledLabelContainer = toggleStyledComponent(
250+
cssModulesFlag,
251+
'div',
252+
styled.div`
253+
> * {
254+
padding-left: var(--stack-gap-condensed);
255+
}
256+
257+
> label {
258+
font-weight: var(--base-text-weight-normal);
259+
}
260+
`,
261+
)
262+
263+
const StyledVerticalLayout = toggleStyledComponent(
264+
cssModulesFlag,
265+
'div',
266+
styled.div`
267+
display: flex;
268+
flex-direction: column;
269+
align-items: flex-start;
270+
271+
& > *:not(label) + * {
272+
margin-top: var(--base-size-4);
273+
}
274+
275+
&:where([data-has-label]) > * + * {
276+
margin-top: var(--base-size-4);
277+
}
278+
279+
${sx}
280+
`,
281+
)
282+
283+
const StyledLeadingVisual = toggleStyledComponent(
284+
cssModulesFlag,
285+
'div',
286+
styled.div`
287+
color: var(--fgColor-default);
288+
margin-left: var(--base-size-8);
289+
290+
&:where([data-disabled]) {
291+
color: var(--fgColor-muted);
292+
}
293+
294+
> * {
295+
fill: currentColor;
296+
min-width: var(--text-body-size-large);
297+
min-height: var(--text-body-size-large);
298+
}
299+
300+
> *:where([data-has-caption]) {
301+
min-width: var(--base-size-24);
302+
min-height: var(--base-size-24);
303+
}
304+
`,
305+
)
306+
225307
export default Object.assign(FormControl, {
226308
Caption: FormControlCaption,
227309
Label: FormControlLabel,
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.Caption {
2+
display: block;
3+
font-size: var(--text-body-size-small);
4+
color: var(--fgColor-muted);
5+
6+
&:where([data-control-disabled]) {
7+
color: var(--control-fgColor-disabled);
8+
}
9+
}

0 commit comments

Comments
 (0)