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

React nodes in checkboxes and radio buttons #254

Merged
merged 1 commit into from
Jan 9, 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
25 changes: 25 additions & 0 deletions src/components/Checkbox/Checkbox.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,31 @@ describe('Checkbox', () => {
render({ description });
expect(screen.getByText(description)).toBeDefined();
});

it.each([false, undefined])(
'Does not have presentation role when the "presentation" property is %s',
(presentation) => {
render({ presentation });
expect(screen.queryByRole('presentation')).toBeFalsy();
},
);

it('Has presentation role when the "presentation" property is true', () => {
render({ presentation: true });
expect(screen.getByRole('presentation')).toBeInTheDocument();
expect(screen.queryByRole('checkbox')).toBeFalsy();
});

it('Displays label and description when they are React nodes', () => {
const labelText = 'Label';
const descriptionText = 'Description';
render({
label: <span>{labelText}</span>,
description: <span>{descriptionText}</span>,
});
expect(screen.getByText(labelText)).toBeInTheDocument();
expect(screen.getByText(descriptionText)).toBeInTheDocument();
});
});

const render = (props: Partial<CheckboxProps> = {}) => {
Expand Down
9 changes: 6 additions & 3 deletions src/components/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ChangeEventHandler } from 'react';
import type { ChangeEventHandler, ReactNode } from 'react';
import React from 'react';
import cn from 'classnames';

Expand All @@ -13,13 +13,14 @@ export interface CheckboxProps {
checkboxId?: string;
checked?: boolean;
compact?: boolean;
description?: string;
description?: ReactNode;
disabled?: boolean;
error?: boolean;
hideLabel?: boolean;
label?: string;
label?: ReactNode;
name?: string;
onChange: ChangeEventHandler<HTMLInputElement>;
presentation?: boolean;
readOnly?: boolean;
}

Expand All @@ -34,6 +35,7 @@ export const Checkbox = ({
label,
name,
onChange,
presentation,
readOnly,
}: CheckboxProps) => (
<CheckboxRadioTemplate
Expand All @@ -54,6 +56,7 @@ export const Checkbox = ({
label={label}
name={name}
onChange={onChange}
presentation={presentation}
size={
compact
? CheckboxRadioTemplateSize.Xsmall
Expand Down
7 changes: 7 additions & 0 deletions src/components/CheckboxGroup/CheckboxGroup.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,13 @@ describe('CheckboxGroup', () => {
expect(screen.getByText(label)).toBeDefined();
expect(screen.getByText(description)).toBeDefined();
});

it('Renders all checkboxes with presentation role when the "presentation" property is true', () => {
render({ presentation: true });
const numOfCheckboxes = defaultProps.items.length;
expect(screen.queryAllByRole('presentation')).toHaveLength(numOfCheckboxes);
expect(screen.queryAllByRole('checkbox')).toHaveLength(0);
});
});

const render = (props: Partial<CheckboxGroupProps> = {}) => {
Expand Down
10 changes: 7 additions & 3 deletions src/components/CheckboxGroup/CheckboxGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ReactNode } from 'react';
import React, { useEffect, useReducer } from 'react';
import cn from 'classnames';

Expand Down Expand Up @@ -25,12 +26,13 @@ export type CheckedNames = string[];

export interface CheckboxGroupProps {
compact?: boolean;
description?: string;
description?: ReactNode;
disabled?: boolean;
error?: React.ReactNode;
error?: ReactNode;
items: CheckboxItem[];
legend?: string;
legend?: ReactNode;
onChange?: (names: CheckedNames) => void;
presentation?: boolean;
variant?: CheckboxGroupVariant;
}

Expand Down Expand Up @@ -59,6 +61,7 @@ export const CheckboxGroup = ({
items,
legend,
onChange,
presentation,
variant = CheckboxGroupVariant.Vertical,
}: CheckboxGroupProps) => {
if (!areItemsUnique(items.map((item) => item.name))) {
Expand Down Expand Up @@ -113,6 +116,7 @@ export const CheckboxGroup = ({
name: item.name,
});
}}
presentation={presentation}
/>
))}
</div>
Expand Down
11 changes: 11 additions & 0 deletions src/components/FieldSet/FieldSet.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ describe('FieldSet', () => {
render({ disabled: false });
expect(screen.getByRole('group')).toBeEnabled();
});

it('Displays legend and description if they are React nodes', () => {
const legendText = 'Legend';
const descriptionText = 'Description';
render({
legend: <span>{legendText}</span>,
description: <span>{descriptionText}</span>,
});
expect(screen.getByText(legendText)).toBeInTheDocument();
expect(screen.getByText(descriptionText)).toBeInTheDocument();
});
});

const render = (props: Partial<FieldSetProps> = {}) => {
Expand Down
9 changes: 5 additions & 4 deletions src/components/FieldSet/FieldSet.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ReactNode } from 'react';
import React from 'react';
import cn from 'classnames';

Expand All @@ -6,12 +7,12 @@ import { ErrorMessage } from '@/components/ErrorMessage';
import classes from './FieldSet.module.css';

export interface FieldSetProps {
children: React.ReactNode;
children: ReactNode;
className?: string;
description?: string;
description?: ReactNode;
disabled?: boolean;
error?: React.ReactNode;
legend?: string;
error?: ReactNode;
legend?: ReactNode;
size?: FieldSetSize;
}

Expand Down
25 changes: 25 additions & 0 deletions src/components/RadioButton/RadioButton.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,31 @@ describe('RadioButton', () => {
const wrapper = renderAndGetWrapper({ disabled: false });
expect(wrapper).not.toHaveClass('radio--disabled');
});

