@sberbusiness/triplex-next-mcp-server 1.45.0-beta → 1.46.0-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,615 @@
1
+ ---
2
+ component: CardAction
3
+ category: Card
4
+ related: [CardStatic]
5
+ tokens:
6
+ - Card.Action_General_Background
7
+ - Card.Action_General_Background_Hover
8
+ - Card.Action_General_Background_Selected
9
+ - Card.Action_General_Background_Selected_Hover
10
+ - Card.Action_Secondary_Background
11
+ - Card.Action_Secondary_Background_Hover
12
+ - Card.Action_Secondary_Background_Selected
13
+ - Card.Action_Secondary_Background_Selected_Hover
14
+ - Card.Shadow_Default
15
+ - Card.Shadow_Hover
16
+ - Card.Shadow_Selected
17
+ - Card.Shadow_Selected_Hover
18
+ - Card.Shadow_Focus
19
+ - Card.Static_General_Background
20
+ - Card.Static_Secondary_Background
21
+ stories: stories/Cards/CardAction.stories.tsx
22
+ version: "1.0"
23
+ ---
24
+
25
+ # CardAction
26
+
27
+ ## Назначение
28
+
29
+ Интерактивная карточка: кликабельный контейнер с состоянием «выбрана / не выбрана»,
30
+ клавиатурной активацией и собственной обводкой фокуса. Контент собирается из составных
31
+ частей `CardAction.Media` и `CardAction.Content`.
32
+
33
+ Используй когда: карточка сама является элементом выбора или действия — плитка тарифа,
34
+ продукта, способа оплаты, выбираемый вариант в списке карточек.
35
+ Не используй когда: карточка только показывает информацию и не реагирует на клик — возьми
36
+ `CardStatic` (у него нет `role="button"`, `tabIndex` и состояния выбора). Для одиночного
37
+ действия внутри информационной карточки положи `Button` в `CardStatic.Content.Footer`,
38
+ а не делай кликабельной всю карточку.
39
+
40
+ ---
41
+
42
+ ## Варианты и props
43
+
44
+ ### Темы (`ECardTheme`)
45
+
46
+ | Значение | Описание |
47
+ |---|---|
48
+ | `GENERAL` | Основная карточка (по умолчанию): фон + тень, при выборе — внутренняя обводка |
49
+ | `SECONDARY` | Карточка на светлой подложке: в покое без внешней тени, состояния передаются фоном |
50
+
51
+ ### Размеры скругления (`ECardRoundingSize`)
52
+
53
+ | Значение | Радиус |
54
+ |---|---|
55
+ | `SM` | 8px |
56
+ | `MD` | 16px (по умолчанию) |
57
+ | `LG` | 24px |
58
+
59
+ Скругление задаётся на корневом элементе и наследуется обводкой фокуса/выбора, поэтому
60
+ переопределять `border-radius` снаружи не нужно.
61
+
62
+ ### Опциональные props
63
+
64
+ | Prop | Тип | По умолчанию | Описание |
65
+ |---|---|---|---|
66
+ | `theme` | `ECardTheme` | `GENERAL` | Тема оформления |
67
+ | `roundingSize` | `ECardRoundingSize` | `MD` | Размер скругления |
68
+ | `selected` | `boolean` | — | Состояние выбора в контролируемом режиме. Контролируемый режим включает значение `!== undefined` при монтировании, а не сам факт передачи prop'а: `selected={undefined}` — это неконтролируемый режим |
69
+ | `toggle` | `(selected: boolean) => void` | — | Запрос на смену состояния в контролируемом режиме; вызывается со следующим значением |
70
+ | `onToggle` | `(selected: boolean) => void` | — | Уведомление о состоявшейся смене состояния; вызывается с новым значением |
71
+ | `...HTMLDivAttributes` | — | — | Любые атрибуты `<div>`, включая `aria-*` и `data-*`. `onClick`, `onMouseDown`, `onKeyDown`, `onFocus`, `onBlur` не заменяют собственные обработчики компонента, а вызываются перед ними |
72
+
73
+ ### Контролируемый и неконтролируемый режимы
74
+
75
+ Режим определяется **один раз при монтировании** по `selected !== undefined` и дальше не
76
+ меняется. Передать `selected` только на части жизненного цикла нельзя — компонент
77
+ запомнит режим из первого рендера.
78
+
79
+ - **Неконтролируемый** (`selected` при монтировании равен `undefined` — не передан вовсе
80
+ либо передан как `selected={undefined}`): состояние выбора хранится внутри. Клик,
81
+ `Enter` и `Space` переключают его, после чего вызывается `onToggle(новоеЗначение)`.
82
+ `toggle` в этом режиме не вызывается никогда. Начальное состояние — «не выбрана»;
83
+ отдельного prop'а вида `defaultSelected` нет.
84
+ - **Контролируемый** (`selected` при монтировании `!== undefined`): компонент сам состояние
85
+ не меняет. Клик,
86
+ `Enter` и `Space` вызывают `toggle(!selected)` — потребитель обязан обновить `selected`,
87
+ иначе вид карточки не изменится. `onToggle` здесь вызывается **не по клику**, а
88
+ на изменение самого prop'а `selected` — в том числе когда состояние поменяли снаружи, без
89
+ участия карточки.
90
+
91
+ Оговорка про `onToggle` на смену prop'а: она живёт в `componentDidUpdate` и срабатывает
92
+ на любое изменение `selected`, **без проверки режима**. Поэтому у карточки, смонтированной
93
+ неконтролируемой, поздний переход `selected` с `undefined` на `true`/`false` тоже вызовет
94
+ `onToggle` — хотя режим остаётся неконтролируемым и внутреннее состояние выбора от этого
95
+ не меняется. Сценарий редкий (обычно `selected` у неконтролируемой карточки не появляется),
96
+ но при `selected={условие ? x : undefined}` он достижим.
97
+
98
+ ### Составные части
99
+
100
+ | Часть | Назначение |
101
+ |---|---|
102
+ | `CardAction.Media` | Медийная область вверху карточки. Собственных props нет: изображение задаётся через `style`/`className`, высота — потребителем |
103
+ | `CardAction.Content` | Контентная область; prop `paddingSize` (`ECardContentPaddingSize`: `SM` — 16px, `MD` — 24px, по умолчанию `MD`) |
104
+ | `CardAction.Content.Header` | Заголовок, отступ 16px снизу |
105
+ | `CardAction.Content.Body` | Тело, растягивается на свободную высоту |
106
+ | `CardAction.Content.Footer` | Подвал, отступ 16px сверху |
107
+
108
+ Части — общие с `CardStatic` (`CardStatic.Content` и `CardAction.Content` — один и тот же
109
+ компонент), поэтому их правки затрагивают оба вида карточек.
110
+
111
+ ---
112
+
113
+ ## Дизайн-токены
114
+
115
+ Переопределяются через `ThemeProvider` (prop `tokens`) — см. `ThemeProvider-ai.md` →
116
+ «Как переопределять токены». Значения по умолчанию — `src/components/DesignTokens/components/Card.ts`.
117
+
118
+ Паттерн: `Card.Action_{Theme}_Background_{State}` для фона и `Card.Shadow_{State}` для теней.
119
+
120
+ ```text
121
+ Card.Action_General_Background
122
+ Card.Action_General_Background_Hover
123
+ Card.Action_General_Background_Selected
124
+ Card.Action_General_Background_Selected_Hover
125
+ Card.Action_Secondary_Background
126
+ Card.Action_Secondary_Background_Hover
127
+ Card.Action_Secondary_Background_Selected
128
+ Card.Action_Secondary_Background_Selected_Hover
129
+ Card.Shadow_Default
130
+ Card.Shadow_Hover
131
+ Card.Shadow_Selected
132
+ Card.Shadow_Selected_Hover
133
+ Card.Shadow_Focus
134
+ ```
135
+
136
+ Тени выбора и фокуса рисуются не на самом элементе, а на псевдоэлементе `::before` через
137
+ внутреннюю CSS-переменную `--card-inner-shadow`. У `::before` стоит `z-index: 10`, чтобы
138
+ внутренняя обводка ложилась поверх фона `CardAction.Media`: inset-тень на самом корне была бы
139
+ перекрыта фонами дочерних элементов. `pointer-events: none` там же не даёт псевдоэлементу
140
+ перехватывать клики. Корневой элемент интерактивной карточки дополнительно несёт общий класс из
141
+ `styles/Card.module.less` (лейаут, скругление), а её собственные состояния — выбор, hover и
142
+ фокус — задаются токенами `Card.Action_*` и `Card.Shadow_*`.
143
+
144
+ Базовый фон в покое — единственное место, где правила двух модулей пересекаются: общий класс
145
+ темы из `Card.module.less` задаёт `Card.Static_*_Background` с той же специфичностью (0,2,0),
146
+ что и `Card.Action_*_Background`, поэтому победитель зависит от порядка инъекции CSS. На
147
+ состояния это не влияет — у них специфичность выше. Не опирайся на конкретный источник
148
+ базового фона; если нужно поменять его, правь оба модуля согласованно.
149
+
150
+ ---
151
+
152
+ ## Инварианты
153
+
154
+ - `CardAction` — **классовый компонент без `forwardRef`**. `ref` на нём даёт экземпляр класса,
155
+ а не DOM-элемент. Это часть публичного API: перевод на функциональный компонент
156
+ с `forwardRef` сменит ref-target и является breaking change.
157
+ Все `handle*`-методы (`handleClick`, `handleMouseDown`, `handleKeyDown`, `handleFocus`,
158
+ `handleBlur`, `handleToggle`) объявлены `public` и вместе с ref-экземпляром входят
159
+ в наблюдаемую поверхность API — сужение до `private` тоже breaking change.
160
+ - Корневой элемент — `<div>` с `role="button"` и `tabIndex={0}`. И роль, и `tabIndex`
161
+ выставляются до `...attributes`, поэтому потребитель может их переопределить.
162
+ - Имена props (`selected`, `toggle`, `onToggle`, `theme`, `roundingSize`), значения
163
+ `ECardTheme` / `ECardRoundingSize` / `ECardContentPaddingSize` и barrel-экспорты
164
+ `src/components/Card/index.ts` — публичный API, не переименовывать.
165
+ - Статические свойства `CardAction.Content` и `CardAction.Media` — часть публичного API.
166
+ - Составные части (`CardMedia`, `CardContent`, `CardContent.Header/Body/Footer`) объявлены
167
+ как `React.FC` и **`ref` не пробрасывают** — переданный им `ref` молча теряется, DOM-узел
168
+ через него не получить. Не пиши на них тест вида
169
+ `expect(ref.current).toBeInstanceOf(HTMLDivElement)` — он упадёт. Файлы частей общие
170
+ с `CardStatic`, поэтому добавление им `forwardRef` — отдельная задача на семейство Card,
171
+ а не правка в рамках `CardAction`.
172
+ - CSS-классы `selected` и `focusVisible` используются unit-тестами — не переименовывать
173
+ без синхронной правки тестов.
174
+ - Режим (контролируемый / неконтролируемый) фиксируется при монтировании — не превращать
175
+ в вычисление на каждый рендер без обсуждения: это изменит наблюдаемое поведение.
176
+
177
+ ---
178
+
179
+ ## Accessibility
180
+
181
+ - Роль `button` и `tabIndex={0}` на корневом `<div>` — карточка попадает в tab-порядок и
182
+ анонсируется как кнопка.
183
+ - Клавиатура: `Enter` и `Space` переключают состояние выбора. Для `Space` вызывается
184
+ `preventDefault()`, чтобы страница не прокручивалась; для `Enter` — нет.
185
+ - Обводка фокуса показывается **только при клавиатурном фокусе**: компонент отслеживает
186
+ источник фокуса (`EFocusSource`) — после `mousedown` фокус считается мышиным и класс
187
+ `focusVisible` не выставляется. Нативный `outline` погашен, видимый фокус даёт токен
188
+ `Card.Shadow_Focus`.
189
+ - Компонент **не выставляет `aria-pressed`** и не хардкодит текстов (библиотека
190
+ мультиязычная). Если карточка работает как переключатель, потребитель передаёт
191
+ `aria-pressed={selected}` (и при необходимости `aria-label`) сам — они попадут на корневой
192
+ элемент через `...attributes`.
193
+ - Интерактивные элементы внутри карточки (кнопка в `Footer`, ссылка в `Body`) получают клик
194
+ раньше карточки, но событие всплывает — карточка переключится тоже. Если это не нужно,
195
+ вызывай `event.stopPropagation()` в обработчике вложенного элемента.
196
+
197
+ ---
198
+
199
+ ## Связанные компоненты
200
+
201
+ - `CardStatic` — статичная карточка: тот же лейаут и те же составные части, но без
202
+ состояния выбора, роли `button` и клавиатурной активации.
203
+ - `CardContent` (`CardAction.Content`) — контентная область карточки с `paddingSize`;
204
+ отдельного AI.md нет, описана выше в «Составных частях».
205
+ - `CardMedia` (`CardAction.Media`) — медийная область; принимает `className` и `...rest`,
206
+ но **не `ref`** (см. «Инварианты»), отдельного AI.md нет.
207
+
208
+ ---
209
+
210
+ ## Stories
211
+
212
+ Основные истории: `stories/Cards/CardAction.stories.tsx`
213
+ Файлы примеров: `stories/Cards/examples/CardAction/`
214
+
215
+ | Story | Example file | Что демонстрирует |
216
+ |---|---|---|
217
+ | `Playground` | `PlaygroundExample.tsx` | Интерактивный подбор `theme`, `roundingSize` и `paddingSize` |
218
+ | `Default` | `DefaultExample.tsx` | Минимальная карточка с заголовком и телом в контролируемом режиме |
219
+ | `Themes` | `ThemesExample.tsx` | Темы `GENERAL` и `SECONDARY` |
220
+ | `PaddingSizes` | `PaddingSizesExample.tsx` | Внутренние отступы контента `SM` и `MD` |
221
+ | `RoundingSizes` | `RoundingSizesExample.tsx` | Скругления `SM`, `MD`, `LG` |
222
+ | `VisualTests` | `VisualTests.tsx` | Скриншот-регрессия: выбранное состояние в обеих темах и клавиатурный фокус (через `play`) |
223
+
224
+ ---
225
+
226
+ ## История изменений
227
+
228
+ | Дата | Изменение |
229
+ |---|---|
230
+ | 2026-09-10 | Создан документ |
231
+
232
+ ---
233
+
234
+ ## Примеры использования
235
+
236
+ ### Default
237
+
238
+ `stories/Cards/examples/CardAction/DefaultExample.tsx`
239
+
240
+ ```tsx
241
+ import React, { useState } from "react";
242
+ import {
243
+ CardAction,
244
+ ECardContentPaddingSize,
245
+ ECardRoundingSize,
246
+ ECardTheme,
247
+ EFontWeightTitle,
248
+ ETextSize,
249
+ ETitleSize,
250
+ Text,
251
+ Title,
252
+ } from "@sberbusiness/triplex-next";
253
+
254
+ export const DefaultExample = () => {
255
+ const [isSelected, setIsSelected] = useState(false);
256
+
257
+ return (
258
+ <div style={{ width: "216px" }}>
259
+ <CardAction
260
+ roundingSize={ECardRoundingSize.MD}
261
+ theme={ECardTheme.GENERAL}
262
+ selected={isSelected}
263
+ toggle={setIsSelected}
264
+ >
265
+ <CardAction.Content paddingSize={ECardContentPaddingSize.MD}>
266
+ <CardAction.Content.Header>
267
+ <Title tag="div" size={ETitleSize.H3} weight={EFontWeightTitle.REGULAR}>
268
+ Title text
269
+ </Title>
270
+ </CardAction.Content.Header>
271
+ <CardAction.Content.Body>
272
+ <Text tag="div" size={ETextSize.B3}>
273
+ Body content
274
+ </Text>
275
+ </CardAction.Content.Body>
276
+ </CardAction.Content>
277
+ </CardAction>
278
+ </div>
279
+ );
280
+ };
281
+ ```
282
+
283
+ ### Themes
284
+
285
+ `stories/Cards/examples/CardAction/ThemesExample.tsx`
286
+
287
+ ```tsx
288
+ import React, { useState } from "react";
289
+ import { DefaulticonStrokePrdIcon20 } from "@sberbusiness/icons-next";
290
+ import {
291
+ CardAction,
292
+ ECardContentPaddingSize,
293
+ ECardRoundingSize,
294
+ ECardTheme,
295
+ EComponentSize,
296
+ EFontType,
297
+ EFontWeightTitle,
298
+ ETextSize,
299
+ ETitleSize,
300
+ Button,
301
+ EButtonTheme,
302
+ Gap,
303
+ Link,
304
+ Text,
305
+ Title,
306
+ } from "@sberbusiness/triplex-next";
307
+
308
+ const MEDIA_HEIGHT = "129px";
309
+
310
+ const CardBody = () => (
311
+ <CardAction.Content.Body>
312
+ <div style={{ display: "flex", alignItems: "center" }}>
313
+ <DefaulticonStrokePrdIcon20 paletteIndex={5} />
314
+ <Text size={ETextSize.B3} style={{ marginLeft: "8px" }}>
315
+ List item text
316
+ </Text>
317
+ </div>
318
+ <div style={{ display: "flex", alignItems: "center", marginTop: "16px" }}>
319
+ <DefaulticonStrokePrdIcon20 paletteIndex={5} />
320
+ <Text size={ETextSize.B3} style={{ marginLeft: "8px" }}>
321
+ List item text
322
+ </Text>
323
+ </div>
324
+ <div style={{ display: "flex", alignItems: "center", marginTop: "16px" }}>
325
+ <DefaulticonStrokePrdIcon20 paletteIndex={5} />
326
+ <Text size={ETextSize.B3} style={{ marginLeft: "8px" }}>
327
+ List item text
328
+ </Text>
329
+ </div>
330
+ <Gap size={8} />
331
+ <Text tag="div" type={EFontType.SECONDARY} size={ETextSize.B4}>
332
+ This message provides additional context or highlights important information to note.
333
+ </Text>
334
+ <Gap size={8} />
335
+ <Text tag="div" type={EFontType.PRIMARY} size={ETextSize.B3}>
336
+ <Link onClick={() => {}}>Link text</Link>
337
+ </Text>
338
+ </CardAction.Content.Body>
339
+ );
340
+
341
+ export const ThemesExample = () => {
342
+ const [isSelectedGeneral, setIsSelectedGeneral] = useState(false);
343
+ const [isSelectedSecondary, setIsSelectedSecondary] = useState(false);
344
+
345
+ return (
346
+ <div style={{ width: "448px", display: "flex", gap: "16px" }}>
347
+ <div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
348
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>GENERAL</div>
349
+ <CardAction
350
+ roundingSize={ECardRoundingSize.MD}
351
+ theme={ECardTheme.GENERAL}
352
+ selected={isSelectedGeneral}
353
+ toggle={setIsSelectedGeneral}
354
+ >
355
+ <CardAction.Media
356
+ style={{ backgroundImage: "url(assets/images/evotor.png)", height: MEDIA_HEIGHT }}
357
+ />
358
+ <CardAction.Content paddingSize={ECardContentPaddingSize.MD}>
359
+ <CardAction.Content.Header>
360
+ <Title tag="div" size={ETitleSize.H3} weight={EFontWeightTitle.REGULAR}>
361
+ Title text
362
+ </Title>
363
+ </CardAction.Content.Header>
364
+ <CardBody />
365
+ <CardAction.Content.Footer>
366
+ <Button
367
+ theme={isSelectedGeneral ? EButtonTheme.SECONDARY_LIGHT : EButtonTheme.SECONDARY}
368
+ size={EComponentSize.SM}
369
+ >
370
+ Button text
371
+ </Button>
372
+ </CardAction.Content.Footer>
373
+ </CardAction.Content>
374
+ </CardAction>
375
+ </div>
376
+ <div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
377
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>SECONDARY</div>
378
+ <CardAction
379
+ roundingSize={ECardRoundingSize.MD}
380
+ theme={ECardTheme.SECONDARY}
381
+ selected={isSelectedSecondary}
382
+ toggle={setIsSelectedSecondary}
383
+ >
384
+ <CardAction.Media
385
+ style={{ backgroundImage: "url(assets/images/evotor.png)", height: MEDIA_HEIGHT }}
386
+ />
387
+ <CardAction.Content paddingSize={ECardContentPaddingSize.MD}>
388
+ <CardAction.Content.Header>
389
+ <Title tag="div" size={ETitleSize.H3} weight={EFontWeightTitle.REGULAR}>
390
+ Title text
391
+ </Title>
392
+ </CardAction.Content.Header>
393
+ <CardBody />
394
+ </CardAction.Content>
395
+ </CardAction>
396
+ </div>
397
+ </div>
398
+ );
399
+ };
400
+ ```
401
+
402
+ ### PaddingSizes
403
+
404
+ `stories/Cards/examples/CardAction/PaddingSizesExample.tsx`
405
+
406
+ ```tsx
407
+ import React, { useState } from "react";
408
+ import { DefaulticonStrokePrdIcon20 } from "@sberbusiness/icons-next";
409
+ import {
410
+ CardAction,
411
+ ECardContentPaddingSize,
412
+ ECardRoundingSize,
413
+ ECardTheme,
414
+ EFontType,
415
+ EFontWeightTitle,
416
+ ETextSize,
417
+ ETitleSize,
418
+ Gap,
419
+ Link,
420
+ Text,
421
+ Title,
422
+ } from "@sberbusiness/triplex-next";
423
+
424
+ const MEDIA_HEIGHT = "129px";
425
+
426
+ const CardBody = () => (
427
+ <CardAction.Content.Body>
428
+ <div style={{ display: "flex", alignItems: "center" }}>
429
+ <DefaulticonStrokePrdIcon20 paletteIndex={5} />
430
+ <Text size={ETextSize.B3} style={{ marginLeft: "8px" }}>
431
+ List item text
432
+ </Text>
433
+ </div>
434
+ <Gap size={8} />
435
+ <Text tag="div" type={EFontType.SECONDARY} size={ETextSize.B4}>
436
+ This message provides additional context or highlights important information to note.
437
+ </Text>
438
+ <Gap size={8} />
439
+ <Text tag="div" type={EFontType.PRIMARY} size={ETextSize.B3}>
440
+ <Link onClick={() => {}}>Link text</Link>
441
+ </Text>
442
+ </CardAction.Content.Body>
443
+ );
444
+
445
+ export const PaddingSizesExample = () => {
446
+ const [isSelectedSM, setIsSelectedSM] = useState(false);
447
+ const [isSelectedMD, setIsSelectedMD] = useState(false);
448
+
449
+ return (
450
+ <div style={{ width: "448px", display: "flex", gap: "16px" }}>
451
+ <div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
452
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>SM</div>
453
+ <CardAction
454
+ roundingSize={ECardRoundingSize.MD}
455
+ theme={ECardTheme.GENERAL}
456
+ selected={isSelectedSM}
457
+ toggle={setIsSelectedSM}
458
+ >
459
+ <CardAction.Media
460
+ style={{ backgroundImage: "url(assets/images/evotor.png)", height: MEDIA_HEIGHT }}
461
+ />
462
+ <CardAction.Content paddingSize={ECardContentPaddingSize.SM}>
463
+ <CardAction.Content.Header>
464
+ <Title tag="div" size={ETitleSize.H3} weight={EFontWeightTitle.REGULAR}>
465
+ Title text
466
+ </Title>
467
+ </CardAction.Content.Header>
468
+ <CardBody />
469
+ </CardAction.Content>
470
+ </CardAction>
471
+ </div>
472
+ <div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
473
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>MD</div>
474
+ <CardAction
475
+ roundingSize={ECardRoundingSize.MD}
476
+ theme={ECardTheme.GENERAL}
477
+ selected={isSelectedMD}
478
+ toggle={setIsSelectedMD}
479
+ >
480
+ <CardAction.Media
481
+ style={{ backgroundImage: "url(assets/images/evotor.png)", height: MEDIA_HEIGHT }}
482
+ />
483
+ <CardAction.Content paddingSize={ECardContentPaddingSize.MD}>
484
+ <CardAction.Content.Header>
485
+ <Title tag="div" size={ETitleSize.H3} weight={EFontWeightTitle.REGULAR}>
486
+ Title text
487
+ </Title>
488
+ </CardAction.Content.Header>
489
+ <CardBody />
490
+ </CardAction.Content>
491
+ </CardAction>
492
+ </div>
493
+ </div>
494
+ );
495
+ };
496
+ ```
497
+
498
+ ### RoundingSizes
499
+
500
+ `stories/Cards/examples/CardAction/RoundingSizesExample.tsx`
501
+
502
+ ```tsx
503
+ import React, { useState } from "react";
504
+ import { DefaulticonStrokePrdIcon20 } from "@sberbusiness/icons-next";
505
+ import {
506
+ CardAction,
507
+ ECardContentPaddingSize,
508
+ ECardRoundingSize,
509
+ ECardTheme,
510
+ EFontType,
511
+ EFontWeightTitle,
512
+ ETextSize,
513
+ ETitleSize,
514
+ Gap,
515
+ Link,
516
+ Text,
517
+ Title,
518
+ } from "@sberbusiness/triplex-next";
519
+
520
+ const MEDIA_HEIGHT = "129px";
521
+
522
+ const CardBody = () => (
523
+ <CardAction.Content.Body>
524
+ <div style={{ display: "flex", alignItems: "center" }}>
525
+ <DefaulticonStrokePrdIcon20 paletteIndex={5} />
526
+ <Text size={ETextSize.B3} style={{ marginLeft: "8px" }}>
527
+ List item text
528
+ </Text>
529
+ </div>
530
+ <Gap size={8} />
531
+ <Text tag="div" type={EFontType.SECONDARY} size={ETextSize.B4}>
532
+ This message provides additional context or highlights important information to note.
533
+ </Text>
534
+ <Gap size={8} />
535
+ <Text tag="div" type={EFontType.PRIMARY} size={ETextSize.B3}>
536
+ <Link onClick={() => {}}>Link text</Link>
537
+ </Text>
538
+ </CardAction.Content.Body>
539
+ );
540
+
541
+ export const RoundingSizesExample = () => {
542
+ const [isSelectedSM, setIsSelectedSM] = useState(false);
543
+ const [isSelectedMD, setIsSelectedMD] = useState(false);
544
+ const [isSelectedLG, setIsSelectedLG] = useState(false);
545
+
546
+ return (
547
+ <div style={{ width: "680px", display: "flex", gap: "16px" }}>
548
+ <div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
549
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>SM</div>
550
+ <CardAction
551
+ roundingSize={ECardRoundingSize.SM}
552
+ theme={ECardTheme.GENERAL}
553
+ selected={isSelectedSM}
554
+ toggle={setIsSelectedSM}
555
+ >
556
+ <CardAction.Media
557
+ style={{ backgroundImage: "url(assets/images/evotor.png)", height: MEDIA_HEIGHT }}
558
+ />
559
+ <CardAction.Content paddingSize={ECardContentPaddingSize.MD}>
560
+ <CardAction.Content.Header>
561
+ <Title tag="div" size={ETitleSize.H3} weight={EFontWeightTitle.REGULAR}>
562
+ Title text
563
+ </Title>
564
+ </CardAction.Content.Header>
565
+ <CardBody />
566
+ </CardAction.Content>
567
+ </CardAction>
568
+ </div>
569
+ <div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
570
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>MD</div>
571
+ <CardAction
572
+ roundingSize={ECardRoundingSize.MD}
573
+ theme={ECardTheme.GENERAL}
574
+ selected={isSelectedMD}
575
+ toggle={setIsSelectedMD}
576
+ >
577
+ <CardAction.Media
578
+ style={{ backgroundImage: "url(assets/images/evotor.png)", height: MEDIA_HEIGHT }}
579
+ />
580
+ <CardAction.Content paddingSize={ECardContentPaddingSize.MD}>
581
+ <CardAction.Content.Header>
582
+ <Title tag="div" size={ETitleSize.H3} weight={EFontWeightTitle.REGULAR}>
583
+ Title text
584
+ </Title>
585
+ </CardAction.Content.Header>
586
+ <CardBody />
587
+ </CardAction.Content>
588
+ </CardAction>
589
+ </div>
590
+ <div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
591
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>LG</div>
592
+ <CardAction
593
+ roundingSize={ECardRoundingSize.LG}
594
+ theme={ECardTheme.GENERAL}
595
+ selected={isSelectedLG}
596
+ toggle={setIsSelectedLG}
597
+ >
598
+ <CardAction.Media
599
+ style={{ backgroundImage: "url(assets/images/evotor.png)", height: MEDIA_HEIGHT }}
600
+ />
601
+ <CardAction.Content paddingSize={ECardContentPaddingSize.MD}>
602
+ <CardAction.Content.Header>
603
+ <Title tag="div" size={ETitleSize.H3} weight={EFontWeightTitle.REGULAR}>
604
+ Title text
605
+ </Title>
606
+ </CardAction.Content.Header>
607
+ <CardBody />
608
+ </CardAction.Content>
609
+ </CardAction>
610
+ </div>
611
+ </div>
612
+ );
613
+ };
614
+ ```
615
+
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  component: Chip
3
3
  category: Chips
