Skip to content

Commit

Permalink
feat: Add ariaControls prop to RadioGroup
Browse files Browse the repository at this point in the history
  • Loading branch information
fa7ad committed Jan 25, 2023
1 parent d52870f commit e90d8dc
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 0 deletions.
113 changes: 113 additions & 0 deletions pages/radio-group/progressive-disclosure.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useReducer, useState } from 'react';
import Box from '~components/box';
import FormField from '~components/form-field';
import RadioGroup from '~components/radio-group';
import Select, { SelectProps } from '~components/select';

const langOptions: Array<SelectProps.Option> = [
{ value: 'en-US', label: 'English (United States)' },
{ value: 'en-GB', label: 'English (United Kingdom)' },
{ value: 'en-NZ', label: 'English (New Zealand)' },
{ value: 'de-DE', label: 'Deutsch (Deutschland)' },
{ value: 'de-CH', label: 'Deutsch (Schweiz)' },
{ value: 'de-AT', label: 'Deutsch (Österreich)' },
];

const optionsPerLanguage = langOptions.reduce((acc, option) => {
const [lang] = option.value!.split('-');
if (!acc[lang]) {
acc[lang] = [];
}
acc[lang].push(option);
return acc;
}, {} as Record<string, Array<SelectProps.Option>>);

const initialState = {
en: optionsPerLanguage.en[0],
de: optionsPerLanguage.de[0],
};

type State = { [K in keyof typeof initialState]: SelectProps.Option };
interface Action {
type: `update/${string & keyof State}`;
payload: Partial<State[keyof State]>;
}

const langReducer: React.Reducer<State, Action> = (state = initialState, action) => {
switch (action.type) {
case 'update/en':
return { ...state, en: action.payload };
case 'update/de':
return { ...state, de: action.payload };
default:
return state;
}
};

export default function RadiosPage() {
const [radioSelection, setRadioSelection] = useState<string>('');

const [lang, dispatch] = useReducer(langReducer, initialState);
const setLang = (type: keyof State, option: SelectProps.Option) =>
dispatch({ type: `update/${type}`, payload: option });

return (
<Box padding="l">
<h1>Radio group progressive disclosure demo</h1>
<FormField
label="Language settings"
description="You can select a specific language or have this service automatically identify the language in your media."
>
<RadioGroup
value={radioSelection}
onChange={event => setRadioSelection(event.detail.value)}
ariaControls="dialekt-settings"
items={[
{
label: 'English',
value: 'en',
description:
'If you know the language spoken in the source media is English, choose this option for optimal results.',
},
{
label: 'Deutsch',
value: 'de',
description:
'Wenn Sie wissen, dass die in den Quellmedien gesprochene Sprache Deutsch ist, wählen Sie diese Option für optimale Ergebnisse.',
},
{
label: 'Automatic language detection',
value: 'automatic',
description: 'If you do not know the language spoken in the source media, choose this option.',
},
]}
/>
</FormField>
<Box id="dialekt-settings" display={!Object.keys(lang).includes(radioSelection) ? 'none' : 'block'}>
{radioSelection === 'en' && (
<FormField label="English Dialect" description="Select the English dialect you want to use.">
<Select
selectedOption={lang.en}
onChange={event => setLang('en', event.detail.selectedOption)}
options={optionsPerLanguage.en}
/>
</FormField>
)}
{radioSelection === 'de' && (
<FormField
label="Deutscher Dialekt"
description="Wählen Sie das Deutsch-Dialekt aus, den Sie verwenden möchten."
>
<Select
selectedOption={lang.de}
onChange={event => setLang('de', event.detail.selectedOption)}
options={optionsPerLanguage.de}
/>
</FormField>
)}
</Box>
</Box>
);
}
6 changes: 6 additions & 0 deletions src/__tests__/__snapshots__/documenter.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -9362,6 +9362,12 @@ Object {
],
"name": "RadioGroup",
"properties": Array [
Object {
"description": "Adds \`aria-controls\` attribute to the radio group. Use this property if the radio group controls the visibility of other elements on the page.",
"name": "ariaControls",
"optional": true,
"type": "string",
},
Object {
"description": "Adds \`aria-describedby\` to the component. If you're using this component within a form field,
don't set this property because the form field component automatically sets it.
Expand Down
4 changes: 4 additions & 0 deletions src/radio-group/__tests__/radio-group.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ test('renders attributes for assistive technology when set', function () {
const ariaLabelledby = 'something';
const ariaDescribedby = 'something else';
const ariaLabel = 'the last one';
const ariaControls = 'some-id-being-controlled';

const { wrapper } = renderRadioGroup(
<RadioGroup
ariaLabelledby={ariaLabelledby}
ariaDescribedby={ariaDescribedby}
ariaLabel={ariaLabel}
ariaControls={ariaControls}
value={null}
items={defaultItems}
/>
Expand All @@ -45,6 +47,7 @@ test('renders attributes for assistive technology when set', function () {
expect(rootElement).toHaveAttribute('aria-labelledby', ariaLabelledby);
expect(rootElement).toHaveAttribute('aria-describedby', ariaDescribedby);
expect(rootElement).toHaveAttribute('aria-label', ariaLabel);
expect(rootElement).toHaveAttribute('aria-controls', ariaControls);
});

test('does not render attributes for assistive technology when not set', function () {
Expand All @@ -53,6 +56,7 @@ test('does not render attributes for assistive technology when not set', functio
expect(rootElement).not.toHaveAttribute('aria-labelledby');
expect(rootElement).not.toHaveAttribute('aria-describedby');
expect(rootElement).not.toHaveAttribute('aria-label');
expect(rootElement).not.toHaveAttribute('aria-controls');
});

describe('name', () => {
Expand Down
5 changes: 5 additions & 0 deletions src/radio-group/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ export interface RadioGroupProps extends BaseComponentProps, FormFieldControlPro
*/
ariaRequired?: boolean;

/**
* Adds `aria-controls` attribute to the radio group. Use this property if the radio group controls the visibility of other elements on the page.
*/
ariaControls?: string;

/**
* Called when the user selects a different radio button. The event `detail` contains the current `value`.
*/
Expand Down
2 changes: 2 additions & 0 deletions src/radio-group/internal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const InternalRadioGroup = React.forwardRef(
items,
ariaLabel,
ariaRequired,
ariaControls,
onChange,
__internalRootRef = null,
...props
Expand All @@ -40,6 +41,7 @@ const InternalRadioGroup = React.forwardRef(
aria-label={ariaLabel}
aria-describedby={ariaDescribedby}
aria-required={ariaRequired}
aria-controls={ariaControls}
{...baseProps}
className={clsx(baseProps.className, styles.root)}
ref={__internalRootRef}
Expand Down

0 comments on commit e90d8dc

Please sign in to comment.