Skip to content
Merged
5 changes: 5 additions & 0 deletions .changeset/hungry-pumas-poke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/react': minor
---

Removes feature flag for FormControl
186 changes: 61 additions & 125 deletions packages/react/src/FormControl/FormControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,8 @@ import FormControlLeadingVisual from './FormControlLeadingVisual'
import FormControlValidation from './_FormControlValidation'
import {FormControlContextProvider} from './_FormControlContext'
import {warning} from '../utils/warning'
import styled from 'styled-components'
import sx from '../sx'
import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent'
import {cssModulesFlag} from './feature-flags'
import {useFeatureFlag} from '../FeatureFlags'
import classes from './FormControl.module.css'
import {defaultSxProp} from '../utils/defaultSxProp'

export type FormControlProps = {
children?: React.ReactNode
Expand All @@ -51,7 +47,6 @@ export type FormControlProps = {

const FormControl = React.forwardRef<HTMLDivElement, FormControlProps>(
({children, disabled: disabledProp, layout = 'vertical', id: idProp, required, sx, className}, ref) => {
const enabled = useFeatureFlag(cssModulesFlag)
const [slots, childrenWithoutSlots] = useSlots(children, {
caption: FormControlCaption,
label: FormControlLabel,
Expand Down Expand Up @@ -122,6 +117,46 @@ const FormControl = React.forwardRef<HTMLDivElement, FormControlProps>(
}

const isLabelHidden = slots.label?.props.visuallyHidden
const InputChildren = (
<>
<div className={classes.ControlChoiceInputs}>
{React.isValidElement(InputComponent)
? React.cloneElement(
InputComponent as React.ReactElement<{
id: string
disabled: boolean
required: boolean
['aria-describedby']: string
}>,
{
id,
disabled,
// allow checkboxes to be required
required: required && !isRadioInput,
['aria-describedby']: captionId as string,
},
)
: null}
{childrenWithoutSlots.filter(
child =>
React.isValidElement(child) && ![Checkbox, Radio].some(inputComponent => child.type === inputComponent),
)}
</div>
{slots.leadingVisual ? (
<div
className={classes.LeadingVisual}
data-disabled={disabled ? '' : undefined}
data-has-caption={slots.caption ? '' : undefined}
>
{slots.leadingVisual}
</div>
) : null}
<div className={classes.LabelContainer}>
{slots.label}
{slots.caption}
</div>
</>
)

return (
<FormControlContextProvider
Expand All @@ -134,69 +169,33 @@ const FormControl = React.forwardRef<HTMLDivElement, FormControlProps>(
}}
>
{isChoiceInput || layout === 'horizontal' ? (
<StyledHorizontalLayout
ref={ref}
data-has-leading-visual={slots.leadingVisual ? '' : undefined}
sx={sx}
className={clsx(className, {
[classes.ControlHorizontalLayout]: enabled,
})}
>
<StyledChoiceInputs className={classes.ControlChoiceInputs}>
{React.isValidElement(InputComponent)
? React.cloneElement(
InputComponent as React.ReactElement<{
id: string
disabled: boolean
required: boolean
['aria-describedby']: string
}>,
{
id,
disabled,
// allow checkboxes to be required
required: required && !isRadioInput,
['aria-describedby']: captionId as string,
},
)
: null}
{childrenWithoutSlots.filter(
child =>
React.isValidElement(child) &&
![Checkbox, Radio].some(inputComponent => child.type === inputComponent),
)}
</StyledChoiceInputs>
{slots.leadingVisual ? (
<StyledLeadingVisual
className={clsx({
[classes.LeadingVisual]: enabled,
})}
data-disabled={disabled ? '' : undefined}
data-has-caption={slots.caption ? '' : undefined}
>
{slots.leadingVisual}
</StyledLeadingVisual>
) : null}
<StyledLabelContainer className={classes.LabelContainer}>
{slots.label}
{slots.caption}
</StyledLabelContainer>
</StyledHorizontalLayout>
sx !== defaultSxProp ? (
<Box
ref={ref}
data-has-leading-visual={slots.leadingVisual ? '' : undefined}
sx={sx}
className={clsx(className, classes.ControlHorizontalLayout)}
>
{InputChildren}
</Box>
) : (
<div
ref={ref}
data-has-leading-visual={slots.leadingVisual ? '' : undefined}
className={clsx(className, classes.ControlHorizontalLayout)}
>
{InputChildren}
</div>
)
) : (
<Box
ref={ref}
data-has-label={!isLabelHidden ? '' : undefined}
display="flex"
flexDirection="column"
alignItems="flex-start"
sx={
enabled
? sx
: {...(isLabelHidden ? {'> *:not(label) + *': {marginTop: 1}} : {'> * + *': {marginTop: 1}}), ...sx}
}
className={clsx(className, {
[classes.ControlVerticalLayout]: enabled,
})}
sx={sx}
className={clsx(className, classes.ControlVerticalLayout)}
>
{slots.label}
{React.isValidElement(InputComponent) &&
Expand Down Expand Up @@ -229,69 +228,6 @@ const FormControl = React.forwardRef<HTMLDivElement, FormControlProps>(
},
)

const StyledHorizontalLayout = toggleStyledComponent(
cssModulesFlag,
'div',
styled.div`
display: flex;

&:where([data-has-leading-visual]) {
align-items: center;
}

${sx}
`,
)

const StyledChoiceInputs = toggleStyledComponent(
cssModulesFlag,
'div',
styled.div`
> input {
margin-left: 0;
margin-right: 0;
}
`,
)

const StyledLabelContainer = toggleStyledComponent(
cssModulesFlag,
'div',
styled.div`
> * {
padding-left: var(--stack-gap-condensed);
}

> label {
font-weight: var(--base-text-weight-normal);
}
`,
)

const StyledLeadingVisual = toggleStyledComponent(
cssModulesFlag,
'div',
styled.div`
color: var(--fgColor-default);
margin-left: var(--base-size-8);

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

> * {
fill: currentColor;
min-width: var(--text-body-size-large);
min-height: var(--text-body-size-large);
}

> *:where([data-has-caption]) {
min-width: var(--base-size-24);
min-height: var(--base-size-24);
}
`,
)

export default Object.assign(FormControl, {
Caption: FormControlCaption,
Label: FormControlLabel,
Expand Down
34 changes: 6 additions & 28 deletions packages/react/src/FormControl/FormControlCaption.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import {clsx} from 'clsx'
import React from 'react'
import styled from 'styled-components'
import {cssModulesFlag} from './feature-flags'
import {useFeatureFlag} from '../FeatureFlags'
import Text from '../Text'
import sx from '../sx'
import type {SxProp} from '../sx'
import classes from './FormControlCaption.module.css'
import {useFormControlContext} from './_FormControlContext'
import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent'
import {toggleSxComponent} from '../internal/utils/toggleSxComponent'

type FormControlCaptionProps = React.PropsWithChildren<
{
Expand All @@ -18,36 +14,18 @@ type FormControlCaptionProps = React.PropsWithChildren<
>

function FormControlCaption({id, children, sx, className}: FormControlCaptionProps) {
const enabled = useFeatureFlag(cssModulesFlag)
const {captionId, disabled} = useFormControlContext()
const Caption = toggleSxComponent(sx, Text) as React.ComponentType<FormControlCaptionProps>

return (
<StyledCaption
<Caption
id={id ?? captionId}
className={clsx(className, {
[classes.Caption]: enabled,
})}
className={clsx(className, classes.Caption)}
data-control-disabled={disabled ? '' : undefined}
sx={sx}
>
{children}
</StyledCaption>
</Caption>
)
}

const StyledCaption = toggleStyledComponent(
cssModulesFlag,
Text,
styled(Text)`
color: var(--fgColor-muted);
display: block;
font-size: var(--text-body-size-small);

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

${sx}
`,
)

export {FormControlCaption}
1 change: 0 additions & 1 deletion packages/react/src/FormControl/feature-flags.ts

This file was deleted.

69 changes: 8 additions & 61 deletions packages/react/src/internal/components/InputLabel.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import {clsx} from 'clsx'
import React from 'react'
import styled from 'styled-components'
import sx, {type SxProp} from '../../sx'
import {cssModulesFlag} from '../../FormControl/feature-flags'
import {useFeatureFlag} from '../../FeatureFlags'
import {type SxProp} from '../../sx'
import classes from './InputLabel.module.css'
import {toggleStyledComponent} from '../utils/toggleStyledComponent'
import {toggleSxComponent} from '../utils/toggleSxComponent'

type BaseProps = SxProp & {
disabled?: boolean
Expand Down Expand Up @@ -43,76 +40,26 @@ function InputLabel({
className,
...props
}: Props) {
const enabled = useFeatureFlag(cssModulesFlag)
const Label = toggleSxComponent({sx}, as) as React.ComponentType<Props>
return (
<StyledLabel
as={as}
<Label
data-control-disabled={disabled ? '' : undefined}
data-visually-hidden={visuallyHidden ? '' : undefined}
htmlFor={htmlFor}
id={id}
className={clsx(className, {
[classes.Label]: enabled,
})}
sx={sx}
className={clsx(className, classes.Label)}
{...props}
>
{required || requiredText ? (
<StyledRequiredText
className={clsx({
[classes.RequiredText]: enabled,
})}
>
<span className={classes.RequiredText}>
<span>{children}</span>
<span aria-hidden={requiredIndicator ? undefined : true}>{requiredText ?? '*'}</span>
</StyledRequiredText>
</span>
) : (
children
)}
</StyledLabel>
</Label>
)
}

const StyledLabel = toggleStyledComponent(
cssModulesFlag,
'label',
styled.label`
align-self: flex-start;
display: block;
color: var(--fgColor-default);
cursor: pointer;
font-weight: 600;
font-size: var(--text-body-size-medium);

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

&:where([data-visually-hidden]) {
border: 0;
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
white-space: nowrap;
width: 1px;
}

${sx}
`,
)

const StyledRequiredText = toggleStyledComponent(
cssModulesFlag,
'span',
styled.span`
display: flex;
column-gap: var(--base-size-4);
`,
)

export {InputLabel}
Loading
Loading