Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add a helper text to radio buttons options #2191

Merged
merged 2 commits into from
Jul 25, 2023
Merged
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
5 changes: 5 additions & 0 deletions .changeset/lemon-eyes-roll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sumup/circuit-ui': minor
---

Added a new `description` prop to the RadioInput component. Use it to provide additional context for an option.
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ describe('RadioButton', () => {
const inputEl = screen.getByRole('radio');
expect(inputEl).toHaveAccessibleName(defaultProps.label);
});

it('should have a description', () => {
render(<RadioButton {...defaultProps} description="Some explanation" />);
const helperEl = screen.getAllByText('Some explanation');
expect(helperEl.length).toBeGreaterThan(0);
const labelEl = screen.getByText(defaultProps.label);
expect(labelEl).toHaveAccessibleDescription('Some explanation');
});
});

describe('State & Interactions', () => {
Expand Down
26 changes: 21 additions & 5 deletions packages/circuit-ui/components/RadioButton/RadioButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
*/

import {
Fragment,
InputHTMLAttributes,
Ref,
createContext,
Expand All @@ -29,13 +28,18 @@ import { uniqueId } from '../../util/id';
import { useClickEvent, TrackingProps } from '../../hooks/useClickEvent';
import { AccessibilityError } from '../../util/errors';
import { deprecate } from '../../util/logger';
import { FieldDescription, FieldWrapper } from '../FieldAtoms';

export interface RadioButtonProps
extends InputHTMLAttributes<HTMLInputElement> {
/**
* A clear and concise description of the option's purpose.
*/
label: string;
/**
* Further details about the option's purpose.
*/
description?: string;
/**
* Triggers error styles on the component.
*/
Expand Down Expand Up @@ -72,7 +76,7 @@ const labelBaseStyles = ({ theme }: StyleProps) => css`
content: '';
display: block;
position: absolute;
top: 50%;
top: calc(${theme.typography.body.one.lineHeight} / 2);
left: 0;
transform: translateY(-50%);
transition: border ${theme.transitions.default};
Expand All @@ -87,7 +91,7 @@ const labelBaseStyles = ({ theme }: StyleProps) => css`
content: '';
display: block;
position: absolute;
top: 50%;
top: calc(${theme.typography.body.one.lineHeight} / 2);
left: ${theme.spacings.bit};
transform: translateY(-50%) scale(0, 0);
opacity: 0;
Expand Down Expand Up @@ -203,6 +207,7 @@ export const RadioButton = forwardRef(
{
onChange,
label,
description,
id: customId,
name,
value,
Expand Down Expand Up @@ -239,7 +244,7 @@ export const RadioButton = forwardRef(
const handleChange = useClickEvent(onChange, tracking, 'radio-button');

return (
<Fragment>
<FieldWrapper disabled={disabled}>
<RadioButtonInput
{...props}
type="radio"
Expand All @@ -258,10 +263,21 @@ export const RadioButton = forwardRef(
invalid={invalid}
className={className}
style={style}
aria-describedby={description ? `${id}-description` : undefined}
>
{label}
{description && (
<FieldDescription aria-hidden="true">
{description}
</FieldDescription>
)}
</RadioButtonLabel>
</Fragment>
{description && (
<p id={`${id}-description`} css={hideVisually}>
{description}
</p>
)}
</FieldWrapper>
);
},
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ Base.args = {
label: 'Choose your favourite fruit',
defaultValue: 'banana',
options: [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Apple', value: 'apple', description: 'Keeps the doctor away' },
{ label: 'Banana', value: 'banana', description: 'Rich in Mg' },
{ label: 'Mango', value: 'mango' },
],
// Storybook displays the default mocked function props poorly,
Expand Down Expand Up @@ -89,8 +89,8 @@ Validations.args = {
label: 'Choose your favourite fruit',
optionalLabel: 'Optional',
options: [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Apple', value: 'apple', description: 'Keeps the doctor away' },
{ label: 'Banana', value: 'banana', description: 'Rich in Mg' },
{ label: 'Mango', value: 'mango' },
],
};
Expand All @@ -102,8 +102,12 @@ export const Disabled = (args: RadioButtonGroupProps) => (
name="fully-disabled"
disabled
options={[
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{
label: 'Apple',
value: 'apple',
description: 'Keeps the doctor away',
},
{ label: 'Banana', value: 'banana', description: 'Rich in Mg' },
{ label: 'Mango', value: 'mango' },
]}
validationHint="All fruits are sold out"
Expand All @@ -113,9 +117,18 @@ export const Disabled = (args: RadioButtonGroupProps) => (
{...args}
name="partially-disabled"
options={[
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Mango', value: 'mango', disabled: true },
{
label: 'Apple',
value: 'apple',
description: 'Keeps the doctor away',
},
{
label: 'Banana',
value: 'banana',
description: 'Rich in Mg',
disabled: true,
},
{ label: 'Mango', value: 'mango' },
]}
validationHint="Some fruits are sold out"
style={storyStyles}
Expand Down