Skip to content

Commit 0b0cc72

Browse files
committed
feat: Add radio button component to Core
1 parent 60f9568 commit 0b0cc72

File tree

33 files changed

+1354
-212
lines changed

33 files changed

+1354
-212
lines changed

build-tools/utils/pluralize.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ const pluralizationMap = {
5959
ProgressBar: 'ProgressBars',
6060
PromptInput: 'PromptInputs',
6161
PropertyFilter: 'PropertyFilters',
62+
RadioButton: 'RadioButtons',
6263
RadioGroup: 'RadioGroups',
6364
S3ResourceSelector: 'S3ResourceSelectors',
6465
SegmentedControl: 'SegmentedControls',

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@
166166
{
167167
"path": "lib/components/internal/widget-exports.js",
168168
"brotli": false,
169-
"limit": "885 kB",
169+
"limit": "895 kB",
170170
"ignore": "react-dom"
171171
}
172172
],

pages/radio-button/cards.page.tsx

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
import React, { useContext, useRef, useState } from 'react';
4+
import clsx from 'clsx';
5+
6+
import Box from '~components/box';
7+
import Checkbox from '~components/checkbox';
8+
import FormField from '~components/form-field';
9+
import RadioButton, { RadioButtonProps } from '~components/radio-button';
10+
import SpaceBetween from '~components/space-between';
11+
12+
import AppContext, { AppContextType } from '../app/app-context';
13+
import ScreenshotArea from '../utils/screenshot-area';
14+
import { ExtraOptions, options } from './common';
15+
16+
import styles from './cards.scss';
17+
18+
type RadioButtonDemoContext = React.Context<
19+
AppContextType<{
20+
disabled?: boolean;
21+
readOnly?: boolean;
22+
}>
23+
>;
24+
25+
const Card = ({
26+
value,
27+
label,
28+
checked,
29+
onChange,
30+
description,
31+
disabled,
32+
readOnly,
33+
}: Omit<RadioButtonProps, 'name'> & { label: string }) => {
34+
const ref = useRef<HTMLInputElement>(null);
35+
return (
36+
<li className={clsx(styles.card, checked && styles['card--selected'])}>
37+
<div>
38+
<h2 className={styles.heading}>
39+
<a href="#">{label}</a>
40+
</h2>
41+
<p className={styles.description}>{description}</p>
42+
</div>
43+
<label aria-label={`Select ${label}`}>
44+
<RadioButton
45+
value={value}
46+
name="quantity"
47+
checked={checked}
48+
disabled={disabled}
49+
readOnly={readOnly}
50+
onChange={onChange}
51+
ref={ref}
52+
/>
53+
</label>
54+
</li>
55+
);
56+
};
57+
58+
export default function RadioButtonsCardsPage() {
59+
const { urlParams, setUrlParams } = useContext(AppContext as RadioButtonDemoContext);
60+
61+
const [value, setValue] = useState<string>('');
62+
63+
return (
64+
<article>
65+
<h1>Radio button — custom card selection</h1>
66+
<Box margin={{ horizontal: 'm' }}>
67+
<SpaceBetween size="s">
68+
<FormField label="Options">
69+
<SpaceBetween size="s">
70+
<Checkbox
71+
checked={!!urlParams.disabled}
72+
onChange={({ detail }) => setUrlParams({ ...urlParams, disabled: detail.checked })}
73+
description="Make one of the radio buttons disabled"
74+
>
75+
Disabled
76+
</Checkbox>
77+
78+
<Checkbox
79+
checked={!!urlParams.readOnly}
80+
onChange={({ detail }) => setUrlParams({ ...urlParams, readOnly: detail.checked })}
81+
description="Make one of the radio buttons read-only"
82+
>
83+
Read-only
84+
</Checkbox>
85+
</SpaceBetween>
86+
</FormField>
87+
</SpaceBetween>
88+
</Box>
89+
<hr />
90+
<ScreenshotArea disableAnimations={true}>
91+
<ul className={styles.cards}>
92+
{options.map((option, index) => (
93+
<Card
94+
key={index}
95+
value={option.value}
96+
label={option.label}
97+
description={option.description}
98+
checked={option.value === value}
99+
onChange={({ detail }) => {
100+
if (detail.checked) {
101+
setValue(option.value);
102+
}
103+
}}
104+
disabled={option.allowDisabled && urlParams.disabled}
105+
readOnly={option.allowReadOnly && urlParams.readOnly}
106+
/>
107+
))}
108+
</ul>
109+
<Box display={value === 'mail' ? 'block' : 'none'} margin={{ top: 'm' }}>
110+
<ExtraOptions />
111+
</Box>
112+
</ScreenshotArea>
113+
</article>
114+
);
115+
}

