Skip to content

Commit f08a52c

Browse files
committed
feat(Alert): add size props; add css-api; refactor component
1 parent 70790b9 commit f08a52c

34 files changed

+291
-64
lines changed

src/components/Alert/Alert.scss

+118
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,129 @@
33
$block: '.#{variables.$ns}alert';
44

55
#{$block} {
6+
--_--icon-indent: var(--g-spacing-2);
7+
--_--close-btn-indent: 2px;
8+
--_--title-font-weight: var(--g-text-subheader-font-weight);
9+
--_--message-font-weight: var(--g-text-body-font-weight);
10+
--_--message-font-size: var(--g-text-body-1-font-size);
11+
--_--message-line-height: var(--g-text-body-1-line-height);
12+
13+
padding: var(--g-alert-padding, var(--_--padding));
14+
--g-card-border-radius: var(--g-alert-border-radius, var(--_--border-radius));
15+
16+
display: flex;
17+
align-items: var(--_--align);
18+
19+
&_align_baseline {
20+
--_--align: baseline;
21+
22+
#{$block}__close-btn {
23+
align-self: flex-start;
24+
position: relative;
25+
inset-block-start: var(--_--close-btn-offset);
26+
inset-inline-end: var(--_--close-btn-offset);
27+
}
28+
}
29+
30+
&_align_center {
31+
--_--align: center;
32+
}
33+
634
&_corners_square {
735
--g-card-border-radius: 0;
836
}
937

38+
&_size {
39+
&_s {
40+
--_--padding: 11px var(--g-spacing-3);
41+
--_--border-radius: var(--g-border-radius-m);
42+
--_--text-content-gap: 0;
43+
--_--content-gap: var(--g-spacing-2);
44+
--_--close-btn-offset: -6px;
45+
--_--title-font-size: var(--g-text-subheader-1-font-size);
46+
--_--title-line-height: var(--g-text-subheader-1-line-height);
47+
}
48+
49+
&_m {
50+
--_--padding: var(--g-spacing-4) var(--g-spacing-5);
51+
--_--border-radius: var(--g-border-radius-l);
52+
--_--text-content-gap: var(--g-spacing-1);
53+
--_--content-gap: var(--g-spacing-3);
54+
--_--close-btn-offset: -4px;
55+
--_--title-font-size: var(--g-text-subheader-2-font-size);
56+
--_--title-line-height: var(--g-text-subheader-2-line-height);
57+
}
58+
59+
&_l {
60+
--_--padding: var(--g-spacing-6);
61+
--_--border-radius: 12px;
62+
--_--icon-indent: var(--g-spacing-3);
63+
--_--close-btn-indent: 6px;
64+
--_--text-content-gap: var(--g-spacing-2);
65+
--_--content-gap: var(--g-spacing-5);
66+
--_--close-btn-offset: -3px;
67+
--_--title-font-size: var(--g-text-subheader-3-font-size);
68+
--_--title-line-height: var(--g-text-subheader-3-line-height);
69+
--_--message-font-size: var(--g-text-body-2-font-size);
70+
--_--message-line-height: var(--g-text-body-2-line-height);
71+
}
72+
}
73+
74+
&__icon-wrapper {
75+
margin-inline-end: var(--g-alert-icon-indent, var(--_--icon-indent));
76+
}
77+
78+
&__icon {
79+
vertical-align: text-bottom;
80+
}
81+
82+
&__main {
83+
flex-grow: 1;
84+
display: flex;
85+
gap: var(--g-alert-content-gap, var(--_--content-gap));
86+
87+
&_layout_vertical {
88+
flex-direction: column;
89+
}
90+
91+
&_layout_horizontal {
92+
flex-direction: row;
93+
}
94+
}
95+
1096
&__text-content {
97+
display: flex;
98+
flex-direction: column;
1199
width: 100%;
100+
flex-grow: 1;
101+
102+
gap: var(--g-alert-text-content-gap, var(--_--text-content-gap));
103+
}
104+
105+
&__title {
106+
color: var(--g-alert-title-color, var(--g-color-text-primary));
107+
font-size: var(--g-alert-title-font-size, var(--_--title-font-size));
108+
font-weight: var(--g-alert-title-font-weight, var(--_--title-font-weight));
109+
line-height: var(--g-alert-title-line-height, var(--_--title-line-height));
110+
}
111+
112+
&__message {
113+
color: var(--g-alert-message-color, var(--g-color-text-complementary));
114+
font-size: var(--g-alert-message-font-size, var(--_--message-font-size));
115+
font-weight: var(--g-alert-message-font-weight, var(--_--message-font-weight));
116+
line-height: var(--g-alert-message-line-height, var(--_--message-line-height));
117+
}
118+
119+
&__actions {
120+
display: flex;
121+
align-items: flex-start;
122+
flex-wrap: wrap;
123+
124+
gap: var(--g-alert-actions-gap, var(--g-spacing-3));
125+
126+
&_center {
127+
align-items: center;
128+
}
12129
}
13130

