Skip to content

Commit 6b2c412

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

File tree

33 files changed

+1455
-211
lines changed

33 files changed

+1455
-211
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": "960 kB",
170170
"ignore": "react-dom"
171171
}
172172
],

pages/radio-button/cards.page.tsx

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
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+
label,
27+
checked,
28+
onChange,
29+
description,
30+
disabled,
31+
readOnly,
32+
}: Omit<RadioButtonProps, 'name'> & { label: string }) => {
33+
const ref = useRef<HTMLInputElement>(null);
34+
return (
35+
<li className={clsx(styles.card, checked && styles['card--selected'])}>
36+
<div>
37+
<h2 className={styles.heading}>
38+
<a href="#">{label}</a>
39+
</h2>
40+
<p className={styles.description}>{description}</p>
41+
</div>
42+
<label aria-label={`Select ${label}`}>
43+
<RadioButton
44+
name="quantity"
45+
checked={checked}
46+
disabled={disabled}
47+
readOnly={readOnly}
48+
onChange={onChange}
49+
ref={ref}
50+
/>
51+
</label>
52+
</li>
53+
);
54+
};
55+
56+
export default function RadioButtonsCardsPage() {
57+
const { urlParams, setUrlParams } = useContext(AppContext as RadioButtonDemoContext);
58+
59+
const [value, setValue] = useState<string>('');
60+
61+
return (
62+
<article>
63+
<h1>Radio button — custom card selection</h1>
64+
<Box margin={{ horizontal: 'm' }}>
65+
<SpaceBetween size="s">
66+
<FormField label="Options">
67+
<SpaceBetween size="s">
68+
<Checkbox
69+
checked={!!urlParams.disabled}
70+
onChange={({ detail }) => setUrlParams({ ...urlParams, disabled: detail.checked })}
71+
description="Make one of the radio buttons disabled"
72+
>
73+
Disabled
74+
</Checkbox>
75+
76+
<Checkbox
77+
checked={!!urlParams.readOnly}
78+
onChange={({ detail }) => setUrlParams({ ...urlParams, readOnly: detail.checked })}
79+
description="Make one of the radio buttons read-only"
80+
>
81+
Read-only
82+
</Checkbox>
83+
</SpaceBetween>
84+
</FormField>
85+
</SpaceBetween>
86+
</Box>
87+
<hr />
88+
<ScreenshotArea disableAnimations={true}>
89+
<ul className={styles.cards}>
90+
{options.map((option, index) => (
91+
<Card
92+
key={index}
93+
label={option.label}
94+
description={option.description}
95+
checked={option.value === value}
96+
onChange={({ detail }) => {
97+
if (detail.checked) {
98+
setValue(option.value);
99+
}
100+
}}
101+
disabled={option.allowDisabled && urlParams.disabled}
102+
readOnly={option.allowReadOnly && urlParams.readOnly}
103+
/>
104+
))}
105+
</ul>
106+
<Box display={value === 'mail' ? 'block' : 'none'} margin={{ top: 'm' }}>
107+
<ExtraOptions />
108+
</Box>
109+
</ScreenshotArea>
110+
</article>
111+
);
112+
}

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: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
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+
label,
27+
checked,
28+
description,
29+
disabled,
30+
readOnly,
31+
onChange,
32+
}: Omit<RadioButtonProps, 'name'> & { label: string }) => {
33+
const ref = useRef<HTMLInputElement>(null);
34+
return (
35+
<tr className={clsx(styles.row, checked && styles['row--selected'])}>
36+
<td className={styles.cell}>
37+
<label aria-label={`Select ${label}`}>
38+
<RadioButton
39+
name="quantity"
40+
checked={checked}
41+
disabled={disabled}
42+
readOnly={readOnly}
43+
onChange={onChange}
44+
ref={ref}
45+
/>
46+
</label>
47+
</td>
48+
<td className={styles.cell}>
49+
<a href="#">{label}</a>
50+
</td>
51+
<td className={styles.cell}>{description}</td>
52+
</tr>
53+
);
54+
};
55+
56+
export default function RadioButtonsTablePage() {
57+
const { urlParams, setUrlParams } = useContext(AppContext as RadioButtonDemoContext);
58+
59+
const [value, setValue] = useState<string>('');
60+
61+
return (
62+
<article>
63+
<h1>Radio button — custom table selection</h1>
64+
<Box margin={{ horizontal: 'm' }}>
65+
<SpaceBetween size="s">
66+
<FormField label="Options">
67+
<SpaceBetween size="s">
68+
<Checkbox
69+
checked={!!urlParams.disabled}
70+
onChange={({ detail }) => setUrlParams({ ...urlParams, disabled: detail.checked })}
71+
description="Make one of the radio buttons disabled"
72+
>
73+
Disabled
74+
</Checkbox>
75+
76+
<Checkbox
77+
checked={!!urlParams.readOnly}
78+
onChange={({ detail }) => setUrlParams({ ...urlParams, readOnly: detail.checked })}
79+
description="Make one of the radio buttons read-only"
80+
>
81+
Read-only
82+
</Checkbox>
83+
</SpaceBetween>
84+
</FormField>
85+
</SpaceBetween>
86+
</Box>
87+
<hr />
88+
<ScreenshotArea disableAnimations={true}>
89+
<table className={styles.table}>
90+
<thead className={styles.head}>
91+
<tr className={styles.row}>
92+
<th className={styles['header-cell']}></th>
93+
<th className={styles['header-cell']}>Name</th>
94+
<th className={styles['header-cell']}>Description</th>
95+
</tr>
96+
</thead>
97+
<tbody>
98+
{options.map((option, index) => (
99+
<Row
100+
key={index}
101+
label={option.label}
102+
description={option.description}
103+
checked={option.value === value}
104+
onChange={({ detail }) => {
105+
if (detail.checked) {
106+
setValue(option.value);
107+
}
108+
}}
109+
disabled={option.allowDisabled && urlParams.disabled}
110+
readOnly={option.allowReadOnly && urlParams.readOnly}
111+
>
112+
{option.label}
113+
</Row>
114+
))}
115+
</tbody>
116+
</table>
117+
<Box display={value === 'mail' ? 'block' : 'none'} margin={{ top: 'm' }}>
118+
<ExtraOptions />
119+
</Box>
120+
</ScreenshotArea>
121+
</article>
122+
);
123+
}

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)