Skip to content

Commit

Permalink
fix(FormGroupLabel): enable screenreaders to read a label's "optional…
Browse files Browse the repository at this point in the history
…" text

Currently, Gamut's forms' labels include either `*` to signify required, or `(optional)` to tell learners what fields are necessary. 
Both are set to `aria-hidden` but a test.io filing requests that the `(optional)` be read aloud.

This PR changes the `(optional)` text to be read along with the label text and keeps the `aria-hidden` on the `*`.
  • Loading branch information
LinKCoding authored Jan 15, 2025
1 parent c1a9e00 commit 3b1a3a5
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 41 deletions.
13 changes: 2 additions & 11 deletions packages/gamut/src/Form/__tests__/FormGroup.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const renderView = setupRtl(FormGroup, {

const label = 'up dog';
const htmlFor = 'up-dog';
const optional = '(optional)';
const optionalLabelText = `${label} (optional)`;

describe('FormGroup', () => {
Expand All @@ -19,22 +18,14 @@ describe('FormGroup', () => {

view.getByLabelText(optionalLabelText);
});
it('render (optional) as aria-hidden', async () => {
const { view } = renderView({ label, htmlFor });
expect(view.getByText(optional)).toHaveAttribute('aria-hidden', 'true');
});

});
describe('when htmlFor is not provided', () => {
it('renders Label as a div', () => {
const { view } = renderView({ label });

expect(view.queryByLabelText(optionalLabelText)).toBeNull();
view.getByText(label);
});

it('render (optional) as aria-hidden', () => {
const { view } = renderView({ label });
expect(view.getByText(optional)).toHaveAttribute('aria-hidden', 'true');
view.getByText(/up dog/);
});
});

Expand Down
8 changes: 5 additions & 3 deletions packages/gamut/src/Form/elements/FormGroupLabel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,11 @@ export const FormGroupLabel: React.FC<FormGroupLabelProps> = ({
>
{children}
{!isSoloField && (
<Text as="span" aria-hidden>
{required ? '*' : ' \u00A0(optional)'}
</Text>
required ? (
<Text as="span" aria-hidden>*</Text>
) : (
'\u00A0(optional)'
)
)}
</Label>
{infotip && <InfoTip {...infotip} />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('GridFormContent', () => {
const { view } = renderView();

view.getByLabelText('Stub Text (optional)');
view.getByRole('textbox', { name: 'Stub Text' });
view.getByRole('textbox', { name: /Stub Text/ });
});

it('gives the field access to form context and validation', async () => {
Expand All @@ -34,7 +34,7 @@ describe('GridFormContent', () => {
mode: 'onChange',
});

fireEvent.input(view.getByRole('textbox', { name: 'Stub Text' }), {
fireEvent.input(view.getByRole('textbox', { name: /Stub Text/ }), {
target: {
value:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { setupRtl } from '@codecademy/gamut-tests';
import { fireEvent } from '@testing-library/dom';
import { act } from 'react';

import {
stubFileField,
Expand Down Expand Up @@ -48,9 +49,9 @@ describe('GridFormSections', () => {
const { view } = renderView();

const textLabel = view.getByLabelText('Stub Text (optional)');
const textField = view.getByRole('textbox', { name: 'Stub Text' });
const textField = view.getByRole('textbox', { name: /Stub Text/ });
const radioLabel = view.getByLabelText('Stub Select (optional)');
const radioField = view.getByRole('combobox', { name: 'Stub Select' });
const radioField = view.getByRole('combobox', { name: /Stub Select/ });

expect(textLabel).toBeTruthy();
expect(textField).toBeTruthy();
Expand All @@ -64,18 +65,22 @@ describe('GridFormSections', () => {
mode: 'onChange',
});

fireEvent.input(view.getByRole('textbox', { name: 'Stub Text' }), {
target: {
value:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
},
await act(async () => {
fireEvent.input(view.getByRole('textbox', { name: 'Stub Text' }), {
target: {
value:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
},
});
});

fireEvent.input(view.getByRole('textbox', { name: 'Stub Text Again' }), {
target: {
value: 'Do you know what that is?',
},
});
await act(async () => {
fireEvent.input(view.getByRole('textbox', { name: 'Stub Text Again' }), {
target: {
value: 'Do you know what that is?',
},
});
})

await view.findByText('what is it?');
await view.findByText('not enough updog');
Expand All @@ -87,17 +92,21 @@ describe('GridFormSections', () => {
mode: 'onChange',
});

fireEvent.input(view.getByRole('textbox', { name: 'Stub Text' }), {
target: {
value:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
},
await act(async () => {
fireEvent.input(view.getByRole('textbox', { name: 'Stub Text' }), {
target: {
value:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
},
});
});

fireEvent.input(view.getByRole('textbox', { name: 'Stub Text Again' }), {
target: {
value: 'Do you know what that is?',
},
await act(async () => {
fireEvent.input(view.getByRole('textbox', { name: 'Stub Text Again' }), {
target: {
value: 'Do you know what that is?',
},
});
});

expect(await view.findAllByRole('alert')).toHaveLength(1);
Expand Down
8 changes: 4 additions & 4 deletions packages/gamut/src/GridForm/__tests__/GridForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,10 @@ const getBaseCases = (view: RenderResult<typeof queries, HTMLElement>) => {
name: 'Check me!',
}) as HTMLInputElement;
const selectField = view.getByRole('combobox', {
name: 'Stub Select',
name: /Stub Select/,
}) as HTMLInputElement;
const textField = view.getByRole('textbox', {
name: 'Stub Text',
name: /Stub Text/,
}) as HTMLInputElement;
return { checkboxField, selectField, textField };
};
Expand Down Expand Up @@ -257,7 +257,7 @@ describe('GridForm', () => {
},
});

const textField = view.getByRole('textbox', { name: 'Stub Text' });
const textField = view.getByRole('textbox', { name: /Stub Text/ });

await act(async () => {
fireEvent.input(textField, {
Expand All @@ -284,7 +284,7 @@ describe('GridForm', () => {
validation: 'onChange',
});

const textField = view.getByRole('textbox', { name: 'Stub Text' });
const textField = view.getByRole('textbox', { name: /Stub Text/ });

await act(async () => {
fireEvent.input(textField, {
Expand Down

0 comments on commit 3b1a3a5

Please sign in to comment.