14131
&__actions_minContent {
@@ -17,5 +134,6 @@ $block: '.#{variables.$ns}alert';
17134

18135
&__close-btn {
19136
flex-shrink: 0;
137+
margin-inline-start: var(--g-alert-close-btn-indent, var(--_--close-btn-indent));
20138
}
21139
}

src/components/Alert/Alert.tsx

+50-40
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {Button} from '../Button';
44
import {Card} from '../Card';
55
import {Icon} from '../Icon';
66
import {colorText} from '../Text';
7-
import {Flex, spacing} from '../layout';
87

98
import {AlertAction} from './AlertAction';
109
import {AlertActions} from './AlertActions';
@@ -13,69 +12,80 @@ import {AlertIcon} from './AlertIcon';
1312
import {AlertTitle} from './AlertTitle';
1413
import {DEFAULT_ICON_SIZE, bAlert} from './constants';
1514
import i18n from './i18n';
16-
import type {AlertProps} from './types';
15+
import type {AlertProps, AlertSize} from './types';
16+
17+
function alertSizeToIconSize(alertSize: AlertSize): number {
18+
switch (alertSize) {
19+
case 's':
20+
return 16;
21+
case 'm':
22+
return 18;
23+
case 'l':
24+
default:
25+
return 22;
26+
}
27+
}
1728

