Skip to content

Commit 1446c8c

Browse files
authored
feat: Implementation of the Badge component Style API (#3677)
1 parent 3056b94 commit 1446c8c

File tree

9 files changed

+253
-43
lines changed

9 files changed

+253
-43
lines changed

pages/badge.page.tsx

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,3 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
3-
import * as React from 'react';
4-
5-
import Badge, { BadgeProps } from '~components/badge';
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<BadgeProps>([
12-
{
13-
color: [
14-
'blue',
15-
'grey',
16-
'green',
17-
'red',
18-
'severity-critical',
19-
'severity-high',
20-
'severity-medium',
21-
'severity-low',
22-
'severity-neutral',
23-
],
24-
children: [
25-
'ABC',
26-
'Badge With A Very Long Text',
27-
<>
28-
Badge with <strong>html</strong>
29-
</>,
30-
],
31-
},
32-
]);
33-
34-
export default function BadgePermutations() {
35-
return (
36-
<>
37-
<h1>Badge permutations</h1>
38-
<ScreenshotArea disableAnimations={true}>
39-
<PermutationsView permutations={permutations} render={permutation => <Badge {...permutation} />} />
40-
</ScreenshotArea>
41-
</>
42-
);
43-
}
3+
export { default } from './badge/permutations.page';

pages/badge/permutations.page.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
import * as React from 'react';
4+
5+
import Badge, { BadgeProps } from '~components/badge';
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<BadgeProps>([
12+
{
13+
color: [
14+
'blue',
15+
'grey',
16+
'green',
17+
'red',
18+
'severity-critical',
19+
'severity-high',
20+
'severity-medium',
21+
'severity-low',
22+
'severity-neutral',
23+
],
24+
children: [
25+
'ABC',
26+
'Badge With A Very Long Text',
27+
<>
28+
Badge with <strong>html</strong>
29+
</>,
30+
],
31+
},
32+
]);
33+
34+
export default function BadgePermutations() {
35+
return (
36+
<>
37+
<h1>Badge permutations</h1>
38+
<ScreenshotArea disableAnimations={true}>
39+
<PermutationsView permutations={permutations} render={permutation => <Badge {...permutation} />} />
40+
</ScreenshotArea>
41+
</>
42+
);
43+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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 { Badge as CloudscapeBadge, SpaceBetween } from '~components';
8+
9+
import { palette } from '../app/themes/style-api';
10+
import ScreenshotArea from '../utils/screenshot-area';
11+
12+
export default function CustomBadgeTypes() {
13+
return (
14+
<ScreenshotArea>
15+
<h1>Custom Badge Types</h1>
16+
<SpaceBetween direction="horizontal" size="m">
17+
<CustomBadge colorTheme="blue">Blue</CustomBadge>
18+
<CustomBadge colorTheme="critical">Critical</CustomBadge>
19+
<CustomBadge colorTheme="high">High</CustomBadge>
20+
<CustomBadge colorTheme="medium">Medium</CustomBadge>
21+
<CustomBadge colorTheme="low">Low</CustomBadge>
22+
<CustomBadge colorTheme="neutral">Neutral</CustomBadge>
23+
</SpaceBetween>
24+
</ScreenshotArea>
25+
);
26+
}
27+
28+
interface CustomBadgeProps {
29+
children?: React.ReactNode;
30+
colorTheme: 'blue' | 'critical' | 'high' | 'medium' | 'low' | 'neutral';
31+
}
32+
33+
function CustomBadge({ children, colorTheme }: CustomBadgeProps) {
34+
const mode = useCurrentMode(useRef(document.body));
35+
const background = backgrounds[mode][colorTheme];
36+
const borderColor = borderColors[mode][colorTheme];
37+
const borderWidth = borderWidths[colorTheme];
38+
const color = colors[mode];
39+
return (
40+
<CloudscapeBadge
41+
style={{
42+
root: {
43+
background,
44+
borderColor,
45+
borderRadius: '8px',
46+
color,
47+
borderWidth,
48+
paddingBlock: '8px',
49+
paddingInline: '12px',
50+
},
51+
}}
52+
>
53+
<span style={{ fontSize: '16px' }}>{children}</span>
54+
</CloudscapeBadge>
55+
);
56+
}
57+
58+
const backgrounds = {
59+
light: {
60+
blue: palette.blue80,
61+
critical: palette.red80,
62+
high: palette.red60,
63+
medium: palette.green80,
64+
low: palette.teal90,
65+
neutral: palette.neutral80,
66+
},
67+
dark: {
68+
blue: palette.blue40,
69+
critical: palette.red30,
70+
high: palette.red30,
71+
medium: palette.green20,
72+
low: palette.teal20,
73+
neutral: palette.neutral20,
74+
},
75+
};
76+
77+
const colors = {
78+
light: palette.neutral10,
79+
dark: palette.neutral100,
80+
};
81+
82+
const borderColors = {
83+
light: {
84+
blue: palette.neutral80,
85+
critical: palette.blue90,
86+
high: palette.red80,
87+
medium: palette.green80,
88+
low: palette.neutral80,
89+
neutral: palette.teal80,
90+
},
91+
dark: {
92+
blue: palette.neutral20,
93+
critical: palette.red60,
94+
high: palette.red10,
95+
medium: palette.green30,
96+
low: palette.neutral20,
97+
neutral: palette.teal40,
98+
},
99+
};
100+
101+
const borderWidths = {
102+
blue: '2px',
103+
critical: '4px',
104+
high: '0px',
105+
medium: '0px',
106+
low: '0px',
107+
neutral: '0px',
108+
};

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2907,6 +2907,23 @@ use the \`id\` attribute, consider setting it on a parent element instead.",
29072907
"optional": true,
29082908
"type": "string",
29092909
},
2910+
{
2911+
"description": "Specifies an object of selectors and properties that are used to apply custom styles.
2912+
2913+
- \`root.background\` (string) - (Optional) Background for badge.
2914+
- \`root.borderColor\` (string) - (Optional) Border color for badge.
2915+
- \`root.borderRadius\` (string) - (Optional) Border radius style.
2916+
- \`root.borderWidth\` (string) - (Optional) Border width style.
2917+
- \`root.color\` (string) - (Optional) Text color for badge.
2918+
- \`root.paddingBlock\` (string) - (Optional) Block dimension padding.
2919+
- \`root.paddingInline\` (string) - (Optional) Inline dimension padding.",
2920+
"name": "style",
2921+
"optional": true,
2922+
"systemTags": [
2923+
"core",
2924+
],
2925+
"type": "BadgeProps.Style",
2926+
},
29102927
],
29112928
"regions": [
29122929
{

src/badge/__tests__/badge.test.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,31 @@ describe('Badge', () => {
5757
expect(badge).toHaveClass(styles[`badge-color-grey`]);
5858
});
5959
});
60+
61+
describe('Style API', () => {
62+
test('all style properties', () => {
63+
const badge = renderBadge(
64+
<Badge
65+
style={{
66+
root: {
67+
background: 'rgb(255, 255, 255)',
68+
borderColor: 'rgb(0, 0, 0)',
69+
borderRadius: '8px',
70+
borderWidth: '2px',
71+
paddingBlock: '4px',
72+
paddingInline: '8px',
73+
},
74+
}}
75+
>
76+
Badge
77+
</Badge>
78+
);
79+
80+
expect(getComputedStyle(badge).getPropertyValue('background')).toBe('rgb(255, 255, 255)');
81+
expect(getComputedStyle(badge).getPropertyValue('border-color')).toBe('rgb(0, 0, 0)');
82+
expect(getComputedStyle(badge).getPropertyValue('border-radius')).toBe('8px');
83+
expect(getComputedStyle(badge).getPropertyValue('border-width')).toBe('2px');
84+
expect(getComputedStyle(badge).getPropertyValue('padding-block')).toBe('4px');
85+
expect(getComputedStyle(badge).getPropertyValue('padding-inline')).toBe('8px');
86+
});
87+
});

src/badge/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,20 @@ import { getBaseProps } from '../internal/base-component';
88
import useBaseComponent from '../internal/hooks/use-base-component';
99
import { applyDisplayName } from '../internal/utils/apply-display-name';
1010
import { BadgeProps } from './interfaces';
11+
import { getBadgeStyles } from './style';
1112

1213
import styles from './styles.css.js';
1314

1415
export { BadgeProps };
1516

16-
export default function Badge({ color = 'grey', children, ...rest }: BadgeProps) {
17+
export default function Badge({ color = 'grey', children, style, ...rest }: BadgeProps) {
1718
const { __internalRootRef } = useBaseComponent('Badge', { props: { color } });
1819
const baseProps = getBaseProps(rest);
1920

2021
const className = clsx(baseProps.className, styles.badge, styles[`badge-color-${color}`]);
2122

2223
return (
23-
<span {...baseProps} {...{ className }} ref={__internalRootRef}>
24+
<span {...baseProps} {...{ className }} ref={__internalRootRef} style={getBadgeStyles(style)}>
2425
{children}
2526
</span>
2627
);

src/badge/interfaces.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,32 @@ export interface BadgeProps extends BaseComponentProps {
2323
* Text displayed inside the badge.
2424
*/
2525
children?: React.ReactNode;
26+
27+
/**
28+
* Specifies an object of selectors and properties that are used to apply custom styles.
29+
*
30+
* - `root.background` (string) - (Optional) Background for badge.
31+
* - `root.borderColor` (string) - (Optional) Border color for badge.
32+
* - `root.borderRadius` (string) - (Optional) Border radius style.
33+
* - `root.borderWidth` (string) - (Optional) Border width style.
34+
* - `root.color` (string) - (Optional) Text color for badge.
35+
* - `root.paddingBlock` (string) - (Optional) Block dimension padding.
36+
* - `root.paddingInline` (string) - (Optional) Inline dimension padding.
37+
* @awsuiSystem core
38+
*/
39+
style?: BadgeProps.Style;
40+
}
41+
42+
export namespace BadgeProps {
43+
export interface Style {
44+
root?: {
45+
background?: string;
46+
borderColor?: string;
47+
borderRadius?: string;
48+
borderWidth?: string;
49+
color?: string;
50+
paddingBlock?: string;
51+
paddingInline?: string;
52+
};
53+
}
2654
}

src/badge/style.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 { SYSTEM } from '../internal/environment';
4+
import { BadgeProps } from './interfaces';
5+
6+
export function getBadgeStyles(style: BadgeProps['style']) {
7+
let properties = {};
8+
9+
if (style?.root && SYSTEM === 'core') {
10+
properties = {
11+
background: style.root.background,
12+
borderColor: style.root.borderColor,
13+
borderRadius: style.root.borderRadius,
14+
borderWidth: style.root.borderWidth,
15+
color: style.root.color,
16+
paddingBlock: style.root.paddingBlock,
17+
paddingInline: style.root.paddingInline,
18+
};
19+
}
20+
21+
return properties;
22+
}

src/badge/styles.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
border-start-end-radius: awsui.$border-radius-badge;
1717
border-end-start-radius: awsui.$border-radius-badge;
1818
border-end-end-radius: awsui.$border-radius-badge;
19+
border-block-style: solid;
20+
border-inline-style: solid;
21+
border-width: 0;
1922
padding-block: 0;
2023
padding-inline: awsui.$space-xs;
2124
color: awsui.$color-text-notification-default;

0 commit comments

Comments
 (0)