4
- related: [ChipGroup, ChipIcon, ChipOptions, ChipSelect, ChipMultiselect, ChipSort, ChipSuggest, ChipDatePicker, ChipClearButton, ChipDropdownArrow, Badge, IconWrapper]
4
+ related: [ChipGroup, ChipIcon, ChipOptions, ChipSelect, ChipMultiselect, ChipSort, ChipSuggest, ChipDatePicker, ChipMonthYearPicker, ChipClearButton, ChipDropdownArrow, Badge, IconWrapper]
5
5
  tokens:
6
6
  - Chip.Background_Type1_Default
7
7
  - Chip.Background_Type1_Hover
@@ -33,7 +33,7 @@ version: "1.0"
33
33
  Компактный интерактивный элемент-«чипс»: выполняет действие по нажатию и отображает
34
34
  выбранное (`selected`) состояние. Одновременно служит базой для всего семейства Chip*
35
35
  — поверх него построены `ChipIcon`, `ChipOptions`, а также target-элементы
36
- `ChipSelect`, `ChipMultiselect`, `ChipSuggest`, `ChipDatePicker`.
36
+ `ChipSelect`, `ChipMultiselect`, `ChipSuggest`, `ChipDatePicker`, `ChipMonthYearPicker`.
37
37
 
38
38
  Используй когда: нужен переключаемый фильтр, тег или компактная кнопка-действие
