Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
29efb92
remove sx support from FormControl.tsx
llastflowers Aug 27, 2025
07ffc19
remove sx from docs and related components
llastflowers Aug 27, 2025
8ca9211
Update FormControl to remove sx support
llastflowers Aug 27, 2025
37a7aa7
Merge branch 'main' into llastflowers/5794/no-sx-FormControl
llastflowers Sep 10, 2025
5bdcf7d
Merge branch 'main' into llastflowers/5794/no-sx-FormControl
llastflowers Sep 11, 2025
ca74bcb
Merge remote-tracking branch 'origin/main' into llastflowers/5794/no-…
liuliu-dev Sep 22, 2025
4931978
fix lint, export formcontrol in styled-react
liuliu-dev Sep 22, 2025
992034a
resolve conflicts
liuliu-dev Sep 22, 2025
68477d0
Merge branch 'main' into llastflowers/5794/no-sx-FormControl
liuliu-dev Sep 22, 2025
e190509
Merge remote-tracking branch 'origin/llastflowers/5794/no-sx-FormCont…
liuliu-dev Sep 22, 2025
fab535c
remove sx in selectpanel stories
liuliu-dev Sep 22, 2025
d190d57
formcontrol label
liuliu-dev Sep 23, 2025
0906165
sub components
liuliu-dev Sep 23, 2025
2f307be
func style
liuliu-dev Sep 23, 2025
18b87ea
caption props
liuliu-dev Sep 23, 2025
445d004
Merge branch 'llastflowers/5794/no-sx-FormControl' of https://github.…
liuliu-dev Sep 24, 2025
8905aa2
Merge branch 'liuliu/formcontrol-sx-removal' into llastflowers/5794/n…
liuliu-dev Sep 24, 2025
a875679
resolve conflicts
liuliu-dev Sep 24, 2025
2a5e15e
formcontrol label with sx
liuliu-dev Sep 24, 2025
ef97e3f
prettier
liuliu-dev Sep 24, 2025
385acbe
label fix
liuliu-dev Sep 24, 2025
355004d
type
liuliu-dev Sep 24, 2025
17b735d
Merge remote-tracking branch 'origin/main' into llastflowers/5794/no-…
liuliu-dev Sep 26, 2025
e399b85
Merge branch 'main' into llastflowers/5794/no-sx-FormControl
liuliu-dev Sep 26, 2025
7c56124
Merge branch 'llastflowers/5794/no-sx-FormControl' of https://github.…
liuliu-dev Sep 26, 2025
8215212
remove styled in leadingvisual
liuliu-dev Sep 26, 2025
183183f
lint fix
liuliu-dev Sep 26, 2025
57822f9
format
liuliu-dev Sep 26, 2025
e81d299
conflicts
liuliu-dev Sep 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/grumpy-lobsters-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/react': major
---

Update FormControl component to no longer support sx
27 changes: 1 addition & 26 deletions packages/react/src/FormControl/FormControl.docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,6 @@
"description": "Class name(s) for custom styling.",
"defaultValue": ""
},
{
"name": "sx",
"type": "SystemStyleObject",
"deprecated": true
},
{
"name": "ref",
"type": "React.RefObject<HTMLDivElement>"
Expand Down Expand Up @@ -152,11 +147,6 @@
"type": "string",
"description": "Class name(s) for custom styling.",
"defaultValue": ""
},
{
"name": "sx",
"type": "SystemStyleObject",
"deprecated": true
}
]
},
Expand All @@ -174,11 +164,6 @@
"type": "React.ReactNode",
"defaultValue": "",
"description": "The content (usually just text) that is rendered to give contextual info about the field"
},
{
"name": "sx",
"type": "SystemStyleObject",
"deprecated": true
}
]
},
Expand All @@ -203,11 +188,6 @@
"type": "string",
"description": "May be used to override the ID assigned by FormControl's React Context",
"defaultValue": ""
},
{
"name": "sx",
"type": "SystemStyleObject",
"deprecated": true
}
]
},
Expand All @@ -219,13 +199,8 @@
"type": "React.ReactNode",
"defaultValue": "",
"description": "The visual to render before the choice input's label"
},
{
"name": "sx",
"type": "SystemStyleObject",
"deprecated": true
}
]
}
]
}
}
16 changes: 6 additions & 10 deletions packages/react/src/FormControl/FormControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import Textarea from '../Textarea'
import {CheckboxOrRadioGroupContext} from '../internal/components/CheckboxOrRadioGroup'
import ValidationAnimationContainer from '../internal/components/ValidationAnimationContainer'
import {useSlots} from '../hooks/useSlots'
import type {SxProp} from '../sx'
import {useId} from '../hooks/useId'
import {FormControlCaption} from './FormControlCaption'
import FormControlLabel from './FormControlLabel'
Expand All @@ -20,7 +19,6 @@ import FormControlValidation from './_FormControlValidation'
import {FormControlContextProvider} from './_FormControlContext'
import {warning} from '../utils/warning'
import classes from './FormControl.module.css'
import {BoxWithFallback} from '../internal/components/BoxWithFallback'

