diff --git a/.changeset/sharp-items-impress.md b/.changeset/sharp-items-impress.md new file mode 100644 index 000000000..75f706d79 --- /dev/null +++ b/.changeset/sharp-items-impress.md @@ -0,0 +1,5 @@ +--- +'@utilitywarehouse/web-ui': patch +--- + +Fix disabled Radio diff --git a/packages/web-ui/src/BaseRadioGroup/BaseRadioGroup.context.ts b/packages/web-ui/src/BaseRadioGroup/BaseRadioGroup.context.ts new file mode 100644 index 000000000..1603b042b --- /dev/null +++ b/packages/web-ui/src/BaseRadioGroup/BaseRadioGroup.context.ts @@ -0,0 +1,14 @@ +import { createContext, useContext } from 'react'; + +export type BaseRadioGroupContextValue = { + hasGroupHelperText: boolean; + 'aria-describedby'?: string; +}; + +export const BaseRadioGroupContext = createContext({ + hasGroupHelperText: false, +} as BaseRadioGroupContextValue); + +export const BaseRadioGroupProvider = BaseRadioGroupContext.Provider; + +export const useBaseRadioGroup = () => useContext(BaseRadioGroupContext); diff --git a/packages/web-ui/src/BaseRadioGroup/BaseRadioGroup.props.ts b/packages/web-ui/src/BaseRadioGroup/BaseRadioGroup.props.ts new file mode 100644 index 000000000..7b1a4da4a --- /dev/null +++ b/packages/web-ui/src/BaseRadioGroup/BaseRadioGroup.props.ts @@ -0,0 +1,38 @@ +import { type ReactNode } from 'react'; +import { type RadioGroupProps as RadixRadioGroupProps } from '@radix-ui/react-radio-group'; + +export interface BaseRadioGroupProps extends Omit { + children: ReactNode; + /** + * The label for the radio group. This should contain the question being + * answered by the radio group. + * + * If you don't include a label you need to ensure you use the `aria-label` + * or `aria-labelledby` prop to properly associate a label with the radio + * group. + */ + label?: ReactNode; + /** + * Helper text for the radio group. Provides a hint such as specific + * requirements for what to choose. When displayed, child `Radio` or + * `RadioTile` components will not display `helperText`. + */ + helperText?: ReactNode; + /** + * Position of the helper text. + * @default 'top' + */ + helperTextPosition?: 'top' | 'bottom'; + /** + * Set whether to display the helper text icon. + */ + showHelperTextIcon?: boolean; + /** Controls whether the error message is displayed. */ + error?: boolean; + /** The error message to be displayed. */ + errorMessage?: ReactNode; + /** + * Set whether to display the error message icon. + */ + showErrorMessageIcon?: boolean; +} diff --git a/packages/web-ui/src/RadioGroup/RadioGroupFormField.tsx b/packages/web-ui/src/BaseRadioGroup/BaseRadioGroup.tsx similarity index 85% rename from packages/web-ui/src/RadioGroup/RadioGroupFormField.tsx rename to packages/web-ui/src/BaseRadioGroup/BaseRadioGroup.tsx index 92373f7d9..0ad5a41ec 100644 --- a/packages/web-ui/src/RadioGroup/RadioGroupFormField.tsx +++ b/packages/web-ui/src/BaseRadioGroup/BaseRadioGroup.tsx @@ -8,12 +8,12 @@ import { HelperText } from '../HelperText'; import { useIds } from '../hooks'; import { PropsWithSx } from '../types'; import { mergeIds } from '../utils'; -import { BaseRadioGroupProps } from './RadioGroup.props'; -import { RadioGroupContext } from './RadioGroup.context'; +import { BaseRadioGroupProps } from './BaseRadioGroup.props'; +import { BaseRadioGroupProvider } from './BaseRadioGroup.context'; -const componentName = 'RadioGroupFormField'; +const componentName = 'BaseRadioGroup'; -export const RadioGroupFormField = forwardRef>( +export const BaseRadioGroup = forwardRef>( ( { id: providedId, @@ -72,7 +72,7 @@ export const RadioGroupFormField = forwardRef ) : null} - {children} + {children} {showErrorMessage ? ( @@ -90,4 +90,4 @@ export const RadioGroupFormField = forwardRef = { export default meta; type Story = StoryObj; -export const Workshop: Story = { - render: args => , -}; +export const Workshop: Story = {}; diff --git a/packages/web-ui/src/HelperText/HelperText.tsx b/packages/web-ui/src/HelperText/HelperText.tsx index 6bf558e4f..8de1bfab3 100644 --- a/packages/web-ui/src/HelperText/HelperText.tsx +++ b/packages/web-ui/src/HelperText/HelperText.tsx @@ -43,7 +43,7 @@ const StyledElement = styled('span')({ // Button's color property if not set. color: 'var(--helper-text-color)', }, - ':where([data-disabled])': { + ':where([data-disabled],[data-disabled] &)': { '--helper-text-color': 'var(--helper-text-color-disabled)', }, [classSelectors.valid]: { diff --git a/packages/web-ui/src/Radio/Radio.props.ts b/packages/web-ui/src/Radio/Radio.props.ts index 90b582a6a..a1e83c963 100644 --- a/packages/web-ui/src/Radio/Radio.props.ts +++ b/packages/web-ui/src/Radio/Radio.props.ts @@ -1,5 +1,5 @@ -import { type RadioGroupItemProps } from '@radix-ui/react-radio-group'; -import { type ReactNode } from 'react'; +import type { RadioGroupItemProps } from '@radix-ui/react-radio-group'; +import type { ReactNode } from 'react'; export interface RadioProps extends Omit { /** diff --git a/packages/web-ui/src/Radio/Radio.stories.tsx b/packages/web-ui/src/Radio/Radio.stories.tsx index ad13bd94f..a9e152d65 100644 --- a/packages/web-ui/src/Radio/Radio.stories.tsx +++ b/packages/web-ui/src/Radio/Radio.stories.tsx @@ -6,7 +6,7 @@ import { Stack } from '../Stack'; import { Text, TextProps } from '../Text'; const meta: Meta = { - title: 'Web UI / Components / RadioGroup', + title: 'Web UI / Components / Radio / Radio', component: Radio, }; diff --git a/packages/web-ui/src/Radio/Radio.tsx b/packages/web-ui/src/Radio/Radio.tsx index ebb9447f0..73e3236f2 100644 --- a/packages/web-ui/src/Radio/Radio.tsx +++ b/packages/web-ui/src/Radio/Radio.tsx @@ -11,11 +11,11 @@ import { HelperText } from '../HelperText'; import { useIds } from '../hooks'; import { PropsWithSx } from '../types'; import { RadioProps } from './Radio.props'; -import { RadioGroupContext } from '../RadioGroup/RadioGroup.context'; import { styled } from '../theme'; import { spacing, withGlobalPrefix } from '../utils'; import clsx from 'clsx'; import { Flex } from '../Flex'; +import { useBaseRadioGroup } from '../BaseRadioGroup'; const componentName = 'Radio'; const componentClassName = withGlobalPrefix(componentName); @@ -111,13 +111,16 @@ export const Radio = React.forwardRef ref ) => { const { id, labelId, helperTextId } = useIds({ providedId, componentPrefix: 'radio' }); - const { hasGroupHelperText, 'aria-describedby': ariaDescribedby } = - React.useContext(RadioGroupContext); + const { hasGroupHelperText, 'aria-describedby': ariaDescribedby } = useBaseRadioGroup(); const showHelperText = !hasGroupHelperText && !!helperText; const showLabel = !!label; return ( - + = { - title: 'Web UI / Components / RadioGridGroup', + title: 'Web UI / Components / Radio / RadioGridGroup', component: RadioGridGroup, tags: ['autodocs'], argTypes: { diff --git a/packages/web-ui/src/RadioGridGroup/RadioGridGroup.tsx b/packages/web-ui/src/RadioGridGroup/RadioGridGroup.tsx index 56d0933c0..5c920d553 100644 --- a/packages/web-ui/src/RadioGridGroup/RadioGridGroup.tsx +++ b/packages/web-ui/src/RadioGridGroup/RadioGridGroup.tsx @@ -1,33 +1,14 @@ import * as React from 'react'; import { Box } from '../Box'; -import { RadioGroupFormField } from '../RadioGroup/RadioGroupFormField'; -import { breakpoints } from '../tokens'; import { PropsWithSx } from '../types'; import { RadioGridGroupProps } from './RadioGridGroup.props'; import clsx from 'clsx'; -import { withGlobalPrefix } from '../utils'; +import { getColumns, withGlobalPrefix } from '../utils'; +import { BaseRadioGroup } from '../BaseRadioGroup'; const componentName = 'RadioGridGroup'; const componentClassName = withGlobalPrefix(componentName); -function convert(c: string) { - return `repeat(${c}, minmax(10px, 1fr))`; -} -function getColumns(columns: RadioGridGroupProps['columns']) { - if (Array.isArray(columns)) { - return columns.map(s => convert(s as string)); - } - if (typeof columns === 'object') { - return Object.keys(breakpoints).reduce((acc: { [key: string]: string }, breakpoint: string) => { - if (columns[breakpoint] !== null) { - acc[breakpoint] = convert(columns[breakpoint] as string); - } - return acc; - }, {}); - } - return convert(columns as string); -} - /** * The `RadioGridGroup` provides an accessible way to group and control a set * of `Radio` or `RadioTile` components, displayed in a grid layout, allowing @@ -46,7 +27,7 @@ function getColumns(columns: RadioGridGroupProps['columns']) { export const RadioGridGroup = React.forwardRef>( ({ children, contentWidth = 'fit-content', columns = 2, className, ...props }, ref) => { return ( - + {children} - + ); } ); diff --git a/packages/web-ui/src/RadioGroup/RadioGroup.context.ts b/packages/web-ui/src/RadioGroup/RadioGroup.context.ts deleted file mode 100644 index 6a40237ac..000000000 --- a/packages/web-ui/src/RadioGroup/RadioGroup.context.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { createContext } from 'react'; - -export type RadioGroupContextValue = { hasGroupHelperText: boolean; 'aria-describedby'?: string }; -export const RadioGroupContext = createContext({ - hasGroupHelperText: false, -} as RadioGroupContextValue); diff --git a/packages/web-ui/src/RadioGroup/RadioGroup.docs.mdx b/packages/web-ui/src/RadioGroup/RadioGroup.docs.mdx index 378d21d69..94254d1c7 100644 --- a/packages/web-ui/src/RadioGroup/RadioGroup.docs.mdx +++ b/packages/web-ui/src/RadioGroup/RadioGroup.docs.mdx @@ -20,7 +20,7 @@ import { Divider } from '@mui/material'; - [Keyboard interactions](#keyboard-interactions) - [Helper text](#helper-text) - [Error message](#error-message) -- [Width](#width) +- [Content Width](#content-width) - [Controlled](#controlled) - [RadioGroup props](#radiogroup-props) - [Radio & RadioTile](#radio-&-radiotile) @@ -95,7 +95,7 @@ either above or below the group's radio items. While the radio items can also display a helper text, these will not be displayed if the radio group has a helper text. - + ## Error message @@ -105,7 +105,7 @@ submitted. -## Width +## Content width The width of the `RadioGroup` should be set by the parent layout, however it is possible to independently set the width of the children using `contentWidth` @@ -119,7 +119,7 @@ the `RadioGroup`. This is a responsive property, so you are able to set different widths for different breakpoints. - + ## Controlled diff --git a/packages/web-ui/src/RadioGroup/RadioGroup.props.ts b/packages/web-ui/src/RadioGroup/RadioGroup.props.ts index 8063b1b0e..c9c0b14b0 100644 --- a/packages/web-ui/src/RadioGroup/RadioGroup.props.ts +++ b/packages/web-ui/src/RadioGroup/RadioGroup.props.ts @@ -1,42 +1,5 @@ +import { BaseRadioGroupProps } from '../BaseRadioGroup'; import { BoxProps } from '../Box'; -import { type ReactNode } from 'react'; -import { type RadioGroupProps as RadixRadioGroupProps } from '@radix-ui/react-radio-group'; - -export interface BaseRadioGroupProps extends Omit { - children: ReactNode; - /** - * The label for the radio group. This should contain the question being - * answered by the radio group. - * - * If you don't include a label you need to ensure you use the `aria-label` - * or `aria-labelledby` prop to properly associate a label with the radio - * group. - */ - label?: ReactNode; - /** - * Helper text for the radio group. Provides a hint such as specific - * requirements for what to choose. When displayed, child `Radio` or - * `RadioTile` components will not display `helperText`. - */ - helperText?: ReactNode; - /** - * Position of the helper text. - * @default 'top' - */ - helperTextPosition?: 'top' | 'bottom'; - /** - * Set whether to display the helper text icon. - */ - showHelperTextIcon?: boolean; - /** Controls whether the error message is displayed. */ - error?: boolean; - /** The error message to be displayed. */ - errorMessage?: ReactNode; - /** - * Set whether to display the error message icon. - */ - showErrorMessageIcon?: boolean; -} export interface RadioGroupProps extends Omit { /** The direction of the radios, will also set the aria-orientation value. */ diff --git a/packages/web-ui/src/RadioGroup/RadioGroup.stories.tsx b/packages/web-ui/src/RadioGroup/RadioGroup.stories.tsx index aabd5bb64..c05394ac3 100644 --- a/packages/web-ui/src/RadioGroup/RadioGroup.stories.tsx +++ b/packages/web-ui/src/RadioGroup/RadioGroup.stories.tsx @@ -8,7 +8,7 @@ import { Box } from '../Box'; import { colors } from '@utilitywarehouse/colour-system'; const meta: Meta = { - title: 'Web UI / Components / RadioGroup', + title: 'Web UI / Components / Radio / RadioGroup', component: RadioGroup, argTypes: { direction: { @@ -62,8 +62,8 @@ export const Workshop: Story = { }, }; -export const WithRadioHelperText: Story = { - name: 'With Radio HelperText', +export const RadioHelperText: Story = { + name: 'Radio HelperText', render: args => { return ( @@ -78,8 +78,8 @@ export const WithRadioHelperText: Story = { }, }; -export const Width: Story = { - name: 'Width', +export const ContentWidth: Story = { + name: 'Content Width', render: args => { return ( @@ -89,7 +89,7 @@ export const Width: Story = { ); }, - args: { contentWidth: '100%' }, + args: { contentWidth: '200px' }, }; export const Controlled: Story = { diff --git a/packages/web-ui/src/RadioGroup/RadioGroup.tsx b/packages/web-ui/src/RadioGroup/RadioGroup.tsx index a46c74bc7..b3b69701e 100644 --- a/packages/web-ui/src/RadioGroup/RadioGroup.tsx +++ b/packages/web-ui/src/RadioGroup/RadioGroup.tsx @@ -2,10 +2,10 @@ import * as React from 'react'; import { PropsWithSx } from '../types'; import { withGlobalPrefix } from '../utils'; import { RadioGroupProps } from './RadioGroup.props'; -import { RadioGroupFormField } from './RadioGroupFormField'; import clsx from 'clsx'; import { Flex } from '../Flex'; import { styled } from '../theme'; +import { BaseRadioGroup } from '../BaseRadioGroup'; const componentName = 'RadioGroup'; const componentClassName = withGlobalPrefix(componentName); @@ -36,7 +36,7 @@ const StyledElement = styled(Flex)({ export const RadioGroup = React.forwardRef>( ({ children, contentWidth = 'fit-content', direction = 'column', className, ...props }, ref) => { return ( - {children} - + ); } ); diff --git a/packages/web-ui/src/RadioTile/RadioTile.stories.tsx b/packages/web-ui/src/RadioTile/RadioTile.stories.tsx index b1335969b..24f31580b 100644 --- a/packages/web-ui/src/RadioTile/RadioTile.stories.tsx +++ b/packages/web-ui/src/RadioTile/RadioTile.stories.tsx @@ -5,7 +5,7 @@ import { RadioGroup } from '../RadioGroup'; import { Stack } from '../Stack'; const meta: Meta = { - title: 'Web UI / Components / RadioGroup', + title: 'Web UI / Components / Radio / RadioTile', component: RadioTile, }; diff --git a/packages/web-ui/src/RadioTile/RadioTile.tsx b/packages/web-ui/src/RadioTile/RadioTile.tsx index ec9fc4a0c..89160615c 100644 --- a/packages/web-ui/src/RadioTile/RadioTile.tsx +++ b/packages/web-ui/src/RadioTile/RadioTile.tsx @@ -4,14 +4,14 @@ import { Label } from '../Label'; import { HelperText } from '../HelperText'; import { colors, colorsCommon } from '@utilitywarehouse/colour-system'; import { useIds } from '../hooks'; -import { withGlobalPrefix, spacing } from '../utils'; +import { withGlobalPrefix, spacing, px } from '../utils'; import { PropsWithSx } from '../types'; import { StyledRadioIndicator } from '../Radio/Radio'; -import { RadioGroupContext } from '../RadioGroup/RadioGroup.context'; import clsx from 'clsx'; import { RadioTileProps } from './RadioTile.props'; import { styled } from '../theme'; import { Flex } from '../Flex'; +import { useBaseRadioGroup } from '../BaseRadioGroup'; const componentName = 'Radio'; const componentClassName = withGlobalPrefix(componentName); @@ -42,7 +42,7 @@ const StyledRadio = styled('div')({ const StyledRadioItem = styled(Item)({ all: 'unset', - borderRadius: '8px', + borderRadius: px(8), padding: spacing(2), display: 'flex', boxShadow: 'inset 0 0 0 2px var(--radio-item-box-shadow-color)', @@ -92,8 +92,7 @@ export const RadioTile = React.forwardRef { const { id, labelId, helperTextId } = useIds({ providedId, componentPrefix: 'radiotile' }); - const { hasGroupHelperText, 'aria-describedby': ariaDescribedby } = - React.useContext(RadioGroupContext); + const { hasGroupHelperText, 'aria-describedby': ariaDescribedby } = useBaseRadioGroup(); const showHelperText = !hasGroupHelperText && !!helperText; const showLabel = !!label; diff --git a/packages/web-ui/src/lab/Link/Link.stories.tsx b/packages/web-ui/src/lab/Link/Link.stories.tsx index d5fb2bb91..c6e64f0fe 100644 --- a/packages/web-ui/src/lab/Link/Link.stories.tsx +++ b/packages/web-ui/src/lab/Link/Link.stories.tsx @@ -12,7 +12,7 @@ import { Link } from './Link'; import { Flex } from '../../Flex'; const meta: Meta = { - title: 'Web UI / Lab / Link', + title: 'Web UI / Lab / Link / Link', component: Link, args: { href: '#' }, argTypes: { diff --git a/packages/web-ui/src/lab/TextLink/TextLink.stories.tsx b/packages/web-ui/src/lab/TextLink/TextLink.stories.tsx index 7878dc714..a84067e79 100644 --- a/packages/web-ui/src/lab/TextLink/TextLink.stories.tsx +++ b/packages/web-ui/src/lab/TextLink/TextLink.stories.tsx @@ -7,7 +7,7 @@ import { Box } from '../../Box'; import { colors, colorsCommon } from '@utilitywarehouse/colour-system'; const meta: Meta = { - title: 'Web UI / Lab / TextLink', + title: 'Web UI / Lab / Link / TextLink', component: TextLink, argTypes: { children: { control: { type: 'text' } }, diff --git a/packages/web-ui/src/utils/columns.ts b/packages/web-ui/src/utils/columns.ts new file mode 100644 index 000000000..74a451b73 --- /dev/null +++ b/packages/web-ui/src/utils/columns.ts @@ -0,0 +1,21 @@ +import { StackProps } from '../Stack'; +import { breakpoints } from '../tokens'; + +function convert(c: string) { + return `repeat(${c}, minmax(10px, 1fr))`; +} + +export function getColumns(columns: StackProps['spacing']) { + if (Array.isArray(columns)) { + return columns.map(s => convert(s as string)); + } + if (typeof columns === 'object') { + return Object.keys(breakpoints).reduce((acc: { [key: string]: string }, breakpoint: string) => { + if (columns[breakpoint] !== null) { + acc[breakpoint] = convert(columns[breakpoint] as string); + } + return acc; + }, {}); + } + return convert(columns as string); +} diff --git a/packages/web-ui/src/utils/index.ts b/packages/web-ui/src/utils/index.ts index 9cf9753b1..8dd3a4455 100644 --- a/packages/web-ui/src/utils/index.ts +++ b/packages/web-ui/src/utils/index.ts @@ -1,4 +1,5 @@ export * from './color'; +export * from './columns'; export * from './css-selector-helpers'; export * from './data-attributes'; export * from './media-queries';