Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions .changeset/quick-bats-remain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@primer/styled-react': patch
'@primer/react': patch
---

Update RadioGroup component to no longer support sx, add sx wrapper to @primer/styled-react.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.RadioGroupWithTopMargin {
margin-top: var(--base-size-24);
}
3 changes: 2 additions & 1 deletion packages/react/src/Banner/Banner.examples.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import RadioGroup from '../RadioGroup'
import Radio from '../Radio'
import {Button} from '../Button'
import React from 'react'
import classes from './Banner.examples.stories.module.css'
import {useFocus} from '../internal/hooks/useFocus'
import {PageLayout} from '../PageLayout'

Expand Down Expand Up @@ -65,11 +66,11 @@ export const WithAnnouncement = () => {
secondaryAction={<Banner.SecondaryAction>Button</Banner.SecondaryAction>}
/>
<RadioGroup
sx={{marginTop: 4}}
name="options"
onChange={selected => {
setSelected(selected as Choice)
}}
className={classes.RadioGroupWithTopMargin}
>
<RadioGroup.Label>Choices</RadioGroup.Label>
<FormControl>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.CheckboxGroup {
color: var(--fgColor-default, var(--color-fg-default));
/* stylelint-disable-next-line primer/spacing */
margin-top: 15px;
/* stylelint-disable-next-line primer/spacing */
margin-bottom: 15px;
}

.MutedCaption {
color: var(--fgColor-muted);
font-size: var(--text-body-size-small);
font-weight: var(--text-caption-weight);
}

.BoldLabel {
color: var(--fgColor-default, var(--color-fg-default));
font-size: var(--text-body-size-medium);
font-weight: var(--base-text-weight-semibold);
}
28 changes: 4 additions & 24 deletions packages/react/src/CheckboxGroup/CheckboxGroup.dev.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type {Meta} from '@storybook/react-vite'
import {Checkbox, CheckboxGroup, FormControl} from '..'
import classes from './CheckboxGroup.dev.stories.module.css'

export default {
title: 'Components/CheckboxGroup/Dev',
Expand All @@ -8,30 +9,9 @@ export default {
} as Meta

export const SxProps = () => (
<CheckboxGroup
sx={{
color: 'var(--fgColor-default, var(--color-fg-default))',
my: '15px',
}}
>
<CheckboxGroup.Caption
sx={{
color: 'fg.muted',
fontSize: 0,
fontWeight: 400,
}}
>
Caption
</CheckboxGroup.Caption>
<CheckboxGroup.Label
sx={{
color: 'var(--fgColor-default, var(--color-fg-default))',
fontSize: ['14px'],
fontWeight: 600,
}}
>
Choices
</CheckboxGroup.Label>
<CheckboxGroup className={classes.CheckboxGroup}>
<CheckboxGroup.Caption className={classes.MutedCaption}>Caption</CheckboxGroup.Caption>
<CheckboxGroup.Label className={classes.BoldLabel}>Choices</CheckboxGroup.Label>
<FormControl required>
<Checkbox value="one" defaultChecked />
<FormControl.Label>Choice one</FormControl.Label>
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/RadioGroup/RadioGroup.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {CheckboxOrRadioGroupArgs} from '../utils/form-story-helpers'
export default {
title: 'Components/RadioGroup',
component: RadioGroup,
parameters: {controls: {exclude: ['aria-labelledby', 'id', 'onChange', 'sx']}},
parameters: {controls: {exclude: ['aria-labelledby', 'id', 'onChange']}},
} as Meta

export const Playground = ({
Expand Down
4 changes: 1 addition & 3 deletions packages/react/src/RadioGroup/RadioGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import CheckboxOrRadioGroupCaption from '../internal/components/CheckboxOrRadioG
import CheckboxOrRadioGroupLabel from '../internal/components/CheckboxOrRadioGroup/CheckboxOrRadioGroupLabel'
import CheckboxOrRadioGroupValidation from '../internal/components/CheckboxOrRadioGroup/CheckboxOrRadioGroupValidation'
import {useRenderForcingRef} from '../hooks'
import type {SxProp} from '../sx'

type RadioGroupProps = {
/**
Expand All @@ -18,8 +17,7 @@ type RadioGroupProps = {
* The name used to identify this group of radios
*/
name: string
} & CheckboxOrRadioGroupProps &
SxProp
} & CheckboxOrRadioGroupProps

export const RadioGroupContext = createContext<{
disabled?: boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ import CheckboxOrRadioGroupValidation from './CheckboxOrRadioGroupValidation'
import CheckboxOrRadioGroupContext from './CheckboxOrRadioGroupContext'
import VisuallyHidden from '../../../_VisuallyHidden'
import {useSlots} from '../../../hooks/useSlots'
import type {SxProp} from '../../../sx'
import classes from './CheckboxOrRadioGroup.module.css'
import {clsx} from 'clsx'
import {BoxWithFallback} from '../BoxWithFallback'

export type CheckboxOrRadioGroupProps = {
/** Class name for custom styling */
Expand All @@ -32,7 +30,7 @@ export type CheckboxOrRadioGroupProps = {
* If true, the user must make a selection before the owning form can be submitted
*/
required?: boolean
} & SxProp
}

const CheckboxOrRadioGroup: React.FC<React.PropsWithChildren<CheckboxOrRadioGroupProps>> = ({
'aria-labelledby': ariaLabelledby,
Expand All @@ -41,7 +39,6 @@ const CheckboxOrRadioGroup: React.FC<React.PropsWithChildren<CheckboxOrRadioGrou
id: idProp,
required = false,
className,
sx,
}) => {
const [slots, rest] = useSlots(children, {
caption: CheckboxOrRadioGroupCaption,
Expand Down Expand Up @@ -80,53 +77,48 @@ const CheckboxOrRadioGroup: React.FC<React.PropsWithChildren<CheckboxOrRadioGrou
}}
>
<div>
<BoxWithFallback
className={clsx(className, classes.GroupFieldset)}
data-validation={validationChild ? '' : undefined}
{...(labelChild
? {
as: 'fieldset',
disabled,
}
: {})}
sx={sx}
>
{labelChild ? (
/*
{labelChild ? (
<fieldset
className={clsx(className, classes.GroupFieldset)}
data-validation={validationChild ? '' : undefined}
disabled={disabled}
>
{/*
Placing the caption text and validation text in the <legend> provides a better user
experience for more screenreaders.

Reference: https://blog.tenon.io/accessible-validation-of-checkbox-and-radiobutton-groups/
*/
*/}
<legend className={classes.GroupLegend} data-legend-visible={isLegendVisible ? '' : undefined}>
{slots.label}
{slots.caption}
{React.isValidElement(slots.validation) && slots.validation.props.children && (
<VisuallyHidden>{slots.validation.props.children}</VisuallyHidden>
)}
</legend>
) : (
/*

<div className={classes.Body}>
{React.Children.toArray(rest).filter(child => React.isValidElement(child))}
</div>
</fieldset>
) : (
<div className={clsx(className, classes.GroupFieldset)} data-validation={validationChild ? '' : undefined}>
{/*
If CheckboxOrRadioGroup.Label wasn't passed as a child, we don't render a <legend>
but we still want to render a caption
*/
slots.caption
)}
*/}
{slots.caption}

<div
className={classes.Body}
{...(!labelChild
? {
['aria-labelledby']: ariaLabelledby,
['aria-describedby']: [validationMessageId, captionId].filter(Boolean).join(' '),
as: 'div',
role: 'group',
}
: {})}
>
{React.Children.toArray(rest).filter(child => React.isValidElement(child))}
<div
className={classes.Body}
aria-labelledby={ariaLabelledby}
aria-describedby={[validationMessageId, captionId].filter(Boolean).join(' ')}
role="group"
>
{React.Children.toArray(rest).filter(child => React.isValidElement(child))}
</div>
</div>
</BoxWithFallback>
)}
{validationChild && (
<ValidationAnimationContainer
// If we have CheckboxOrRadioGroup.Label as a child, we render a screenreader-accessible validation message in the <legend>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import React from 'react'
import Text from '../../../Text'
import type {SxProp} from '../../../sx'
import CheckboxOrRadioGroupContext from './CheckboxOrRadioGroupContext'
import classes from './CheckboxOrRadioGroup.module.css'
import {clsx} from 'clsx'

type CheckboxOrRadioGroupCaptionProps = React.PropsWithChildren<SxProp> & {className?: string}
type CheckboxOrRadioGroupCaptionProps = React.PropsWithChildren<{className?: string}>

const CheckboxOrRadioGroupCaption: React.FC<CheckboxOrRadioGroupCaptionProps> = ({className, children, sx}) => {
const CheckboxOrRadioGroupCaption: React.FC<CheckboxOrRadioGroupCaptionProps> = ({className, children}) => {
const {captionId} = React.useContext(CheckboxOrRadioGroupContext)
return (
<Text className={clsx(className, classes.CheckboxOrRadioGroupCaption)} id={captionId} sx={sx}>
<Text className={clsx(className, classes.CheckboxOrRadioGroupCaption)} id={captionId}>
{children}
</Text>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react'
import VisuallyHidden from '../../../_VisuallyHidden'
import type {SxProp} from '../../../sx'
import CheckboxOrRadioGroupContext from './CheckboxOrRadioGroupContext'
import classes from './CheckboxOrRadioGroup.module.css'
import {Stack} from '../../../Stack'
Expand All @@ -13,13 +12,12 @@ export type CheckboxOrRadioGroupLabelProps = {
* Whether to visually hide the fieldset legend
*/
visuallyHidden?: boolean
} & SxProp
}

const CheckboxOrRadioGroupLabel: React.FC<React.PropsWithChildren<CheckboxOrRadioGroupLabelProps>> = ({
children,
className,
visuallyHidden = false,
sx,
}) => {
const {required, disabled} = React.useContext(CheckboxOrRadioGroupContext)

Expand All @@ -29,7 +27,6 @@ const CheckboxOrRadioGroupLabel: React.FC<React.PropsWithChildren<CheckboxOrRadi
isVisible={!visuallyHidden}
title={required ? 'required field' : undefined}
data-label-disabled={disabled ? '' : undefined}
sx={sx}
>
{required ? (
<Stack direction="horizontal" gap="none">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import React from 'react'
import InputValidation from '../InputValidation'
import type {SxProp} from '../../../sx'
import type {FormValidationStatus} from '../../../utils/types/FormValidationStatus'
import CheckboxOrRadioGroupContext from './CheckboxOrRadioGroupContext'

export type CheckboxOrRadioGroupValidationProps = {
/** Changes the visual style to match the validation status */
variant: FormValidationStatus
} & SxProp
}

const CheckboxOrRadioGroupValidation: React.FC<React.PropsWithChildren<CheckboxOrRadioGroupValidationProps>> = ({
children,
variant,
sx,
}) => {
const {validationMessageId = ''} = React.useContext(CheckboxOrRadioGroupContext)
return (
<InputValidation validationStatus={variant} id={validationMessageId} sx={sx}>
<InputValidation validationStatus={variant} id={validationMessageId}>
{children}
</InputValidation>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

exports[`@primer/styled-react exports 1`] = `
[
"RadioGroup",
"ActionList",
"ActionMenu",
"Autocomplete",
Expand Down Expand Up @@ -31,7 +32,6 @@ exports[`@primer/styled-react exports 1`] = `
"PageLayout",
"Popover",
"ProgressBar",
"RadioGroup",
"RelativeTime",
"SegmentedControl",
"Select",
Expand Down
7 changes: 6 additions & 1 deletion packages/styled-react/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import {RadioGroup as PrimerRadioGroup} from '@primer/react'
import {createStyledComponent} from './utils/createStyledComponent'

const RadioGroup: ReturnType<typeof createStyledComponent> = /*#__PURE__*/ createStyledComponent(PrimerRadioGroup)
export {RadioGroup}

export {
ActionList,
ActionMenu,
Expand Down Expand Up @@ -28,7 +34,6 @@ export {
PageLayout,
Popover,
ProgressBar,
RadioGroup,
RelativeTime,
SegmentedControl,
Select,
Expand Down
Loading