Skip to content

Commit d6c0425

Browse files
authored
feat: Implementation of the Button component Style API. (#3540)
1 parent 3bf5819 commit d6c0425

File tree

13 files changed

+578
-19
lines changed

13 files changed

+578
-19
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@
164164
{
165165
"path": "lib/components/internal/widget-exports.js",
166166
"brotli": false,
167-
"limit": "790 kB",
167+
"limit": "800 kB",
168168
"ignore": "react-dom"
169169
}
170170
],

pages/app/themes/style-api.tsx

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
export const palette = {
4+
blue10: 'hsl(220, 95%, 95%)',
5+
blue20: 'hsl(220, 85%, 85%)',
6+
blue40: 'hsl(220, 70%, 70%)',
7+
blue60: 'hsl(220, 50%, 50%)',
8+
blue80: 'hsl(220, 95%, 30%)',
9+
blue90: 'hsl(220, 100%, 20%)',
10+
blue100: 'hsl(220, 100%, 15%)',
11+
body: 'rgb(13, 26, 38)',
12+
green10: 'hsl(130, 60%, 95%)',
13+
green20: 'hsl(130, 60%, 90%)',
14+
green30: 'hsl(130, 44%, 63%)',
15+
green60: 'hsl(130, 43%, 46%)',
16+
green80: 'hsl(130, 33%, 37%)',
17+
green90: 'hsl(130, 27%, 29%)',
18+
green100: 'hsl(130, 22%, 23%)',
19+
orange10: 'hsl(30, 75%, 95%)',
20+
orange20: 'hsl(30, 75%, 85%)',
21+
orange40: 'hsl(30, 75%, 75%)',
22+
orange60: 'hsl(30, 50%, 50%)',
23+
orange80: 'hsl(30, 95%, 30%)',
24+
orange90: 'hsl(30, 100%, 20%)',
25+
orange100: 'hsl(30, 100%, 15%)',
26+
neutral10: 'hsl(210, 5%, 98%)',
27+
neutral20: 'hsl(210, 5%, 94%)',
28+
neutral40: 'hsl(210, 5%, 87%)',
29+
neutral60: 'hsl(210, 10%, 58%)',
30+
neutral80: 'hsl(210, 10%, 40%)',
31+
neutral90: 'hsl(210, 25%, 25%)',
32+
neutral100: 'hsl(210, 50%, 10%)',
33+
red10: 'hsl(0, 75%, 95%)',
34+
red20: 'hsl(0, 75%, 85%)',
35+
red30: 'hsl(0, 75%, 75%)',
36+
red60: 'hsl(0, 50%, 50%)',
37+
red80: 'hsl(0, 95%, 30%)',
38+
red90: 'hsl(0, 100%, 20%)',
39+
red100: 'hsl(0, 100%, 15%)',
40+
teal10: 'hsl(190, 75%, 95%)',
41+
teal20: 'hsl(190, 75%, 85%)',
42+
teal30: 'hsl(190, 70%, 70%)',
43+
teal40: 'hsl(190, 70%, 70%)',
44+
teal60: 'hsl(190, 50%, 50%)',
45+
teal80: 'hsl(190, 95%, 30%)',
46+
teal90: 'rgb(0, 85, 102)',
47+
teal100: 'rgb(0, 64, 77)',
48+
white: 'rgb(255, 255, 255)',
49+
};
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
import React, { useRef } from 'react';
4+
5+
import { useCurrentMode } from '@cloudscape-design/component-toolkit/internal';
6+
7+
import { Button as CloudscapeButton, SpaceBetween } from '~components';
8+
9+
import { palette } from '../app/themes/style-api';
10+
import ScreenshotArea from '../utils/screenshot-area';
11+
12+
export default function CustomButtonTypes() {
13+
return (
14+
<ScreenshotArea>
15+
<h1>Custom Button Types</h1>
16+
17+
<SpaceBetween direction="horizontal" size="m">
18+
<CustomButton colorTheme="default" variation="primary" id="default">
19+
Default
20+
</CustomButton>
21+
<CustomButton colorTheme="success" variation="primary">
22+
Success
23+
</CustomButton>
24+
<CustomButton colorTheme="error" variation="primary">
25+
Error
26+
</CustomButton>
27+
<CustomButton colorTheme="info" variation="primary">
28+
Info
29+
</CustomButton>
30+
<CustomButton colorTheme="warning" variation="primary">
31+
Warning
32+
</CustomButton>
33+
<CustomButton colorTheme="success" variation="primary" isDisabled={true}>
34+
isDisabled
35+
</CustomButton>
36+
<CustomButton colorTheme="success" variation="primary" isLoading={true}>
37+
isLoading
38+
</CustomButton>
39+
</SpaceBetween>
40+
</ScreenshotArea>
41+
);
42+
}
43+
44+
interface CustomButtonProps {
45+
children?: React.ReactNode;
46+
colorTheme: 'default' | 'error' | 'info' | 'warning' | 'success';
47+
id?: string;
48+
isDisabled?: boolean;
49+
isLoading?: boolean;
50+
onClick?: any;
51+
variation: 'primary';
52+
}
53+
54+
function CustomButton({ children, colorTheme, id, isDisabled, isLoading, onClick, variation }: CustomButtonProps) {
55+
const mode = useCurrentMode(useRef(document.body));
56+
const background = backgrounds[mode][colorTheme];
57+
const color = isDisabled || isLoading ? colors[mode] : colors[mode];
58+
const focusRing = focusRings[mode];
59+
60+
return (
61+
<CloudscapeButton
62+
data-testid={id}
63+
disabled={isDisabled}
64+
loading={isLoading}
65+
onClick={onClick}
66+
variant={variation}
67+
style={{
68+
root: {
69+
background,
70+
borderRadius: '4px',
71+
borderWidth: '0px',
72+
color,
73+
focusRing,
74+
paddingBlock: '12px',
75+
paddingInline: '16px',
76+
},
77+
}}
78+
>
79+
<span style={{ fontSize: '16px' }}>{children}</span>
80+
</CloudscapeButton>
81+
);
82+
}
83+
84+
const backgrounds = {
85+
light: {
86+
default: {
87+
active: palette.teal100,
88+
default: palette.teal80,
89+
disabled: palette.neutral40,
90+
hover: palette.teal90,
91+
},
92+
error: {
93+
active: palette.red100,
94+
default: palette.red80,
95+
disabled: palette.neutral40,
96+
hover: palette.red90,
97+
},
98+
info: {
99+
active: palette.blue100,
100+
default: palette.blue80,
101+
disabled: palette.neutral40,
102+
hover: palette.blue90,
103+
},
104+
success: {
105+
active: palette.green100,
106+
default: palette.green80,
107+
disabled: palette.neutral40,
108+
hover: palette.green90,
109+
},
110+
warning: {
111+
active: palette.orange100,
112+
default: palette.orange80,
113+
disabled: palette.neutral40,
114+
hover: palette.orange90,
115+
},
116+
},
117+
dark: {
118+
default: {
119+
active: palette.teal10,
120+
default: palette.teal30,
121+
disabled: palette.neutral80,
122+
hover: palette.teal20,
123+
},
124+
error: {
125+
active: palette.red10,
126+
default: palette.red30,
127+
disabled: palette.neutral80,
128+
hover: palette.red20,
129+
},
130+
info: {
131+
active: palette.blue10,
132+
default: palette.blue40,
133+
disabled: palette.neutral80,
134+
hover: palette.blue20,
135+
},
136+
success: {
137+
active: palette.green10,
138+
default: palette.green30,
139+
disabled: palette.neutral80,
140+
hover: palette.green20,
141+
},
142+
warning: {
143+
active: palette.orange10,
144+
default: palette.orange40,
145+
disabled: palette.neutral80,
146+
hover: palette.orange20,
147+
},
148+
},
149+
};
150+
151+
const colors = {
152+
light: {
153+
active: palette.neutral10,
154+
default: palette.neutral10,
155+
hover: palette.neutral10,
156+
disabled: palette.neutral60,
157+
},
158+
dark: {
159+
active: palette.neutral100,
160+
default: palette.neutral100,
161+
hover: palette.neutral100,
162+
disabled: palette.neutral60,
163+
},
164+
};
165+
166+
const focusRings = {
167+
light: {
168+
borderColor: 'rgb(0, 64, 77)',
169+
borderWidth: '2px',
170+
},
171+
dark: {
172+
borderColor: 'rgb(233, 249, 252)',
173+
borderWidth: '2px',
174+
},
175+
};
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
import React from 'react';
4+
5+
import Button, { ButtonProps } from '~components/button';
6+
7+
import createPermutations from '../utils/permutations';
8+
import PermutationsView from '../utils/permutations-view';
9+
import ScreenshotArea from '../utils/screenshot-area';
10+
11+
const permutations = createPermutations<ButtonProps>([
12+
{
13+
ariaLabel: ['Border Styles'],
14+
children: ['Border Styles'],
15+
disabled: [false, true],
16+
iconName: ['add-plus'],
17+
iconAlt: ['add-plus'],
18+
style: [
19+
{
20+
root: {
21+
borderColor: {
22+
active: 'purple',
23+
default: 'magenta',
24+
hover: 'orange',
25+
disabled: 'brown',
26+
},
27+
borderRadius: '2px',
28+
borderWidth: '4px',
29+
},
30+
},
31+
],
32+
variant: ['primary', 'normal', 'link', 'icon', 'inline-icon', 'inline-link'],
33+
},
34+
{
35+
ariaLabel: ['Background and Color Styles'],
36+
children: ['Background and Color Styles'],
37+
disabled: [false, true],
38+
iconName: ['arrow-left'],
39+
iconAlt: ['arrow-left'],
40+
loading: [false, true],
41+
style: [
42+
{
43+
root: {
44+
background: {
45+
active: 'brown',
46+
default: 'purple',
47+
hover: 'brown',
48+
disabled: '#ccc',
49+
},
50+
borderWidth: '0px',
51+
color: {
52+
active: 'white',
53+
default: 'white',
54+
hover: 'white',
55+
disabled: 'black',
56+
},
57+
},
58+
},
59+
],
60+
variant: ['primary', 'normal', 'link', 'icon', 'inline-icon', 'inline-link'],
61+
},
62+
{
63+
ariaLabel: ['Padding Styles'],
64+
children: ['Padding Styles'],
65+
disabled: [false, true],
66+
iconName: ['delete-marker'],
67+
iconAlt: ['delete-marker'],
68+
iconAlign: ['left', 'right'],
69+
loading: [false, true],
70+
fullWidth: [false, true],
71+
style: [
72+
{
73+
root: {
74+
borderColor: {
75+
active: 'black',
76+
default: 'black',
77+
disabled: 'black',
78+
hover: 'black',
79+
},
80+
borderWidth: '2px',
81+
borderRadius: '1px',
82+
paddingBlock: '22px',
83+
paddingInline: '44px',
84+
},
85+
},
86+
],
87+
variant: ['primary', 'normal', 'link', 'icon', 'inline-icon', 'inline-link'],
88+
},
89+
]);
90+
91+
export default function ButtonStylePermutations() {
92+
return (
93+
<>
94+
<h1>Button Style permutations</h1>
95+
<ScreenshotArea disableAnimations={true}>
96+
<PermutationsView permutations={permutations} render={permutation => <Button {...permutation} />} />
97+
</ScreenshotArea>
98+
</>
99+
);
100+
}

