Skip to content
Open
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
11 changes: 10 additions & 1 deletion src/components/RadioButton/RadioButtonItem.tsx

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about updating the snapshots & adding regression test here?
existing RadioButton.Item tests use toMatchSnapshot() & those snapshots currently contain focusable={true} for inner radio.

since this PR changes that rendered output but only updates the component file, the snapshot tests will fail when CI runs, I suppose

it would also be useful to guard against the duplicate control explicitly:

it('exposes only one radio control', async () => {
  await render(
    <RadioButton.Item
      label="Radio button"
      value="radio"
      status="unchecked"
    />
  );

  expect(screen.getAllByRole('radio')).toHaveLength(1);
});

Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,16 @@ const RadioButtonItem = ({
color,
theme,
uncheckedColor,
};
// The outer TouchableRipple in RadioButtonItem already provides the
// interactive surface. Hide the inner RadioButton from the
// accessibility tree so screen readers only encounter one control,
// and keep tabIndex: -1 so web keyboard users have a single
// tabstop per row (matches the approach used in Checkbox.Item).
focusable: false,
tabIndex: -1,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we also hide inner RadioButton from the accessibility tree?

tabIndex: -1 only removes it from tab sequence - it remains focusable programmatically according to W3C guidance
inner Android & iOS controls still expose role="radio" & aria-checked, so screen readers may encounter 2 radio controls

so could we explicitly hide inner visual control as documented for aria-hidden?

Suggested change
tabIndex: -1,
tabIndex: -1,
accessible: false,
'aria-hidden': true,

this follows the same approach already used by Checkbox.Item

accessible: false,
'aria-hidden': true,
} as const;
const isLeading = position === 'leading';
let radioButton: any;

Expand Down
13 changes: 13 additions & 0 deletions src/components/__tests__/RadioButton/RadioButtonItem.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,16 @@ it('should execute onLongPress', async () => {

expect(onLongPress).toHaveBeenCalledTimes(1);
});

it('exposes only one radio control to screen readers', async () => {
await render(
<RadioButton.Item
label="Radio button"
value="radio"
status="unchecked"
/>
);

expect(screen.getAllByRole('radio')).toHaveLength(1);
});

Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,10 @@ exports[`can render leading radio button control 1`] = `
"text": undefined,
}
}
accessible={true}
accessible={false}
aria-hidden={true}
collapsable={false}
focusable={true}
focusable={false}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
Expand Down Expand Up @@ -288,9 +289,10 @@ exports[`can render the Android radio button on different platforms 1`] = `
"text": undefined,
}
}
accessible={true}
accessible={false}
aria-hidden={true}
collapsable={false}
focusable={true}
focusable={false}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
Expand Down Expand Up @@ -440,9 +442,10 @@ exports[`can render the iOS radio button on different platforms 1`] = `
"text": undefined,
}
}
accessible={true}
accessible={false}
aria-hidden={true}
collapsable={false}
focusable={true}
focusable={false}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
Expand Down Expand Up @@ -618,9 +621,10 @@ exports[`renders unchecked 1`] = `
"text": undefined,
}
}
accessible={true}
accessible={false}
aria-hidden={true}
collapsable={false}
focusable={true}
focusable={false}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
Expand Down