39
39
  в ряду однотипных элементов.
@@ -152,7 +152,7 @@ Default и Hover. Цвет текста (`Color_*`) от типа не зави
152
152
  - **Класс `.chipGroupItem`** выставляется всегда, наравне с `.chip`. Объявлен
153
153
  в `styles/Chip.module.less` и служит общим маркером «элемент верхнего уровня
154
154
  в ряду чипсов»: его проставляют себе все члены семейства — `Chip`, `ChipSort`,
155
- `ChipMultiselect`, `ChipSelect`, `ChipDatePicker`, `ChipSuggest`. Причём
155
+ `ChipMultiselect`, `ChipSelect`, `ChipDatePicker`, `ChipMonthYearPicker`, `ChipSuggest`. Причём
156
156
  `.chipGroupItem` не всегда совпадает с `.chip`: в `ChipSelect` / `ChipMultiselect`
157
157
  `.chip` лежит внутри элемента с `.chipGroupItem`. Раскладка `ChipGroup` на этот
158
158
  класс не опирается — в её стилях он не встречается. Не удалять и не переименовывать.
@@ -177,7 +177,7 @@ Default и Hover. Цвет текста (`Color_*`) от типа не зави
177
177
  компонент не генерирует (у `<span role="button">` нет нативного поведения кнопки),