pages/radio-button/cards.scss

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
@use '~design-tokens' as awsui;
7+
8+
.cards {
9+
display: flex;
10+
flex-wrap: wrap;
11+
gap: awsui.$space-scaled-xs;
12+
list-style-type: none;
13+
margin-block: 0;
14+
margin-inline: 0;
15+
padding-block: 0;
16+
padding-inline: 0;
17+
}
18+
19+
.card {
20+
$border-radius: awsui.$space-scaled-s;
21+
align-items: start;
22+
border-color: awsui.$color-border-control-default;
23+
border-start-start-radius: $border-radius;
24+
border-start-end-radius: $border-radius;
25+
border-end-start-radius: $border-radius;
26+
border-end-end-radius: $border-radius;
27+
border-block-style: solid;
28+
border-inline-style: solid;
29+
border-block-width: 1px;
30+
border-inline-width: 1px;
31+
display: flex;
32+
gap: awsui.$space-scaled-s;
33+
inline-size: 300px;
34+
justify-content: space-between;
35+
padding-block: awsui.$space-scaled-l;
36+
padding-inline: awsui.$space-scaled-l;
37+
38+
&--selected {
39+
border-color: awsui.$color-border-item-focused;
40+
}
41+
}
42+
43+
.heading {
44+
margin-block: 0;
45+
}
46+
.description {
47+
margin-block: awsui.$space-scaled-xs 0;
48+
}

pages/radio-button/common.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
import React, { useState } from 'react';
4+
5+
import FormField from '~components/form-field';
6+
import Input from '~components/input';
7+
8+
// SPDX-License-Identifier: Apache-2.0
9+
export const options = [
10+
{ value: 'email', label: 'E-Mail', description: 'First option' },
11+
{ value: 'phone', label: 'Telephone', description: 'Second option', allowDisabled: true, allowReadOnly: true },
12+
{ value: 'mail', label: 'Postal mail', description: 'Third option' },
13+
];
14+
15+
export const ExtraOptions = () => {
16+
const [value, setValue] = useState('');
17+
return (
18+
<FormField label="Address">
19+
<Input value={value} onChange={({ detail }) => setValue(detail.value)} placeholder="Enter your address" />
20+
</FormField>
21+
);
22+
};

pages/radio-button/styles.scss

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
@use '~design-tokens' as awsui;
7+
8+
.radio-group {
9+
display: flex;
10+
column-gap: awsui.$space-scaled-m;
11+
row-gap: awsui.$space-scaled-xs;
12+
&--horizontal {
13+
flex-direction: row;
14+
flex-wrap: wrap;
15+
}
16+
&--vertical {
17+
flex-direction: column;
18+
}
19+
}