it.each([false, undefined])(
'Does not have presentation role when the "presentation" property is %s',
(presentation) => {
render({ presentation });
expect(screen.queryByRole('presentation')).toBeFalsy();
},
);

it('Has presentation role when the "presentation" property is true', () => {
render({ presentation: true });
expect(screen.getByRole('presentation')).toBeInTheDocument();
expect(screen.queryByRole('radio')).toBeFalsy();
});

it('Displays label and description when they are React nodes', () => {
const labelText = 'Label';
const descriptionText = 'Description';
render({
label: <span>{labelText}</span>,
description: <span>{descriptionText}</span>,
});
expect(screen.getByText(labelText)).toBeInTheDocument();
expect(screen.getByText(descriptionText)).toBeInTheDocument();
});
});

const render = (props: Partial<RadioButtonProps> = {}) => {
Expand Down
9 changes: 6 additions & 3 deletions src/components/RadioButton/RadioButton.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ChangeEventHandler } from 'react';
import type { ChangeEventHandler, ReactNode } from 'react';
import React from 'react';
import cn from 'classnames';

Expand All @@ -17,13 +17,14 @@ export enum RadioButtonSize {
export interface RadioButtonProps {
checked?: boolean;
className?: string;
description?: string;
description?: ReactNode;
disabled?: boolean;
error?: boolean;
hideLabel?: boolean;
label?: string;
label?: ReactNode;
name: string;
onChange: ChangeEventHandler<HTMLInputElement>;
presentation?: boolean;
radioId?: string;
size?: RadioButtonSize;
value: string;
Expand All @@ -38,6 +39,7 @@ export const RadioButton = ({
label,
name,
onChange,
presentation,
radioId,
size = RadioButtonSize.Small,
value,
Expand All @@ -58,6 +60,7 @@ export const RadioButton = ({
label={label}
name={name}
onChange={onChange}
presentation={presentation}
size={
size === RadioButtonSize.Xsmall
? CheckboxRadioTemplateSize.Xsmall
Expand Down
8 changes: 8 additions & 0 deletions src/components/RadioGroup/RadioGroup.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,14 @@ describe('RadioGroup', () => {
render({ size: RadioGroupSize.Small });
expect(screen.getByRole('radiogroup')).toHaveClass('radio-group--small');
});

it('Renders all radio buttons with presentation role when the "presentation" property is true', () => {
render({ presentation: true });
const numberOfRadios = defaultProps.items.length;
expect(screen.queryAllByRole('presentation')).toHaveLength(numberOfRadios);
expect(screen.queryAllByRole('radio')).toHaveLength(0);
expect(screen.queryAllByRole('radiogroup')).toHaveLength(0);
});
});

const render = (props?: Partial<RadioGroupProps>) =>
Expand Down
13 changes: 8 additions & 5 deletions src/components/RadioGroup/RadioGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ChangeEvent } from 'react';
import type { ChangeEvent, ReactNode } from 'react';
import React, { useEffect, useState } from 'react';

import { RadioButton, RadioButtonSize } from '@/components/RadioButton';
Expand Down Expand Up @@ -26,13 +26,14 @@ export enum RadioGroupVariant {
}

export interface RadioGroupProps {
description?: string;
description?: ReactNode;
disabled?: boolean;
error?: React.ReactNode;
error?: ReactNode;
items: RadioItem[];
legend?: string;
legend?: ReactNode;
name: string;
onChange?: (value?: string) => void;
presentation?: boolean;
size?: RadioGroupSize;
value?: string;
variant?: RadioGroupVariant;
Expand All @@ -46,6 +47,7 @@ export const RadioGroup = ({
legend,
name,
onChange,
presentation,
size = RadioGroupSize.Small,
value,
variant = RadioGroupVariant.Vertical,
Expand Down Expand Up @@ -91,7 +93,7 @@ export const RadioGroup = ({
classes[`radio-group--${variant}`],
classes[`radio-group--${size}`],
].join(' ')}
role='radiogroup'
role={presentation ? undefined : 'radiogroup'}
>
{items.map((radio) => (
<RadioButton
Expand All @@ -102,6 +104,7 @@ export const RadioGroup = ({
key={radio.value}
name={name}
onChange={changeHandler(radio.value)}
presentation={presentation}
size={radioButtonSize}
/>
))}
Expand Down
11 changes: 8 additions & 3 deletions src/components/_CheckboxRadioTemplate/CheckboxRadioTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@ export interface CheckboxRadioTemplateProps {
checked?: boolean;
children: ReactNode;
className?: string;
description?: string;
description?: ReactNode;
disabled?: boolean;
hideInput?: boolean;
hideLabel?: boolean;
inputId?: string;
inputWrapperClassName?: string;
label?: string;
label?: ReactNode;
name?: string;
onChange: ChangeEventHandler<HTMLInputElement>;
presentation?: boolean;
size: CheckboxRadioTemplateSize;
type: 'checkbox' | 'radio';
value?: string;
Expand All @@ -44,6 +45,7 @@ export const CheckboxRadioTemplate = ({
label,
name,
onChange,
presentation,
size,
type,
value,
Expand All @@ -68,14 +70,17 @@ export const CheckboxRadioTemplate = ({
<span className={classes['template__input-wrapper']}>
<input
aria-describedby={descriptionId}
aria-label={!showLabel ? label : undefined}
aria-label={
!showLabel && typeof label === 'string' ? label : undefined
}
aria-labelledby={showLabel ? labelId : undefined}
checked={checked ?? false}
className={classes['template__input-wrapper__input']}
disabled={disabled}
id={finalInputId}
name={name}
onChange={disabled ? undefined : onChange}
role={presentation ? 'presentation' : undefined}
type={type}
value={value}
/>
Expand Down