Skip to content
This repository was archived by the owner on Jul 14, 2023. It is now read-only.

Commit e8f9515

Browse files
[1.1.1] Fix controlled input props for Radio component (#22)
* Pass through control props for Radio component. * Typo fix.
1 parent 703cb12 commit e8f9515

File tree

7 files changed

+109
-29
lines changed

7 files changed

+109
-29
lines changed

packages/strum-react/CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.1.1] - 2022-08-19
9+
10+
## Fixed
11+
12+
- `onChange` prop not being correctly passed through to `<RadioGroup />` primitive
13+
814
## [1.1.0] - 2022-08-19
915

1016
## Added

packages/strum-react/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@strum/react",
3-
"version": "1.1.0",
3+
"version": "1.1.1",
44
"description": "A React design system built with Vanilla Extract",
55
"license": "MIT",
66
"repository": {
@@ -91,4 +91,4 @@
9191
"react": ">=17",
9292
"react-dom": ">=17"
9393
}
94-
}
94+
}

packages/strum-react/src/components/Checkbox/Checkbox.test.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ describe('<Checkbox />', () => {
1212
expect(screen.getByRole('label', { name })).toBeVisible();
1313
});
1414

15+
it('passing checked controls the input', async () => {
16+
render(<Checkbox checked id="test-id" label={name} />);
17+
18+
const checkbox = screen.getByRole('checkbox', { checked: true });
19+
expect(checkbox).toBeVisible();
20+
});
21+
1522
it('toggles checked state', async () => {
1623
const user = userEvent.setup();
1724
render(<Checkbox id="test-id" label={name} />);

packages/strum-react/src/components/Radio/Radio.docs.mdx

+12
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,15 @@ Radio groups take an optional `label` prop which will display text to describe t
5454
<RadioItem id="error-piano" label="Piano" value="piano" />
5555
</RadioGroup>
5656
```
57+
58+
## Controlled input
59+
60+
To control your radio group with React, simply include a `value` boolean prop and a `onChange` function prop.
61+
62+
```tsx live name="controlled radio input" value="guitar" onChange={(value) => {alert(value)}}
63+
<RadioGroup onChange={onChange} value={value}>
64+
<RadioItem id="controlled-clarinet" label="Clarinet" value="clarinet" />
65+
<RadioItem id="controlled-guitar" label="Guitar" value="guitar" />
66+
<RadioItem id="controlled-piano" label="Piano" value="piano" />
67+
</RadioGroup>
68+
```
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import { cleanup, render, screen } from '../../../test';
1+
import { cleanup, render, screen, userEvent } from '../../../test';
22
import { RadioGroup, RadioItem } from './Radio';
33

4+
const label = 'Test item 1';
5+
const value = 'test-item-1';
6+
47
describe('<Radio />', () => {
58
afterEach(cleanup);
69

710
it('renders', () => {
8-
const label = 'Test item 1';
9-
const value = 'test-item-1';
10-
1111
render(
1212
<>
1313
<RadioGroup>
@@ -19,4 +19,57 @@ describe('<Radio />', () => {
1919
expect(screen.getByRole('radio', { name: label })).toBeVisible();
2020
expect(screen.getByRole('label', { name: label })).toBeVisible();
2121
});
22+
23+
it('passing value controls the input', async () => {
24+
const label1 = 'radio-1';
25+
const label2 = 'radio-2';
26+
const value1 = 'value-1';
27+
const value2 = 'value-2';
28+
29+
render(
30+
<RadioGroup value={value2}>
31+
<RadioItem label={label1} value={value1} />
32+
<RadioItem label={label2} value={value2} />
33+
</RadioGroup>,
34+
);
35+
36+
const radio1 = screen.getByRole('radio', { name: label1 });
37+
const radio2 = screen.getByRole('radio', { name: label2 });
38+
expect(radio1).not.toBeChecked();
39+
expect(radio2).toBeChecked();
40+
});
41+
42+
it('changes selected radio item', async () => {
43+
const user = userEvent.setup();
44+
const label1 = 'radio-1';
45+
const label2 = 'radio-2';
46+
const value1 = 'value-1';
47+
const value2 = 'value-2';
48+
49+
render(
50+
<RadioGroup>
51+
<RadioItem label={label1} value={value1} />
52+
<RadioItem label={label2} value={value2} />
53+
</RadioGroup>,
54+
);
55+
56+
const radio1 = screen.getByRole('radio', { name: label1 });
57+
const radio2 = screen.getByRole('radio', { name: label2 });
58+
59+
await user.click(radio2);
60+
61+
expect(radio1).not.toBeChecked();
62+
expect(radio2).toBeChecked();
63+
});
64+
65+
it('sets the disabled state of the input', () => {
66+
render(
67+
<RadioGroup>
68+
<RadioItem disabled label={label} value={value} />
69+
</RadioGroup>,
70+
);
71+
expect(screen.getByRole('radio', { name: label })).toHaveAttribute(
72+
'disabled',
73+
);
74+
});
2275
});

packages/strum-react/src/components/Radio/Radio.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,16 @@ type RadioGroupProps = {
1818
} & RadixRadio.RadioGroupProps;
1919

2020
export const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>(
21-
({ children, error, label, ...primitiveProps }, ref) => {
21+
({ children, error, label, onChange, value, ...primitiveProps }, ref) => {
2222
const errorId = error ? `${label}-error` : undefined;
2323

2424
return (
2525
<>
2626
<RadixRadio.Root
2727
className={error ? styles.radioGroupWithErrorStyle : undefined}
28+
onValueChange={onChange}
2829
ref={ref}
30+
value={value}
2931
{...primitiveProps}
3032
>
3133
{label && (

packages/strum-react/src/components/Select/Select.test.tsx

+22-22
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,28 @@ describe('<Select />', () => {
99
expect(screen.getByRole('combobox')).toBeVisible();
1010
});
1111

12+
it('passing value controls the input', async () => {
13+
const user = userEvent.setup();
14+
15+
render(
16+
<Select placeholder="Select an instrument" value="piano">
17+
<SelectItem text="Guitar" value="guitar" />
18+
<SelectItem text="Piano" value="piano" />
19+
</Select>,
20+
);
21+
22+
await user.click(screen.getByRole('combobox'));
23+
24+
expect(screen.getByRole('option', { name: 'Guitar' })).toHaveAttribute(
25+
'aria-selected',
26+
'false',
27+
);
28+
expect(screen.getByRole('option', { name: 'Piano' })).toHaveAttribute(
29+
'aria-selected',
30+
'true',
31+
);
32+
});
33+
1234
it('clicking the select shows options', async () => {
1335
const user = userEvent.setup();
1436

@@ -34,26 +56,4 @@ describe('<Select />', () => {
3456
expect(screen.getByText('Violin')).toBeVisible();
3557
expect(screen.getByText('Clarinet')).toBeVisible();
3658
});
37-
38-
it('passing value controls the input', async () => {
39-
const user = userEvent.setup();
40-
41-
render(
42-
<Select placeholder="Select an instrument" value="piano">
43-
<SelectItem text="Guitar" value="guitar" />
44-
<SelectItem text="Piano" value="piano" />
45-
</Select>,
46-
);
47-
48-
await user.click(screen.getByRole('combobox'));
49-
50-
expect(screen.getByRole('option', { name: 'Guitar' })).toHaveAttribute(
51-
'aria-selected',
52-
'false',
53-
);
54-
expect(screen.getByRole('option', { name: 'Piano' })).toHaveAttribute(
55-
'aria-selected',
56-
'true',
57-
);
58-
});
5959
});

0 commit comments

Comments
 (0)