Skip to content

Commit cfeb5f2

Browse files
authored
feat(closing-button): add brand and inverted color #6 (#519)
* feat(closing-button): add brand and inverted color #6 * fix(closing-button): inherit icon color, fix primary focus color #6 * fix(closing-button): improve prop description #6
1 parent 43826ae commit cfeb5f2

4 files changed

Lines changed: 148 additions & 43 deletions

File tree

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,12 @@
11
.tedi-closing-button {
2-
--general-icon-primary: var(--button-close-text-default);
3-
42
display: flex;
53
align-items: center;
64
justify-content: center;
75
padding: 0;
86
cursor: pointer;
9-
background-color: var(--button-close-background-default);
10-
border: 1px solid var(--button-close-background-default);
7+
border: 0;
118
border-radius: var(--button-radius-default);
12-
transition: background 0.5s ease;
13-
14-
@each $state in hover, active {
15-
&:#{ $state } {
16-
--general-icon-primary: var(--button-close-text-#{$state});
17-
18-
background-color: var(--button-close-background-#{$state});
19-
}
20-
}
21-
22-
&:focus-visible {
23-
outline: 2px solid var(--button-main-primary-border-focus);
24-
outline-offset: -2px;
25-
}
9+
transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
2610

2711
&--default {
2812
width: var(--button-sm-icon-size);
@@ -33,4 +17,62 @@
3317
width: var(--button-xs-icon-size);
3418
height: var(--button-xs-icon-size);
3519
}
20+
21+
&--color-primary {
22+
color: var(--button-close-text-default);
23+
background-color: var(--button-close-background-default);
24+
25+
&:hover {
26+
color: var(--button-close-text-hover);
27+
background-color: var(--button-close-background-hover);
28+
}
29+
30+
&:active {
31+
color: var(--button-close-text-active);
32+
background-color: var(--button-close-background-active);
33+
}
34+
35+
&:focus-visible {
36+
color: var(--button-close-text-focus);
37+
outline: 2px solid var(--button-main-primary-border-focus);
38+
}
39+
}
40+
41+
&--color-brand {
42+
color: var(--button-main-neutral-text-default);
43+
background-color: var(--button-main-neutral-icon-only-background-default);
44+
45+
&:hover {
46+
color: var(--button-main-neutral-text-hover);
47+
background-color: var(--button-main-neutral-icon-only-background-hover);
48+
}
49+
50+
&:active {
51+
color: var(--button-main-neutral-text-active);
52+
background-color: var(--button-main-neutral-icon-only-background-active);
53+
}
54+
55+
&:focus-visible {
56+
outline: 2px solid var(--button-main-primary-border-focus);
57+
}
58+
}
59+
60+
&--color-white {
61+
color: var(--button-neutral-inverted-text-default);
62+
background-color: var(--button-neutral-inverted-icon-only-background-default);
63+
64+
&:hover {
65+
color: var(--button-neutral-inverted-text-hover);
66+
background-color: var(--button-main-neutral-inverted-icon-only-background-hover);
67+
}
68+
69+
&:active {
70+
color: var(--button-neutral-inverted-text-active);
71+
background-color: var(--button-main-neutral-inverted-icon-only-background-hover);
72+
}
73+
74+
&:focus-visible {
75+
outline: 2px solid var(--button-main-neutral-inverted-text-focus);
76+
}
77+
}
3678
}

src/tedi/components/buttons/closing-button/closing-button.spec.tsx

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,33 +10,35 @@ jest.mock('../../../providers/label-provider', () => ({
1010

1111
describe('ClosingButton component', () => {
1212
it('renders the ClosingButton with default props', () => {
13-
render(
14-
<ClosingButton
15-
onClick={() => {
16-
console.log('Button pressed');
17-
}}
18-
/>
19-
);
13+
render(<ClosingButton />);
2014

2115
const button = screen.getByRole('button', { name: /close/i });
2216
expect(button).toBeInTheDocument();
23-
expect(button).toHaveClass('tedi-closing-button');
24-
expect(button).toHaveClass('tedi-closing-button--default');
17+
expect(button).toHaveClass('tedi-closing-button tedi-closing-button--default tedi-closing-button--color-primary');
2518

2619
const icon = button.querySelector('span[data-name="icon"]');
2720
expect(icon).toBeInTheDocument();
2821
expect(icon).toHaveClass('tedi-icon--size-24');
2922
});
3023

31-
it('renders with small size when size="small"', () => {
24+
it('renders with the correct small size class and icon size', () => {
3225
render(<ClosingButton size="small" />);
3326

3427
const button = screen.getByRole('button', { name: /close/i });
28+
expect(button).toHaveClass('tedi-closing-button tedi-closing-button--small tedi-closing-button--color-primary');
29+
render(
30+
<ClosingButton
31+
onClick={() => {
32+
console.log('Button pressed');
33+
}}
34+
/>
35+
);
36+
37+
expect(button).toBeInTheDocument();
38+
expect(button).toHaveClass('tedi-closing-button');
3539
expect(button).toHaveClass('tedi-closing-button--small');
3640

3741
const icon = button.querySelector('span[data-name="icon"]');
38-
expect(icon).toBeInTheDocument();
39-
// Small button still uses default icon size
4042
expect(icon).toHaveClass('tedi-icon--size-24');
4143
});
4244

@@ -54,25 +56,29 @@ describe('ClosingButton component', () => {
5456
it('applies custom class names', () => {
5557
render(<ClosingButton className="custom-class" />);
5658

57-
const button = screen.getByRole('button', { name: /Close/i });
58-
expect(button).toHaveClass('tedi-closing-button');
59-
expect(button).toHaveClass('tedi-closing-button--default');
60-
expect(button).toHaveClass('custom-class');
59+
const button = screen.getByRole('button', { name: /close/i });
60+
expect(button).toHaveClass(
61+
'tedi-closing-button tedi-closing-button--default tedi-closing-button--color-primary custom-class'
62+
);
63+
const customButton = screen.getByRole('button', { name: /Close/i });
64+
expect(customButton).toHaveClass('tedi-closing-button');
65+
expect(customButton).toHaveClass('tedi-closing-button--default');
66+
expect(customButton).toHaveClass('custom-class');
6167
});
6268

6369
it('triggers onClick handler when clicked', () => {
6470
const onClickMock = jest.fn();
6571
render(<ClosingButton onClick={onClickMock} />);
6672

67-
const button = screen.getByRole('button', { name: /Close/i });
73+
const button = screen.getByRole('button', { name: /close/i });
6874
fireEvent.click(button);
6975
expect(onClickMock).toHaveBeenCalledTimes(1);
7076
});
7177

7278
it('uses fallback label from label provider when title is not provided', () => {
7379
render(<ClosingButton />);
7480

75-
const button = screen.getByRole('button', { name: /Close/i });
81+
const button = screen.getByRole('button', { name: /close/i });
7682
expect(button).toHaveAttribute('title', 'Close');
7783
expect(button).toHaveAttribute('aria-label', 'Close');
7884
});
@@ -84,4 +90,14 @@ describe('ClosingButton component', () => {
8490
expect(button).toHaveAttribute('title', 'Custom Close');
8591
expect(button).toHaveAttribute('aria-label', 'Custom Close');
8692
});
93+
94+
it('applies the correct color classes', () => {
95+
const colors: Array<'primary' | 'brand' | 'white'> = ['primary', 'brand', 'white'];
96+
97+
colors.forEach((color) => {
98+
const { container } = render(<ClosingButton color={color} />);
99+
const button = container.querySelector('button[data-name="closing-button"]');
100+
expect(button).toHaveClass(`tedi-closing-button--color-${color}`);
101+
});
102+
});
87103
});

src/tedi/components/buttons/closing-button/closing-button.stories.tsx

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Meta, StoryFn, StoryObj } from '@storybook/react';
22

3+
import { Text } from '../../base/typography/text/text';
34
import { Col, Row } from '../../layout/grid';
45
import ClosingButton, { ClosingButtonProps } from './closing-button';
56

@@ -42,16 +43,18 @@ const SizeTemplate: StoryFn = () => {
4243

4344
const stateArray = ['Default', 'Hover', 'Active', 'Focus'];
4445

45-
const StatesTemplate: StoryFn = () => {
46+
const StatesTemplate: StoryFn = (args) => {
4647
return (
4748
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px', maxWidth: '200px' }}>
4849
{stateArray.map((state) => (
4950
<Row key={state}>
5051
<Col>
51-
<b>{state}</b>
52+
<Text color={args.color === 'white' ? 'white' : 'primary'} modifiers="bold">
53+
{state}
54+
</Text>
5255
</Col>
5356
<Col>
54-
<ClosingButton id={state} />
57+
<ClosingButton id={state} {...args} />
5558
</Col>
5659
</Row>
5760
))}
@@ -116,3 +119,32 @@ export const States: Story = {
116119
},
117120
},
118121
};
122+
123+
export const ColorBrand: Story = {
124+
render: StatesTemplate,
125+
args: {
126+
color: 'brand',
127+
},
128+
parameters: {
129+
pseudo: {
130+
hover: '#Hover',
131+
active: '#Active',
132+
focusVisible: '#Focus',
133+
},
134+
},
135+
};
136+
137+
export const ColorInverted: Story = {
138+
render: StatesTemplate,
139+
args: {
140+
color: 'white',
141+
},
142+
parameters: {
143+
pseudo: {
144+
hover: '#Hover',
145+
active: '#Active',
146+
focusVisible: '#Focus',
147+
},
148+
backgrounds: { default: 'brand' },
149+
},
150+
};

src/tedi/components/buttons/closing-button/closing-button.tsx

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useLabels } from '../../../providers/label-provider';
66
import { Icon } from '../../base/icon/icon';
77
import styles from './closing-button.module.scss';
88

9+
type ClosingButtonColor = 'primary' | 'brand' | 'white';
910
export type ClosingButtonSize = 'default' | 'small';
1011
export type ClosingButtonIconSize = 18 | 24;
1112

@@ -16,7 +17,7 @@ export interface ClosingButtonProps extends ButtonHTMLAttributes<HTMLButtonEleme
1617
className?: string;
1718
/**
1819
* Size of the ClosingButton
19-
* @default 'default'
20+
* @default default
2021
*/
2122
size?: ClosingButtonSize;
2223
/**
@@ -29,7 +30,11 @@ export interface ClosingButtonProps extends ButtonHTMLAttributes<HTMLButtonEleme
2930
*/
3031
title?: string;
3132
/*
32-
* Size of the icon inside the button
33+
* Color variant of the ClosingButton
34+
* @default default
35+
*/
36+
color?: ClosingButtonColor;
37+
/* Size of the icon inside the button
3338
* @default 24
3439
* If iconSize is set to 18, the button size will automatically adjust to 'small'.
3540
*/
@@ -38,13 +43,23 @@ export interface ClosingButtonProps extends ButtonHTMLAttributes<HTMLButtonEleme
3843

3944
export const ClosingButton = (props: ClosingButtonProps): JSX.Element => {
4045
const { getLabel } = useLabels();
41-
const { title = getLabel('close'), onClick, size = 'default', iconSize = 24, className, ...rest } = props;
46+
const {
47+
title = getLabel('close'),
48+
onClick,
49+
size = 'default',
50+
iconSize = 24,
51+
color = 'primary',
52+
className,
53+
...rest
54+
} = props;
4255

4356
const resolvedSize: ClosingButtonSize = iconSize === 18 ? 'small' : size;
4457

4558
const buttonClass = cn(
4659
styles['tedi-closing-button'],
4760
{
61+
[styles[`tedi-closing-button--${size}`]]: size,
62+
[styles[`tedi-closing-button--color-${color}`]]: color,
4863
[styles[`tedi-closing-button--${resolvedSize}`]]: resolvedSize,
4964
},
5065
className
@@ -62,7 +77,7 @@ export const ClosingButton = (props: ClosingButtonProps): JSX.Element => {
6277
title={title}
6378
aria-label={title}
6479
>
65-
<Icon name="close" size={resolvedIconSize} />
80+
<Icon name="close" size={resolvedIconSize} color="inherit" />
6681
</button>
6782
);
6883
};

0 commit comments

Comments
 (0)