pages/radio-button/table.page.tsx

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
import React, { useContext, useRef, useState } from 'react';
4+
import clsx from 'clsx';
5+
6+
import Box from '~components/box';
7+
import Checkbox from '~components/checkbox';
8+
import FormField from '~components/form-field';
9+
import RadioButton, { RadioButtonProps } from '~components/radio-button';
10+
import SpaceBetween from '~components/space-between';
11+
12+
import AppContext, { AppContextType } from '../app/app-context';
13+
import ScreenshotArea from '../utils/screenshot-area';
14+
import { ExtraOptions, options } from './common';
15+
16+
import styles from './table.scss';
17+
18+
type RadioButtonDemoContext = React.Context<
19+
AppContextType<{
20+
disabled?: boolean;
21+
readOnly?: boolean;
22+
}>
23+
>;
24+
25+
const Row = ({
26+
value,
27+
label,
28+
checked,
29+
description,
30+
disabled,
31+
readOnly,
32+
onChange,
33+
}: Omit<RadioButtonProps, 'name'> & { label: string }) => {
34+
const ref = useRef<HTMLInputElement>(null);
35+
return (
36+
<tr className={clsx(styles.row, checked && styles['row--selected'])}>
37+
<td className={styles.cell}>
38+
<label aria-label={`Select ${label}`}>
39+
<RadioButton
40+
value={value}
41+
name="quantity"
42+
checked={checked}
43+
disabled={disabled}
44+
readOnly={readOnly}
45+
onChange={onChange}
46+
ref={ref}
47+
/>
48+
</label>
49+
</td>
50+
<td className={styles.cell}>
51+
<a href="#">{label}</a>
52+
</td>
53+
<td className={styles.cell}>{description}</td>
54+
</tr>
55+
);
56+
};
57+
58+
export default function RadioButtonsTablePage() {
59+
const { urlParams, setUrlParams } = useContext(AppContext as RadioButtonDemoContext);
60+
61+
const [value, setValue] = useState<string>('');
62+
63+
return (
64+
<article>
65+
<h1>Radio button — custom table selection</h1>
66+
<Box margin={{ horizontal: 'm' }}>
67+
<SpaceBetween size="s">
68+
<FormField label="Options">
69+
<SpaceBetween size="s">
70+
<Checkbox
71+
checked={!!urlParams.disabled}
72+
onChange={({ detail }) => setUrlParams({ ...urlParams, disabled: detail.checked })}
73+
description="Make one of the radio buttons disabled"
74+
>
75+
Disabled
76+
</Checkbox>
77+
78+
<Checkbox
79+
checked={!!urlParams.readOnly}
80+
onChange={({ detail }) => setUrlParams({ ...urlParams, readOnly: detail.checked })}
81+
description="Make one of the radio buttons read-only"
82+
>
83+
Read-only
84+
</Checkbox>
85+
</SpaceBetween>
86+
</FormField>
87+
</SpaceBetween>
88+
</Box>
89+
<hr />
90+
<ScreenshotArea disableAnimations={true}>
91+
<table className={styles.table}>
92+
<thead className={styles.head}>
93+
<tr className={styles.row}>
94+
<th className={styles['header-cell']}></th>
95+
<th className={styles['header-cell']}>Name</th>
96+
<th className={styles['header-cell']}>Description</th>
97+
</tr>
98+
</thead>
99+
<tbody>
100+
{options.map((option, index) => (
101+
<Row
102+
key={index}
103+
value={option.value}
104+
label={option.label}
105+
description={option.description}
106+
checked={option.value === value}
107+
onChange={({ detail }) => {
108+
if (detail.checked) {
109+
setValue(option.value);
110+
}
111+
}}
112+
disabled={option.allowDisabled && urlParams.disabled}
113+
readOnly={option.allowReadOnly && urlParams.readOnly}
114+
>
115+
{option.label}
116+
</Row>
117+
))}
118+
</tbody>
119+
</table>
120+
<Box display={value === 'mail' ? 'block' : 'none'} margin={{ top: 'm' }}>
121+
<ExtraOptions />
122+
</Box>
123+
</ScreenshotArea>
124+
</article>
125+
);
126+
}

pages/radio-button/table.scss

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
@use '~design-tokens' as awsui;
7+
8+
.table {
9+
border-collapse: collapse;
10+
}
11+
12+
.head {
13+
background-color: awsui.$color-background-cell-shaded;
14+
}
15+
16+
.cell,
17+
.header-cell {
18+
padding-inline: awsui.$space-scaled-s;
19+
}
20+
21+
.cell {
22+
padding-block: awsui.$space-scaled-xs;
23+
}
24+
25+
.header-cell,
26+
.row {
27+
border-block-style: solid;
28+
border-inline-style: solid;
29+
border-block-width: 1px;
30+
border-inline-width: 1px;
31+
border-color: awsui.$color-border-divider-default;
32+
}
33+
34+
.header-cell {
35+
padding-block: awsui.$space-scaled-xxs;
36+
text-align: start;
37+
}
38+
39+
.row {
40+
&--selected {
41+
outline: 1px solid awsui.$color-border-item-focused;
42+
}
43+
}

0 commit comments

Comments
 (0)