export type FormControlProps = {
children?: React.ReactNode
Expand All @@ -42,10 +40,10 @@ export type FormControlProps = {
*/
layout?: 'horizontal' | 'vertical'
className?: string
} & SxProp
}

const FormControl = React.forwardRef<HTMLDivElement, FormControlProps>(
({children, disabled: disabledProp, layout = 'vertical', id: idProp, required, sx, className}, ref) => {
({children, disabled: disabledProp, layout = 'vertical', id: idProp, required, className}, ref) => {
const [slots, childrenWithoutSlots] = useSlots(children, {
caption: FormControlCaption,
label: FormControlLabel,
Expand Down Expand Up @@ -168,19 +166,17 @@ const FormControl = React.forwardRef<HTMLDivElement, FormControlProps>(
}}
>
{isChoiceInput || layout === 'horizontal' ? (
<BoxWithFallback
<div
ref={ref}
data-has-leading-visual={slots.leadingVisual ? '' : undefined}
sx={sx}
className={clsx(className, classes.ControlHorizontalLayout)}
>
{InputChildren}
</BoxWithFallback>
</div>
) : (
<BoxWithFallback
<div
ref={ref}
data-has-label={!isLabelHidden ? '' : undefined}
sx={sx}
className={clsx(className, classes.ControlVerticalLayout)}
>
{slots.label}
Expand All @@ -207,7 +203,7 @@ const FormControl = React.forwardRef<HTMLDivElement, FormControlProps>(
<ValidationAnimationContainer show>{slots.validation}</ValidationAnimationContainer>
) : null}
{slots.caption}
</BoxWithFallback>
</div>
)}
</FormControlContextProvider>
)
Expand Down
20 changes: 7 additions & 13 deletions packages/react/src/FormControl/FormControlCaption.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,25 @@
import {clsx} from 'clsx'
import type React from 'react'
import Text from '../Text'
import type {SxProp} from '../sx'
import classes from './FormControlCaption.module.css'
import {useFormControlContext} from './_FormControlContext'
import {BoxWithFallback} from '../internal/components/BoxWithFallback'

type FormControlCaptionProps = React.PropsWithChildren<
{
id?: string
className?: string
} & SxProp
>
export type FormControlCaptionProps = React.PropsWithChildren<{
id?: string
className?: string
}>

function FormControlCaption({id, children, sx, className}: FormControlCaptionProps) {
function FormControlCaption({id, children, className}: FormControlCaptionProps) {
const {captionId, disabled} = useFormControlContext()

return (
<BoxWithFallback
as={Text}
<Text
id={id ?? captionId}
className={clsx(className, classes.Caption)}
data-control-disabled={disabled ? '' : undefined}
sx={sx}
>
{children}
</BoxWithFallback>
</Text>
)
}

Expand Down
7 changes: 2 additions & 5 deletions packages/react/src/FormControl/FormControlLabel.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type React from 'react'
import type {SxProp} from '../sx'
import {useFormControlContext} from './_FormControlContext'
import {InputLabel} from '../internal/components/InputLabel'

Expand All @@ -12,11 +11,11 @@ export type Props = {
requiredIndicator?: boolean
id?: string
className?: string
} & SxProp
}

const FormControlLabel: React.FC<
React.PropsWithChildren<{htmlFor?: string} & React.ComponentProps<typeof InputLabel> & Props>
> = ({as, children, htmlFor, id, visuallyHidden, requiredIndicator = true, requiredText, sx, className, ...props}) => {
> = ({as, children, htmlFor, id, visuallyHidden, requiredIndicator = true, requiredText, className, ...props}) => {
const {disabled, id: formControlId, required} = useFormControlContext()

/**
Expand All @@ -33,7 +32,6 @@ const FormControlLabel: React.FC<
requiredText,
requiredIndicator,
disabled,
sx,
...props,
}
: {
Expand All @@ -46,7 +44,6 @@ const FormControlLabel: React.FC<
requiredText,
requiredIndicator,
disabled,
sx,
...props,
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.LeadingVisual {
--leadingVisual-size: 16px;

color: var(--fgColor-default);
display: flex;
align-items: center;

&:where([data-control-disabled]) {
color: var(--control-fgColor-disabled);
}

& > * {
min-width: var(--leadingVisual-size);
min-height: var(--leadingVisual-size);
fill: currentColor;
}

&:where([data-has-caption]) {
--leadingVisual-size: 24px;
}
}
38 changes: 5 additions & 33 deletions packages/react/src/FormControl/FormControlLeadingVisual.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,18 @@
import type React from 'react'
import {get} from '../constants'
import type {SxProp} from '../sx'
import {useFormControlContext} from './_FormControlContext'
import styled from 'styled-components'
import sx from '../sx'
import classes from './FormControlLeadingVisual.module.css'

const FormControlLeadingVisual: React.FC<React.PropsWithChildren<SxProp>> = ({children, sx}) => {
const FormControlLeadingVisual: React.FC<React.PropsWithChildren> = ({children}) => {
const {disabled, captionId} = useFormControlContext()
return (
<StyledLeadingVisual
<div
className={classes.LeadingVisual}
data-control-disabled={disabled ? '' : undefined}
data-has-caption={captionId ? '' : undefined}
sx={sx}
>
{children}
</StyledLeadingVisual>
</div>
)
}

const StyledLeadingVisual = styled.div`
--leadingVisual-size: ${get('fontSizes.2')};

color: var(--fgColor-default);

display: flex;
align-items: center; /* Vertical alignment */

&:where([data-control-disabled]) {
color: var(--control-fgColor-disabled);
}

& > * {
min-width: var(--leadingVisual-size);
min-height: var(--leadingVisual-size);
fill: currentColor;
}

&:where([data-has-caption]) {
--leadingVisual-size: ${get('fontSizes.4')};
}

${sx}
`

export default FormControlLeadingVisual
4 changes: 4 additions & 0 deletions packages/react/src/FormControl/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
export {useFormControlForwardedProps} from './_FormControlContext'
export {default} from './FormControl'
export type {FormControlProps} from './FormControl'
export type {FormControlCaptionProps} from './FormControlCaption'
export type {Props as FormControlLabelProps} from './FormControlLabel'
export type {FormControlValidationProps} from './_FormControlValidation'
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ exports[`@primer/react > should not update exports without a semver change 1`] =
"type FocusTrapHookSettings",
"type FocusZoneHookSettings",
"FormControl",
"type FormControlCaptionProps",
"type FormControlLabelProps",
"type FormControlProps",
"type FormControlValidationProps",
"Header",
"type HeaderItemProps",
"type HeaderLinkProps",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,7 @@
max-height: 0;
overflow: hidden;
}

.FormControl {
margin-bottom: var(--base-size-8);
}
Original file line number Diff line number Diff line change
Expand Up @@ -1069,11 +1069,11 @@ const CreateNewLabelDialog = ({
Note this Dialog is not accessible. Do not copy this.
</Flash>
<form onSubmit={onSubmit}>
<FormControl sx={{marginBottom: 2}}>
<FormControl className={classes.FormControl}>
<FormControl.Label>Name</FormControl.Label>
<TextInput name="name" block defaultValue={initialValue} autoFocus />
</FormControl>
<FormControl sx={{marginBottom: 2}}>
<FormControl className={classes.FormControl}>
<FormControl.Label>Color</FormControl.Label>
<TextInput name="color" block defaultValue="fae17d" leadingVisual="#" />
</FormControl>
Expand Down
6 changes: 6 additions & 0 deletions packages/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ export {ConfirmationDialog} from './ConfirmationDialog/ConfirmationDialog'
export {default as Flash} from './Flash'
export type {FlashProps} from './Flash'
export {default as FormControl} from './FormControl'
export type {
FormControlProps,
FormControlCaptionProps,
FormControlLabelProps,
FormControlValidationProps,
} from './FormControl'
export {useFormControlForwardedProps} from './FormControl'
export {default as Header} from './Header'
export type {HeaderProps, HeaderItemProps, HeaderLinkProps} from './Header'
Expand Down
44 changes: 44 additions & 0 deletions packages/styled-react/src/components/FormControl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
Box,
FormControl as PrimerFormControl,
type FormControlProps as PrimerFormControlProps,
type FormControlCaptionProps as PrimerFormControlCaptionProps,
type FormControlValidationProps as PrimerFormControlValidationProps,
type SxProp,
} from '@primer/react'
import {forwardRef, type PropsWithChildren} from 'react'

type FormControlProps = PropsWithChildren<PrimerFormControlProps> & SxProp

const FormControlImpl = forwardRef<HTMLDivElement, FormControlProps>(function FormControl(props, ref) {
return <Box ref={ref} as={PrimerFormControl} {...props} />
})

type FormControlCaptionProps = PropsWithChildren<PrimerFormControlCaptionProps> & SxProp
const FormControlCaption = (props: FormControlCaptionProps) => {
return <Box as={PrimerFormControl.Caption} {...props} />
}

type FormControlValidationProps = PropsWithChildren<PrimerFormControlValidationProps> & SxProp

const FormControlValidation = (props: FormControlValidationProps) => {
return <Box as={PrimerFormControl.Validation} {...props} />
}

const FormControlLeadingVisual = (props: PropsWithChildren<SxProp>) => {
return <Box as={PrimerFormControl.LeadingVisual} {...props} />
}

const FormControl = Object.assign(FormControlImpl, {
Caption: FormControlCaption,
LeadingVisual: FormControlLeadingVisual,
Validation: FormControlValidation,
Label: PrimerFormControl.Label,
}) as typeof FormControlImpl & {
Caption: typeof FormControlCaption
LeadingVisual: typeof FormControlLeadingVisual
Validation: typeof FormControlValidation
Label: typeof PrimerFormControl.Label
}

export {FormControl, type FormControlProps}
2 changes: 1 addition & 1 deletion packages/styled-react/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export {Button} from '@primer/react'
export {CheckboxGroup} from '@primer/react'
export {CircleBadge} from '@primer/react'
export {Details} from '@primer/react'
export {FormControl} from '@primer/react'
export {Heading} from '@primer/react'
export {IconButton} from '@primer/react'
export {Label} from '@primer/react'
Expand Down Expand Up @@ -36,6 +35,7 @@ export {Checkbox, type CheckboxProps} from './components/Checkbox'
export {CounterLabel, type CounterLabelProps} from './components/CounterLabel'
export {Dialog, type DialogProps} from './components/Dialog'
export {Flash} from './components/Flash'
export {FormControl, type FormControlProps} from './components/FormControl'
export {Header, type HeaderProps} from './components/Header'
export {Link, type LinkProps} from './components/Link'
export {LinkButton, type LinkButtonProps} from './components/LinkButton'
Expand Down
Loading