Skip to content

Commit

Permalink
fix(fuselage): RadioButton a11y best practices improvement (#1162)
Browse files Browse the repository at this point in the history
Co-authored-by: Douglas Fabris <devfabris@gmail.com>
  • Loading branch information
dougfabris committed Sep 15, 2023
1 parent bbe8890 commit 387c401
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 45 deletions.
5 changes: 5 additions & 0 deletions .changeset/lucky-doors-warn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/fuselage": patch
---

fix(fuselage): RadioButton a11y best practices improvement
55 changes: 51 additions & 4 deletions packages/fuselage/src/components/RadioButton/RadioButton.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,57 @@
import { composeStories } from '@storybook/testing-react';
import { render } from '@testing-library/react';
import { axe } from 'jest-axe';
import React from 'react';

import { RadioButton } from '.';
import * as stories from './RadioButton.stories';

describe('RadioButton Component', () => {
it('renders without crashing', () => {
render(<RadioButton />);
const testCases = Object.values(composeStories(stories))
.filter((story) => story.storyName !== 'States')
.map((Story) => [Story.storyName || 'Story', Story]);

const { Default, Checked, Disabled } = composeStories(stories);

describe('[RadioButton Rendering]', () => {
test.each(testCases)(
`renders %s without crashing`,
async (_storyname, Story) => {
const tree = render(<Story />);
expect(tree.baseElement).toMatchSnapshot();
}
);

test.each(testCases)(
'%s should have no a11y violations',
async (_storyname, Story) => {
const { container } = render(<Story />);

const results = await axe(container);
expect(results).toHaveNoViolations();
}
);
});

describe('[RadioButton Interacting]', () => {
it('changes style of element as radio button is checked', () => {
const { container } = render(<Default />);
const radioButton = container.querySelector(
'input[type="radio"]'
) as HTMLInputElement;
radioButton.click();
expect(radioButton.checked).toEqual(true);
});
it('displays radio button with defaultChecked value correctly', () => {
const { container } = render(<Checked />);
const radioButton = container.querySelector(
'input[type="radio"]'
) as HTMLInputElement;
expect(radioButton.defaultChecked).toEqual(true);
});
it('displays radio button disabled correctly', () => {
const { container } = render(<Disabled />);
const radioButton = container.querySelector(
'input[type="radio"]'
) as HTMLInputElement;
expect(radioButton.disabled).toEqual(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import type { ComponentStory, ComponentMeta } from '@storybook/react';
import React from 'react';

import { RadioButton } from '../..';
import { PropsVariationSection } from '../../../.storybook/helpers';
import {
PropsVariationSection,
DECORATOR_LABEL,
} from '../../../.storybook/helpers';

export default {
title: 'Inputs/RadioButton',
Expand All @@ -35,7 +38,7 @@ export default {
} as ComponentMeta<typeof RadioButton>;

const Template: ComponentStory<typeof RadioButton> = (args) => (
<RadioButton {...args} />
<RadioButton {...args} aria-label={DECORATOR_LABEL} />
);

export const Default: ComponentStory<typeof RadioButton> = Template.bind({});
Expand All @@ -57,7 +60,7 @@ Disabled.args = {
export const States: ComponentStory<typeof RadioButton> = () => (
<PropsVariationSection
component={RadioButton}
common={{ onChange: action('change') }}
common={{ 'onChange': action('change'), 'aria-label': DECORATOR_LABEL }}
xAxis={{
checked: { checked: true },
unchecked: { checked: false },
Expand Down
44 changes: 6 additions & 38 deletions packages/fuselage/src/components/RadioButton/RadioButton.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,24 @@
import type { ComponentProps, Ref } from 'react';
import type { AllHTMLAttributes, ComponentProps, Ref } from 'react';
import React, { forwardRef } from 'react';

import Box from '../Box';
import { Label } from '../Label';

type RadioButtonProps = ComponentProps<typeof Box> & {
'qa'?: unknown;
'data-qa'?: unknown;
};
type RadioButtonProps = ComponentProps<typeof Box> &
AllHTMLAttributes<HTMLInputElement>;

export const RadioButton = forwardRef(function RadioButton(
{
autoComplete,
checked,
defaultChecked,
disabled,
form,
id,
name,
required,
tabIndex,
value,
qa,
'data-qa': dataQa,
onChange,
onInput,
onInvalid,
...props
}: RadioButtonProps,
props: RadioButtonProps,
ref: Ref<HTMLInputElement>
) {
return (
<Box is={Label} rcx-radio-button {...props}>
<Box is={Label} rcx-radio-button>
<Box
is='input'
rcx-radio-button__input
autoComplete={autoComplete}
checked={checked}
defaultChecked={defaultChecked}
disabled={disabled}
form={form}
id={id}
name={name}
required={required}
tabIndex={tabIndex}
type='radio'
value={value}
data-qa={dataQa || qa}
ref={ref}
onChange={onChange}
onInput={onInput}
onInvalid={onInvalid}
{...props}
/>
<Box is='i' rcx-radio-button__fake aria-hidden='true' />
</Box>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`[RadioButton Rendering] renders Checked without crashing 1`] = `
<body>
<div>
<label
class="rcx-box rcx-box--full rcx-label rcx-box rcx-box--full rcx-radio-button"
>
<input
aria-label="Decorator Label"
checked=""
class="rcx-box rcx-box--full rcx-radio-button__input"
type="radio"
/>
<i
aria-hidden="true"
class="rcx-box rcx-box--full rcx-radio-button__fake"
/>
</label>
</div>
</body>
`;

exports[`[RadioButton Rendering] renders Default without crashing 1`] = `
<body>
<div>
<label
class="rcx-box rcx-box--full rcx-label rcx-box rcx-box--full rcx-radio-button"
>
<input
aria-label="Decorator Label"
class="rcx-box rcx-box--full rcx-radio-button__input"
type="radio"
/>
<i
aria-hidden="true"
class="rcx-box rcx-box--full rcx-radio-button__fake"
/>
</label>
</div>
</body>
`;

exports[`[RadioButton Rendering] renders Disabled without crashing 1`] = `
<body>
<div>
<label
class="rcx-box rcx-box--full rcx-label rcx-box rcx-box--full rcx-radio-button"
>
<input
aria-label="Decorator Label"
class="rcx-box rcx-box--full rcx-radio-button__input"
disabled=""
type="radio"
/>
<i
aria-hidden="true"
class="rcx-box rcx-box--full rcx-radio-button__fake"
/>
</label>
</div>
</body>
`;

0 comments on commit 387c401

Please sign in to comment.