178
178
  внешний `onKeyDown` вызывается всегда и после `preventDefault`. Если чипс должен
179
179
  срабатывать с клавиатуры, потребитель обрабатывает Enter/Space сам через `onKeyDown`.
180
- Обёртки семейства (`ChipSelect`, `ChipSuggest`, `ChipDatePicker`) делают это на своём уровне.
180
+ Обёртки семейства (`ChipSelect`, `ChipSuggest`, `ChipDatePicker`, `ChipMonthYearPicker`) делают это на своём уровне.
181
181
  - Фокус-стиль через `:focus-visible` (`Chip.Shadow_Focus`) — виден только
182
182
  при клавиатурной навигации. Нативная обводка на корневом элементе намеренно отключена
183
183
  (`outline: none` в LESS) в пользу `box-shadow` — убирать правило нельзя, иначе фокус
@@ -206,8 +206,8 @@ Default и Hover. Цвет текста (`Color_*`) от типа не зави
206
206
  - `ChipDropdownArrow` — стрелка дропдауна для `postfix`; объявлена как `React.FC`
207
207
  (не `forwardRef`), собственных обработчиков не имеет. Props: **обязательный**
208
208
  `rotated: boolean` (даёт класс `.rotated`) и `size?: EComponentSize`
209
- - `ChipSelect`, `ChipMultiselect`, `ChipSuggest`, `ChipDatePicker`, `ChipSort` —
210
- составные компоненты, использующие `Chip` как target-элемент дропдауна
209
+ - `ChipSelect`, `ChipMultiselect`, `ChipSuggest`, `ChipDatePicker`, `ChipMonthYearPicker`,
210
+ `ChipSort` — составные компоненты, использующие `Chip` как target-элемент дропдауна
211
211
  - `Badge` (`Badge.Dot`) — значок уведомлений при `showNotificationIcon`
212
212
  - `IconWrapper` — обёртка `prefix` / `postfix`, добавляет классы `hoverable` / `disabled`,
213
213
  управляющие цветом иконки при взаимодействии
@@ -239,6 +239,7 @@ Default и Hover. Цвет текста (`Color_*`) от типа не зави
239
239
  | 2026-07-29 | Создан документ (TRI-26, AI-Ready Phase 1) |
240
240
  | 2026-07-29 | AI-рефакторинг (TRI-26): codestyle-чистка `Chip.tsx` (константы-маппинги в UPPER_SNAKE_CASE, эквивалентные упрощения в `clsx`, JSDoc на `children` и `handleKeyDown`), unit-тесты расширены с 5 до 22 кейсов; публичный API, DOM и визуал не изменены |
241
241
  | 2026-07-29 | Правки по ревью PR #487: уточнена атрибуция класса `.chipGroupItem` (его проставляет семейство Chip*, а не раскладка `ChipGroup`) и описание API `ChipClearButton` / `ChipDropdownArrow` |
242
+ | 2026-09-07 | В семейство добавлен `ChipMonthYearPicker` — упомянут в списках потребителей `Chip` и `.chipGroupItem` |
242
243
 
243
244
  ---
244
245