1829
export const Alert = (props: AlertProps) => {
1930
const {
2031
theme = 'normal',
2132
view = 'filled',
33+
size = 'm',
2234
layout = 'vertical',
2335
message,
2436
className,
2537
corners,
2638
style,
2739
onClose,
28-
align,
40+
align = 'baseline',
2941
qa,
3042
} = props;
3143

3244
return (
33-
<AlertContextProvider layout={layout} view={view}>
45+
<AlertContextProvider layout={layout} view={view} size={size}>
3446
<Card
3547
style={style}
36-
className={bAlert({corners}, spacing({py: 4, px: 5}, className))}
48+
className={bAlert({corners, size, align: align}, className)}
3749
theme={theme}
3850
view={view}
3951
qa={qa}
4052
>
41-
<Flex gap="3" alignItems={align}>
42-
{typeof props.icon === 'undefined' ? (
43-
<Alert.Icon theme={theme} view={view} />
44-
) : (
45-
props.icon // ability to pass `null` as `icon` prop value
46-
)}
47-
<Flex direction={layout === 'vertical' ? 'column' : 'row'} gap="5" grow>
48-
<Flex gap="2" grow className={bAlert('text-content')}>
49-
<Flex direction="column" gap="1" grow justifyContent={align}>
50-
{typeof props.title === 'string' ? (
51-
<Alert.Title text={props.title} />
52-
) : (
53-
props.title
54-
)}
55-
{message}
56-
</Flex>
57-
</Flex>
58-
{Array.isArray(props.actions) ? (
59-
<Alert.Actions items={props.actions} />
53+
{typeof props.icon === 'undefined' ? (
54+
<Alert.Icon theme={theme} view={view} size={alertSizeToIconSize(size)} />
55+
) : (
56+
props.icon && ( // ability to pass `null` as `icon` prop value
57+
<div className={bAlert('icon-wrapper')}>{props.icon}</div>
58+
)
59+
)}
60+
<div className={bAlert('main', {layout})}>
61+
<div className={bAlert('text-content')}>
62+
{typeof props.title === 'string' ? (
63+
<Alert.Title text={props.title} />
6064
) : (
61-
props.actions
65+
props.title
6266
)}
63-
</Flex>
64-
{onClose && (
65-
<Button
66-
view="flat"
67-
className={bAlert('close-btn')}
68-
onClick={onClose}
69-
aria-label={i18n('label_close')}
70-
>
71-
<Icon
72-
data={Xmark}
73-
size={DEFAULT_ICON_SIZE}
74-
className={colorText({color: 'secondary'})}
75-
/>
76-
</Button>
67+
<div className={bAlert('message')}>{message}</div>
68+
</div>
69+
{Array.isArray(props.actions) ? (
70+
<Alert.Actions items={props.actions} />
71+
) : (
72+
props.actions
7773
)}
78-
</Flex>
74+
</div>
75+
{onClose && (
76+
<Button
77+
view="flat"
78+
className={bAlert('close-btn')}
79+
onClick={onClose}
80+
aria-label={i18n('label_close')}
81+
>
82+
<Icon
83+
data={Xmark}
84+
size={DEFAULT_ICON_SIZE}
85+
className={colorText({color: 'secondary'})}
86+
/>
87+
</Button>
88+
)}
7989
</Card>
8090
</AlertContextProvider>
8191
);

src/components/Alert/AlertAction.tsx

+21-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,30 @@
11
'use client';
22

33
import {Button} from '../Button';
4+
import type {ButtonSize} from '../Button';
45

5-
import type {AlertActionProps} from './types';
6+
import type {AlertActionProps, AlertSize} from './types';
67
import {useAlertContext} from './useAlertContext';
78

9+
function alertSizeToButtonSize(alertSize: AlertSize): ButtonSize {
10+
switch (alertSize) {
11+
case 's':
12+
case 'm':
13+
return 'm';
14+
case 'l':
15+
default:
16+
return 'l';
17+
}
18+
}
19+
820
export const AlertAction = (props: AlertActionProps) => {
9-
const {view} = useAlertContext();
21+
const {view, size} = useAlertContext();
1022

11-
return <Button view={view === 'filled' ? 'normal-contrast' : undefined} {...props} />;
23+
return (
24+
<Button
25+
view={view === 'filled' ? 'normal-contrast' : undefined}
26+
size={alertSizeToButtonSize(size)}
27+
{...props}
28+
/>
29+
);
1230
};

src/components/Alert/AlertActions.tsx

+7-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
'use client';
22

3-
import {Flex} from '../layout';
4-
53
import {AlertAction} from './AlertAction';
64
import {bAlert} from './constants';
75
import type {AlertActionsProps} from './types';
@@ -11,18 +9,18 @@ export const AlertActions = ({items, children, className}: AlertActionsProps) =>
119
const {layout} = useAlertContext();
1210

1311
return (
14-
<Flex
15-
className={bAlert('actions', {minContent: layout === 'horizontal'}, className)}
16-
direction="row"
17-
gap="3"
18-
wrap
19-
alignItems={layout === 'horizontal' ? 'center' : 'flex-start'}
12+
<div
13+
className={bAlert(
14+
'actions',
15+
{minContent: layout === 'horizontal', center: layout === 'horizontal'},
16+
className,
17+
)}
2018
>
2119
{items?.map(({handler, text}, i) => (
2220
<AlertAction key={i} onClick={handler}>
2321
{text}
2422
</AlertAction>
2523
)) || children}
26-
</Flex>
24+
</div>
2725
);
2826
};

src/components/Alert/AlertContextProvider.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
import {AlertContext} from './AlertContext';
44
import type {AlertContextProviderProps} from './types';
55

6-
export const AlertContextProvider = ({layout, view, children}: AlertContextProviderProps) => {
7-
return <AlertContext.Provider value={{layout, view}}>{children}</AlertContext.Provider>;
6+
export const AlertContextProvider = ({layout, view, size, children}: AlertContextProviderProps) => {
7+
return <AlertContext.Provider value={{layout, view, size}}>{children}</AlertContext.Provider>;
88
};

src/components/Alert/AlertIcon.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ export const AlertIcon = ({
6969
}
7070

7171
return (
72-
<div className={bAlert('icon', colorText({color}, className))}>
73-
<Icon data={iconByTheme[view] as IconData} size={size} />
72+
<div className={bAlert('icon-wrapper', colorText({color}, className))}>
73+
<Icon className={bAlert('icon')} data={iconByTheme[view] as IconData} size={size} />
7474
</div>
7575
);
7676
};

src/components/Alert/AlertTitle.tsx

+1-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
1-
import {Text} from '../Text';
2-
31
import {bAlert} from './constants';
42
import type {AlertTitleProps} from './types';
53

64
export const AlertTitle = ({text, className}: AlertTitleProps) => {
7-
return (
8-
<Text variant="subheader-2" className={bAlert('title', className)}>
9-
{text}
10-
</Text>
11-
);
5+
return <div className={bAlert('title', className)}>{text}</div>;
126
};

src/components/Alert/README-ru.md

+21
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ LANDING_BLOCK-->
219219
| Имя | Описание | Тип | Значение по умолчанию |
220220
| :-------- | :--------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------: | :-------------------: |
221221
| theme | Внешний вид алерта. | `"normal"` `"info"` `"success"` `"warning"` `"danger"` `"utility"` | `"normal"` |
222+
| size | Размер алерта. | `"s"` `"m"` `"l"` | `"m"` |
222223
| view | Включает или отключает цвет фона окна алерта. | `"filled"` `"outlined"` | `"filled"` |
223224
| layout | Используется для привлечения внимания пользователей к контенту, если задано свойство `actions` с кнопками. | `"vertical"` `"horizontal"` | `"vertical"` |
224225
| corners | Управляет оформлением углов (прямые или скругленные) для окна алерта. | `"rounded"` `"square"` | `"rounded"` |
@@ -231,3 +232,23 @@ LANDING_BLOCK-->
231232
| className | Имя CSS-класса алерта. | `string` | |
232233
| icon | Переопределяет иконку по умолчанию. | `React.ReactNode` | |
233234
| qa | HTML-атрибут `data-qa`, используется для тестирования. | `string` | |
235+
236+
## CSS API
237+
238+
| Name | Description |
239+
| :------------------------------ | :------------------------------------------------------------------------------------------ |
240+
| `--g-alert-padding` | Боковые отступы. |
241+
| `--g-alert-border-radius` | Радиус скругления углов. |
242+
| `--g-alert-icon-indent` | Расстояние между иконкой и контентной частью компонента (текстовая часть + дейтствия) |
243+
| `--g-alert-close-btn-indent` | Расстояние между контентной частью компонета (текстовая честь + действия) и кнопой закрытия |
244+
| `--g-alert-content-gap` | Расстояние между текстовым контентом и блоком действий. |
245+
| `--g-alert-text-content-gap` | Расстояние между заголовком и сообщением. |
246+
| `--g-alert-actions-gap` | Расстояние между кнопками действий. |
247+
| `--g-alert-title-color` | Цвет заголовка. |
248+
| `--g-alert-title-font-size` | Размер шрифта заголовка. |
249+
| `--g-alert-title-font-weight` | Жирность шрифта заголовка. |
250+
| `--g-alert-title-line-height` | Межстрочный интервал заголовка. |
251+
| `--g-alert-message-color` | Цвет сообщения. |
252+
| `--g-alert-message-font-size` | Размер шрифта сообщения. |
253+
| `--g-alert-message-font-weight` | Жирность шрифта сообщения. |
254+
| `--g-alert-message-line-height` | Межстрочный интервал сообщения. |

0 commit comments

Comments
 (0)