src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4222,6 +4222,25 @@ If the \`rel\` property is provided, it overrides the default behavior.",
42224222
"optional": true,
42234223
"type": "string",
42244224
},
4225+
{
4226+
"inlineType": {
4227+
"name": "ButtonProps.Style",
4228+
"properties": [
4229+
{
4230+
"name": "root",
4231+
"optional": true,
4232+
"type": "{ background?: { active?: string | undefined; default?: string | undefined; disabled?: string | undefined; hover?: string | undefined; } | undefined; borderColor?: { active?: string | undefined; default?: string | undefined; disabled?: string | undefined; hover?: string | undefined; } | undefined; ... 5 more ...; pa...",
4233+
},
4234+
],
4235+
"type": "object",
4236+
},
4237+
"name": "style",
4238+
"optional": true,
4239+
"systemTags": [
4240+
"core",
4241+
],
4242+
"type": "ButtonProps.Style",
4243+
},
42254244
{
42264245
"description": "Specifies where to open the linked URL (for example, to open in a new browser window or tab use \`_blank\`).
42274246
This property only applies when an \`href\` is provided.",
@@ -20076,6 +20095,25 @@ It prevents users from clicking the button, but it can still be focused.",
2007620095
"optional": true,
2007720096
"type": "string",
2007820097
},
20098+
{
20099+
"inlineType": {
20100+
"name": "ButtonProps.Style",
20101+
"properties": [
20102+
{
20103+
"name": "root",
20104+
"optional": true,
20105+
"type": "{ background?: { active?: string | undefined; default?: string | undefined; disabled?: string | undefined; hover?: string | undefined; } | undefined; borderColor?: { active?: string | undefined; default?: string | undefined; disabled?: string | undefined; hover?: string | undefined; } | undefined; ... 5 more ...; pa...",
20106+
},
20107+
],
20108+
"type": "object",
20109+
},
20110+
"name": "style",
20111+
"optional": true,
20112+
"systemTags": [
20113+
"core",
20114+
],
20115+
"type": "ButtonProps.Style",
20116+
},
2007920117
{
2008020118
"defaultValue": "'normal'",
2008120119
"description": "Determines the general styling of the toggle button as follows:

0 commit comments

Comments
 (0)