@sberbusiness/triplex-next-mcp-server 0.24.0-beta2 → 0.36.0-beta0

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.
Files changed (55) hide show
  1. package/README.md +10 -0
  2. package/bundle/components/BodyPage.md +186 -0
  3. package/bundle/components/Button.md +90 -85
  4. package/bundle/components/ButtonDropdown.md +28 -70
  5. package/bundle/components/ButtonIcon.md +35 -48
  6. package/bundle/components/Checkbox.md +43 -122
  7. package/bundle/components/CheckboxXGroup.md +109 -46
  8. package/bundle/components/CheckboxYGroup.md +77 -49
  9. package/bundle/components/CollapsibleTree.md +581 -0
  10. package/bundle/components/CollapsibleTreeExtended.md +418 -0
  11. package/bundle/components/EmptyView.md +14 -15
  12. package/bundle/components/FooterPage.md +203 -0
  13. package/bundle/components/Gap.md +115 -57
  14. package/bundle/components/HeaderPage.md +226 -0
  15. package/bundle/components/HelpBox.md +45 -84
  16. package/bundle/components/ImageGallery.md +423 -0
  17. package/bundle/components/ImageGalleryExtended.md +606 -0
  18. package/bundle/components/List.md +194 -186
  19. package/bundle/components/ListItem.md +251 -0
  20. package/bundle/components/ListItemControlsButton.md +103 -0
  21. package/bundle/components/ListItemControlsButtonDropdown.md +107 -0
  22. package/bundle/components/ListItemSelectable.md +99 -0
  23. package/bundle/components/ListItemTable.md +390 -0
  24. package/bundle/components/ListMaster.md +877 -156
  25. package/bundle/components/ListMasterFooter.md +80 -0
  26. package/bundle/components/ListMasterHeader.md +86 -0
  27. package/bundle/components/ListSortable.md +153 -0
  28. package/bundle/components/ListSortableItem.md +175 -0
  29. package/bundle/components/ModalWindow.md +427 -237
  30. package/bundle/components/MonthYearField.md +145 -109
  31. package/bundle/components/Notification.md +625 -155
  32. package/bundle/components/Overlay.md +271 -170
  33. package/bundle/components/Page.md +283 -186
  34. package/bundle/components/Pagination.md +206 -229
  35. package/bundle/guides/codestyle.md +38 -11
  36. package/bundle/guides/commits.md +1 -2
  37. package/bundle/guides/context.md +34 -30
  38. package/bundle/guides/stories.md +110 -40
  39. package/bundle/guides/template.md +3 -7
  40. package/bundle/guides/tests.md +6 -2
  41. package/bundle/manifest.json +194 -24
  42. package/bundle/release-notes/1.26.0.mdx +33 -2
  43. package/bundle/release-notes/1.27.0.mdx +43 -0
  44. package/bundle/release-notes/1.28.0.mdx +25 -0
  45. package/bundle/release-notes/1.29.0.mdx +47 -0
  46. package/bundle/release-notes/1.30.0.mdx +56 -0
  47. package/bundle/release-notes/1.31.0.mdx +52 -0
  48. package/bundle/release-notes/1.32.0.mdx +35 -0
  49. package/bundle/release-notes/1.33.0.mdx +61 -0
  50. package/bundle/release-notes/1.34.0.mdx +35 -0
  51. package/bundle/release-notes/1.35.0.mdx +21 -0
  52. package/bundle/release-notes/1.35.1.mdx +11 -0
  53. package/bundle/release-notes/1.36.0.mdx +21 -0
  54. package/bundle/release-notes/1.37.0.mdx +30 -0
  55. package/package.json +1 -1
package/README.md CHANGED
@@ -80,6 +80,9 @@ npm run inspect
80
80
  # Обновить bundle/ из последнего релиза triplex-next
81
81
  npm run fetch-bundle
82
82
 
83
+ # Обновить только AI-ready компоненты из bundle, остальные данные сохранить
84
+ npm run fetch-bundle -- --merge --file path/to/mcp-data.json
85
+
83
86
  # Обновить bundle/icons.json из @sberbusiness/icons-next
84
87
  npm run fetch-icons
85
88
 
@@ -111,6 +114,13 @@ bundle/
111
114
  содержимое в поддиректории выше. Транзиентный `mcp-data.json` удаляется
112
115
  после раскладки.
113
116
 
117
+ По умолчанию дерево перезаписывается целиком. Флаг `--merge` включает
118
+ инкрементальное обновление: компоненты, у которых в bundle есть данные
119
+ (`raw !== null`), перезаписываются, остальные сохраняют текущую документацию
120
+ и записи в `manifest.json`. Гайды и release notes из bundle перезаписываются,
121
+ отсутствующие в bundle — сохраняются. Режим полезен, пока AI-документация
122
+ в triplex-next выверяется покомпонентно.
123
+
114
124
  В triplex-next bundle собирается скриптом `scripts/generateMcpData.ts`
115
125
  (npm-команда `generateMcpData`) и автоматически прикладывается к релиз-ассетам
116
126
  через `.github/workflows/release.yml`.
@@ -0,0 +1,186 @@
1
+ ---
2
+ component: BodyPage
3
+ category: Page
4
+ related: [Page, HeaderPage, FooterPage, Body, Island, LightBox]
5
+ tokens: []
6
+ stories: stories/Page/BodyPage/BodyPage.stories.tsx
7
+ version: "1.0"
8
+ ---
9
+
10
+ # BodyPage
11
+
12
+ ## Назначение
13
+
14
+ Тело страницы `Page`. Доступен как статическое свойство `Page.Body`. Контейнер для основного
15
+ контента страницы между `Page.Header` и `Page.Footer`.
16
+
17
+ Используй когда: нужно разместить основной контент внутри `Page` — как в карточке (Island), так и без неё.
18
+ Не используй когда: нужен заголовок или футер страницы — для этого есть `Page.Header` / `Page.Footer`.
19
+
20
+ ---
21
+
22
+ ## Варианты и props
23
+
24
+ Публичный API — discriminated union по `type`: `IBodyPageTypeFirstProps | IBodyPageTypeSecondProps`.
25
+ Оба интерфейса расширяют `IBodyProps` (≈ `React.HTMLAttributes<HTMLDivElement>`).
26
+
27
+ ### Обязательные props
28
+
29
+ | Prop | Тип | Описание |
30
+ |---|---|---|
31
+ | `type` | `EBodyPageType` | `FIRST` — контент оборачивается в `Island` (карточка с белым фоном и тенью). `SECOND` — контент без карточки. |
32
+ | `children` | `React.ReactNode` | Контент тела страницы. |
33
+
34
+ ### Опциональные props
35
+
36
+ | Prop | Тип | По умолчанию | Описание |
37
+ |---|---|---|---|
38
+ | `verticalMargin` | `EBodyPageVerticalMargin` | `LARGE` | Вертикальные отступы: `LARGE` = 24px (16px на узких экранах), `SMALL` = 16px (8px на узких). В LightBox используется `SMALL`. |
39
+ | `size` | `EComponentSize` | — | Размер острова (Island). **Доступен только для `type=FIRST`**; для `type=SECOND` типизирован как `never`. |
40
+
41
+ ### Ограничения по типам
42
+
43
+ - `size` доступен только при `type=FIRST` (в `SECOND` контент не оборачивается в Island, поэтому `size?: never`).
44
+
45
+ ---
46
+
47
+ ## Дизайн-токены
48
+
49
+ Собственных CSS-переменных нет. Внешний вид карточки наследуется от `Island` (`EIslandType.TYPE_1`).
50
+ Вертикальные отступы заданы фиксированными значениями в `styles/BodyPage.module.less`
51
+ (24/16px и адаптив 16/8px на `@screen-sm-max`).
52
+
53
+ ---
54
+
55
+ ## Инварианты
56
+
57
+ - `forwardRef<HTMLDivElement>` — не убирать. Ref всегда указывает на корневой элемент
58
+ (Island для FIRST, Body для SECOND).
59
+ - `displayName = "BodyPage"` — не менять.
60
+ - Discriminated union по `type` и значения `EBodyPageType` / `EBodyPageVerticalMargin` — часть
61
+ публичного API, менять нельзя.
62
+ - Экспорты `BodyPage`, `IBodyPageTypeFirstProps`, `IBodyPageTypeSecondProps` идут в barrel
63
+ `src/components/Page/index.ts` — сохранять.
64
+ - `verticalMargin` по умолчанию `LARGE` — менять дефолт нельзя без обсуждения.
65
+
66
+ ---
67
+
68
+ ## Accessibility
69
+
70
+ Особого a11y-поведения нет — это структурный контейнер. Семантику и aria-атрибуты задаёт
71
+ потребитель через проброшенные HTML-атрибуты (`...rest` уходит в `Body`).
72
+
73
+ ---
74
+
75
+ ## Связанные компоненты
76
+
77
+ - `Page` — родитель; `BodyPage` доступен как `Page.Body`.
78
+ - `HeaderPage` (`Page.Header`), `FooterPage` (`Page.Footer`) — соседи внутри `Page`.
79
+ - `Body` — внутренний контейнер контента (рендерится в обоих типах).
80
+ - `Island` — карточка-обёртка для `type=FIRST`.
81
+ - `LightBox` — типичное место использования с `verticalMargin=SMALL`.
82
+
83
+ ---
84
+
85
+ ## Stories
86
+
87
+ Основные истории: `stories/Page/BodyPage/BodyPage.stories.tsx`
88
+ Файлы примеров: `stories/Page/BodyPage/examples/`
89
+
90
+ | Story | Example file | Что демонстрирует |
91
+ |---|---|---|
92
+ | `Playground` | `Playground.tsx` | Интерактивный контроль `type` и `verticalMargin` |
93
+ | `Default` | `Default.tsx` | Минимальный пример (FIRST, контент в карточке) |
94
+ | `Types` | `Types.tsx` | Сравнение типов FIRST и SECOND |
95
+ | `VerticalMargins` | `VerticalMargins.tsx` | Отступы LARGE (24px) и SMALL (16px) |
96
+ | `VisualTests` | `VisualTests.tsx` | Матрица type × verticalMargin для скриншот-регрессии |
97
+
98
+ ---
99
+
100
+ ## История изменений
101
+
102
+ | Дата | Изменение |
103
+ |---|---|
104
+ | 2026-06-18 | Создан документ. AI-рефакторинг (clsx order, JSDoc), unit-тесты, миграция stories на modern pattern. |
105
+
106
+ ---
107
+
108
+ ## Примеры использования
109
+
110
+ ### Default
111
+
112
+ `stories/Page/BodyPage/examples/Default.tsx`
113
+
114
+ ```tsx
115
+ import React from "react";
116
+ import { Page, EBodyPageType, Text, ETextSize, EFontType } from "@sberbusiness/triplex-next";
117
+
118
+ export const Default = () => (
119
+ <Page.Body type={EBodyPageType.FIRST}>
120
+ <Text tag="div" size={ETextSize.B2} type={EFontType.PRIMARY}>
121
+ BodyPage типа FIRST оборачивает контент в Island (карточку) с белым фоном и тенью.
122
+ </Text>
123
+ </Page.Body>
124
+ );
125
+ ```
126
+
127
+ ### Types
128
+
129
+ `stories/Page/BodyPage/examples/Types.tsx`
130
+
131
+ ```tsx
132
+ import React from "react";
133
+ import { Page, EBodyPageType, Text, ETextSize, EFontType } from "@sberbusiness/triplex-next";
134
+
135
+ export const Types = () => (
136
+ <div style={{ display: "flex", flexDirection: "column", gap: "24px" }}>
137
+ <div>
138
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: 700 }}>FIRST</div>
139
+ <Page.Body type={EBodyPageType.FIRST}>
140
+ <Text tag="div" size={ETextSize.B2} type={EFontType.PRIMARY}>
141
+ Тип FIRST оборачивает контент в Island (карточку) с белым фоном и тенью.
142
+ </Text>
143
+ </Page.Body>
144
+ </div>
145
+ <div>
146
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: 700 }}>SECOND</div>
147
+ <Page.Body type={EBodyPageType.SECOND}>
148
+ <Text tag="div" size={ETextSize.B2} type={EFontType.PRIMARY}>
149
+ Тип SECOND отображает контент без карточки.
150
+ </Text>
151
+ </Page.Body>
152
+ </div>
153
+ </div>
154
+ );
155
+ ```
156
+
157
+ ### VerticalMargins
158
+
159
+ `stories/Page/BodyPage/examples/VerticalMargins.tsx`
160
+
161
+ ```tsx
162
+ import React from "react";
163
+ import { Page, EBodyPageType, EBodyPageVerticalMargin, Text, ETextSize, EFontType } from "@sberbusiness/triplex-next";
164
+
165
+ export const VerticalMargins = () => (
166
+ <div style={{ display: "flex", flexDirection: "column", gap: "24px" }}>
167
+ <div>
168
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: 700 }}>LARGE (24px)</div>
169
+ <Page.Body type={EBodyPageType.FIRST} verticalMargin={EBodyPageVerticalMargin.LARGE}>
170
+ <Text tag="div" size={ETextSize.B2} type={EFontType.PRIMARY}>
171
+ Отступы сверху и снизу 24px (значение по умолчанию).
172
+ </Text>
173
+ </Page.Body>
174
+ </div>
175
+ <div>
176
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: 700 }}>SMALL (16px)</div>
177
+ <Page.Body type={EBodyPageType.FIRST} verticalMargin={EBodyPageVerticalMargin.SMALL}>
178
+ <Text tag="div" size={ETextSize.B2} type={EFontType.PRIMARY}>
179
+ Уменьшенные отступы сверху и снизу 16px. Используется в LightBox.
180
+ </Text>
181
+ </Page.Body>
182
+ </div>
183
+ </div>
184
+ );
185
+ ```
186
+
@@ -1,8 +1,6 @@
1
1
  ---
2
2
  component: Button
3
3
  category: Buttons
4
- figma-node: "1-328"
5
- figma-file: "https://www.figma.com/design/MeAqaaLV9acAgoBOkZC9C2/-W--LIB-DSN-01-Components-DCB-Next-v.1.3.0"
6
4
  related: [ButtonIcon, ButtonDropdown, ButtonDropdownExtended]
7
5
  tokens:
8
6
  - --triplex-next-Button-General_Background_Default
@@ -64,42 +62,17 @@ version: "1.0"
64
62
 
65
63
  ---
66
64
 
67
- ## Файловая структура
68
-
69
- ```text
70
- src/components/Button/
71
- ├── Button.tsx # Основной компонент, union type TButtonProps
72
- ├── ButtonBase.tsx # Базовый <button>, extends HTML attrs
73
- ├── ButtonIcon.tsx # Иконочная кнопка (squircle / circle)
74
- ├── ButtonDropdown.tsx # Кнопка с выпадающим меню
75
- ├── ButtonDropdownExtended.tsx # Контейнер с кастомным dropdown
76
- ├── enums.ts # EButtonTheme, EButtonDotsTheme, EButtonIconShape
77
- ├── index.ts # Barrel exports
78
- └── styles/
79
- ├── Button.module.less # Размеры, block, loading, base layout
80
- ├── ButtonGeneral.module.less # Тема GENERAL
81
- ├── ButtonSecondary.module.less
82
- ├── ButtonSecondaryLight.module.less
83
- ├── ButtonDanger.module.less
84
- ├── ButtonLink.module.less # Тема LINK — нет min-width, нет bg
85
- ├── ButtonIcon.module.less # Стили ButtonIcon
86
- ├── ButtonDropdown.module.less
87
- └── ButtonDropdownExtended.module.less
88
- ```
89
-
90
- ---
91
-
92
65
  ## Варианты и props
93
66
 
94
67
  ### Темы (`EButtonTheme`)
95
68
 
96
- | Значение | Описание | Поддерживает loading/icon/block |
97
- |---|---|---|
98
- | `GENERAL` | Основной стиль (primary) | Да |
99
- | `SECONDARY` | Вторичный | Да |
100
- | `SECONDARY_LIGHT` | Вторичный светлый (для тёмных фонов) | Да |
101
- | `DANGER` | Деструктивное действие | Да |
102
- | `LINK` | Текстовая ссылка | Нет — `loading`, `icon`, `block` запрещены через `never` |
69
+ | Значение | Описание |
70
+ |---|---|
71
+ | `GENERAL` | Основной стиль (primary) |
72
+ | `SECONDARY` | Вторичный |
73
+ | `SECONDARY_LIGHT` | Вторичный светлый (для тёмных фонов) |
74
+ | `DANGER` | Деструктивное действие |
75
+ | `LINK` | Текстовая ссылка |
103
76
 
104
77
  ### Размеры (`EComponentSize`)
105
78
 
@@ -109,17 +82,22 @@ src/components/Button/
109
82
  | `MD` | 40px | 0 20px | 8px | 14px |
110
83
  | `LG` | 56px | 0 24px | 10px | 16px |
111
84
 
112
- ### Props компонента `Button`
85
+ ### Обязательные props (`Button`)
86
+
87
+ | Prop | Тип | Описание |
88
+ |---|---|---|
89
+ | `theme` | `EButtonTheme` | Визуальный стиль |
90
+ | `size` | `EComponentSize` | Размер |
91
+
92
+ ### Опциональные props (`Button`)
113
93
 
114
- | Prop | Тип | Обязателен | Описание |
94
+ | Prop | Тип | По умолчанию | Описание |
115
95
  |---|---|---|---|
116
- | `theme` | `EButtonTheme` | Да | Визуальный стиль |
117
- | `size` | `EComponentSize` | Да | Размер |
118
- | `block` | `boolean` | Нет | Полноширинный режим (только не-LINK темы) |
119
- | `loading` | `boolean` | Нет | Состояние загрузки: скрывает контент, показывает спиннер (только не-LINK темы) |
120
- | `icon` | `React.ReactElement` | Нет | Иконка слева от текста; если без `children` — кнопка становится квадратной |
121
- | `children` | `React.ReactNode` | Нет | Текст кнопки |
122
- | `...HTMLButtonAttributes` | — | — | Все стандартные атрибуты `<button>` |
96
+ | `block` | `boolean` | `false` | Полноширинный режим |
97
+ | `loading` | `boolean` | `false` | Состояние загрузки: скрывает контент через `visibility: hidden`, показывает спиннер, ставит `tabIndex={-1}` |
98
+ | `icon` | `React.ReactElement` | — | Иконка слева от текста; если без `children` — кнопка становится квадратной (icon-only) |
99
+ | `children` | `React.ReactNode` | — | Текст кнопки |
100
+ | `...HTMLButtonAttributes` | — | — | Все стандартные атрибуты `<button>`, включая `aria-expanded` (см. Accessibility) |
123
101
 
124
102
  ### Props компонента `ButtonIcon`
125
103
 
@@ -129,45 +107,11 @@ src/components/Button/
129
107
  | `active` | `boolean` | `false` | Визуальное активное состояние |
130
108
  | `...HTMLButtonAttributes` | — | — | Включая `disabled` |
131
109
 
132
- ---
133
-
134
- ## Ключевые особенности реализации
135
-
136
- ### Иконка без текста
137
- Если передан `icon` без `children` — добавляется класс `.icon`, который делает кнопку квадратной:
138
- ```tsx
139
- // Button.tsx — логика определения icon-only режима
140
- const iconOnly = Boolean(icon) && !children;
141
- <button className={clsx(styles.button, { [styles.icon]: iconOnly })}>
142
- ```
143
-
144
- ### Состояние загрузки
145
- - `loading` скрывает контент через `visibility: hidden` (не `display: none`) — сохраняет размеры,
146
- - устанавливает `tabIndex={-1}` — кнопка недоступна с клавиатуры
147
- - Тема лоадера: `SECONDARY` и `SECONDARY_LIGHT` → `ELoaderSmallTheme.BRAND`, остальные → `ELoaderSmallTheme.NEUTRAL`
110
+ ### Ограничения по темам
148
111
 
149
- ### `aria-expanded` управляет визуалом
150
- Состояние "раскрыто" (например, когда кнопка открывает dropdown) управляется через `aria-expanded`:
151
- ```tsx
152
- <Button aria-expanded={true} ...> → добавляет CSS-классы .expanded и .active
153
- ```
154
- Отдельного prop `expanded` нет — используй стандартный HTML-атрибут.
155
-
156
- ### Тема LINK
157
- - Нет `min-width`
158
- - `background: transparent`
159
- - `padding` по вертикали отличается от других тем (меньше)
160
- - `block`, `loading`, `icon` — тип `never` в интерфейсе, TypeScript запрещает передачу
161
-
162
- ---
163
-
164
- ## Accessibility
165
-
166
- - Нативный `<button>` — полная поддержка клавиатуры (Tab, Enter, Space) без дополнительных атрибутов.
167
- - **`ButtonIcon`**: иконочная кнопка без видимого текста — потребитель **обязан** передать `aria-label` через `...rest`. Библиотека мультиязычная, текст не хардкодится.
168
- - **`loading`**: устанавливает `tabIndex={-1}` — кнопка исключается из tab-порядка. Если нужна доступность состояния загрузки для скринридеров, потребитель добавляет `aria-busy="true"` через props.
169
- - **`aria-expanded`**: управляет визуальным состоянием "раскрыто" (добавляет CSS-классы `.expanded`, `.active`). Передаётся потребителем при использовании кнопки как триггера dropdown.
170
- - Фокус-стиль через `:focus-visible` — виден только при клавиатурной навигации.
112
+ - `block`, `loading`, `icon` недоступны для темы `LINK` — тип `never` в интерфейсе, TypeScript запрещает передачу.
113
+ - Тема `LINK` не имеет `min-width`, фона (`background: transparent`) и использует уменьшенный вертикальный `padding`.
114
+ - Тема лоадера (`loading`) для `SECONDARY` и `SECONDARY_LIGHT` — `ELoaderSmallTheme.BRAND`, для остальных — `ELoaderSmallTheme.NEUTRAL`.
171
115
 
172
116
  ---
173
117
 
@@ -175,12 +119,12 @@ const iconOnly = Boolean(icon) && !children;
175
119
 
176
120
  Паттерн: `--triplex-next-Button-{Theme}_{Property}_{State}`
177
121
 
178
- Каждая тема (кроме LINK) имеет полный набор:
122
+ Каждая тема (кроме `LINK`) имеет полный набор:
179
123
  - `_Background_Default/Hover/Active/Disabled`
180
124
  - `_Color_Default/Hover/Active/Disabled`
181
125
  - `_Shadow_Focus`
182
126
 
183
- Тема LINK — только `_Color_*` и `_Shadow_Focus` (нет фона).
127
+ Тема `LINK` — только `_Color_*` и `_Shadow_Focus` (нет фона).
184
128
 
185
129
  При добавлении нового визуального состояния нужен новый токен в `src/generated/themesCssVariables.css`.
186
130
 
@@ -192,7 +136,17 @@ const iconOnly = Boolean(icon) && !children;
192
136
  - **`TButtonProps` union type** — должен включать все тематические интерфейсы. При добавлении новой темы — добавь интерфейс в union.
193
137
  - **`EButtonTheme` значения** — не переименовывать. Это публичное API.
194
138
  - **`EComponentSize`** — общий enum из `src/enums/EComponentSize`, не делай локальный аналог.
195
- - **Размеры (28px/40px/56px)** — не менять без согласования с дизайнером, другие компоненты ориентируются на эти размеры.
139
+ - **Размеры (28px / 40px / 56px)** — не менять без согласования с дизайнером, другие компоненты ориентируются на эти размеры.
140
+
141
+ ---
142
+
143
+ ## Accessibility
144
+
145
+ - Нативный `<button>` — полная поддержка клавиатуры (Tab, Enter, Space) без дополнительных атрибутов.
146
+ - **`ButtonIcon`**: иконочная кнопка без видимого текста — потребитель **обязан** передать `aria-label` через `...rest`. Библиотека мультиязычная, текст не хардкодится.
147
+ - **`loading`**: устанавливает `tabIndex={-1}` — кнопка исключается из tab-порядка. Если нужна доступность состояния загрузки для скринридеров, потребитель добавляет `aria-busy="true"` через props.
148
+ - **`aria-expanded`**: управляет визуальным состоянием «раскрыто» (добавляет CSS-классы `.expanded`, `.active`). Передаётся потребителем при использовании кнопки как триггера dropdown. Отдельного prop `expanded` нет — используй стандартный HTML-атрибут.
149
+ - Фокус-стиль через `:focus-visible` — виден только при клавиатурной навигации.
196
150
 
197
151
  ---
198
152
 
@@ -208,7 +162,7 @@ const iconOnly = Boolean(icon) && !children;
208
162
 
209
163
  ## Stories
210
164
 
211
- `stories/Buttons/Button.stories.tsx`
165
+ Основные истории: `stories/Buttons/Button.stories.tsx`
212
166
  Файлы примеров: `stories/Buttons/examples/Button/`
213
167
 
214
168
  | Story | Example file | Что демонстрирует |
@@ -219,7 +173,9 @@ const iconOnly = Boolean(icon) && !children;
219
173
  | `Sizes` | `SizesExample.tsx` | SM / MD / LG для всех тем |
220
174
  | `Themes` | `ThemesExample.tsx` | Все пять тем |
221
175
  | `WithIcon` | `WithIconExample.tsx` | Icon-only и text+icon |
176
+ | `WithNotificationIcon` | `WithNotificationIconExample.tsx` | Иконка с индикатором уведомления |
222
177
  | `BlockMode` | `BlockModeExample.tsx` | Полноширинный режим |
178
+ | `TextWithIcon` | `TextWithIconExample.tsx` | Текст вместе с иконкой |
223
179
 
224
180
  ---
225
181
 
@@ -228,6 +184,7 @@ const iconOnly = Boolean(icon) && !children;
228
184
  | Дата | Изменение |
229
185
  |---|---|
230
186
  | 2026-03-31 | Создан документ (пилот AI-Ready) |
187
+ | 2026-04-27 | Приведён в соответствие с `docs/ai/template-ai.md`: убраны секции «Файловая структура» и «Ключевые особенности реализации», переструктурированы props, добавлена колонка `Example file` в таблице Stories |
231
188
 
232
189
  ---
233
190
 
@@ -475,6 +432,27 @@ export const WithIconExample = () => (
475
432
  );
476
433
  ```
477
434
 
435
+ ### WithNotificationIcon
436
+
437
+ `stories/Buttons/examples/Button/WithNotificationIconExample.tsx`
438
+
439
+ ```tsx
440
+ import React from "react";
441
+ import { Badge, Button, EButtonTheme, EComponentSize } from "@sberbusiness/triplex-next";
442
+ import { BellStrokeNavIcon20 } from "@sberbusiness/icons-next";
443
+
444
+ export const WithNotificationIconExample = () => (
445
+ <div style={{ position: "relative", display: "inline-flex" }}>
446
+ <Button
447
+ icon={<BellStrokeNavIcon20 paletteIndex={0} />}
448
+ size={EComponentSize.MD}
449
+ theme={EButtonTheme.SECONDARY}
450
+ />
451
+ <Badge.Dot size={EComponentSize.MD} style={{ position: "absolute", top: 4, right: 4 }} />
452
+ </div>
453
+ );
454
+ ```
455
+
478
456
  ### BlockMode
479
457
 
480
458
  `stories/Buttons/examples/Button/BlockModeExample.tsx`
@@ -508,3 +486,30 @@ export const BlockModeExample = () => (
508
486
  );
509
487
  ```
510
488
 
489
+ ### TextWithIcon
490
+
491
+ `stories/Buttons/examples/Button/TextWithIconExample.tsx`
492
+
493
+ ```tsx
494
+ import React from "react";
495
+ import { DefaulticonStrokePrdIcon16, DefaulticonStrokePrdIcon20 } from "@sberbusiness/icons-next";
496
+ import { Button, EButtonTheme, EComponentSize } from "@sberbusiness/triplex-next";
497
+
498
+ export const TextWithIconExample = () => (
499
+ <div style={{ display: "flex", alignItems: "flex-start", gap: 16 }}>
500
+ <Button theme={EButtonTheme.LINK} size={EComponentSize.SM}>
501
+ <DefaulticonStrokePrdIcon16 paletteIndex={5} />
502
+ <span style={{ marginLeft: 8 }}>Button text</span>
503
+ </Button>
504
+ <Button theme={EButtonTheme.LINK} size={EComponentSize.MD}>
505
+ <DefaulticonStrokePrdIcon20 paletteIndex={5} />
506
+ <span style={{ marginLeft: 8 }}>Button text</span>
507
+ </Button>
508
+ <Button theme={EButtonTheme.LINK} size={EComponentSize.LG}>
509
+ <DefaulticonStrokePrdIcon20 paletteIndex={5} />
510
+ <span style={{ marginLeft: 8 }}>Button text</span>
511
+ </Button>
512
+ </div>
513
+ );
514
+ ```
515
+
@@ -1,8 +1,6 @@
1
1
  ---
2
2
  component: ButtonDropdown
3
3
  category: Buttons
4
- figma-node: ""
5
- figma-file: ""
6
4
  related: [Button, ButtonDropdownExtended, Dropdown]
7
5
  tokens: []
8
6
  stories: stories/Buttons/ButtonDropdown.stories.tsx
@@ -13,25 +11,10 @@ version: "1.0"
13
11
 
14
12
  ## Назначение
15
13
 
16
- Кнопка-триггер с выпадающим списком действий.
17
- Поддерживает два визуальных режима: обычная кнопка (`EButtonTheme`) и dots-вариант (`EButtonDotsTheme`).
14
+ Кнопка-триггер с выпадающим списком действий. Поддерживает два визуальных режима: обычная кнопка (`EButtonTheme`) и dots-вариант (`EButtonDotsTheme`).
18
15
 
19
16
  Используй когда: нужно сгруппировать несколько действий под один триггер и дать выбор опции из списка.
20
- Не используй когда: нужна одиночная кнопка без списка - используй `Button`.
21
-
22
- ---
23
-
24
- ## Файловая структура
25
-
26
- ```text
27
- src/components/Button/
28
- ├── ButtonDropdown.tsx # Основной компонент (forwardRef, рендер button + dropdown)
29
- ├── ButtonDropdownExtended.tsx # Базовый контейнер с контролем opened/close и outside click
30
- ├── styles/
31
- │ ├── ButtonDropdown.module.less # Стили caret, active-состояния, block-режима
32
- │ └── ButtonDropdownExtended.module.less
33
- └── index.ts # Публичный экспорт ButtonDropdown
34
- ```
17
+ Не используй когда: нужна одиночная кнопка без списка — используй `Button`.
35
18
 
36
19
  ---
37
20
 
@@ -49,18 +32,13 @@ src/components/Button/
49
32
 
50
33
  | Prop | Тип | По умолчанию | Описание |
51
34
  |---|---|---|---|
52
- | `children` | `React.ReactNode` | `undefined` | Текст/контент кнопки (используется также в mobile header) |
53
- | `selected` | `IButtonDropdownOption` | `undefined` | Предвыбранная опция для подсветки в списке |
35
+ | `children` | `React.ReactNode` | — | Текст/контент кнопки (используется также в mobile header) |
36
+ | `selected` | `IButtonDropdownOption` | — | Предвыбранная опция для подсветки в списке |
54
37
  | `disabled` | `boolean` | `false` | Блокирует кнопку и открытие списка |
55
- | `buttonAttributes` | `React.ButtonHTMLAttributes<HTMLButtonElement>` | `undefined` | Дополнительные HTML-атрибуты нативной кнопки |
56
- | `block` | `boolean` | `false` | Доступен только для non-dots тем |
38
+ | `buttonAttributes` | `React.ButtonHTMLAttributes<HTMLButtonElement>` | — | Дополнительные HTML-атрибуты нативной кнопки (включая `aria-label`) |
39
+ | `block` | `boolean` | `false` | Полноширинный режим (только для non-dots тем) |
57
40
  | `className` | `string` | — | Дополнительный CSS-класс контейнера |
58
- | `...rest` | `React.HTMLAttributes<HTMLDivElement>` | — | Атрибуты корневого `div` компонента |
59
-
60
- ### Ограничения по темам
61
-
62
- - Для `EButtonDotsTheme` prop `block` запрещен типизацией (`never`).
63
- - Dots-режим использует иконку `Dotshorizontal*` вместо текстового контента кнопки.
41
+ | `...rest` | `React.HTMLAttributes<HTMLDivElement>` | — | Атрибуты корневого `<div>` компонента |
64
42
 
65
43
  ### Структура опции (`IButtonDropdownOption`)
66
44
 
@@ -69,50 +47,19 @@ src/components/Button/
69
47
  | `id` | `string` | Уникальный идентификатор опции |
70
48
  | `label` | `React.ReactNode` | Видимый контент пункта меню |
71
49
  | `onSelect` | `() => void` | Callback выбора опции |
72
- | `...rest` | `IDropdownListItemProps` (без ряда запрещенных полей) | Остальные настройки `DropdownList.Item` |
73
-
74
- ---
75
-
76
- ## Ключевые особенности реализации
77
-
78
- ### Композиция через `ButtonDropdownExtended`
79
-
80
- - `ButtonDropdown` делегирует управление opened-state и close-on-outside в `ButtonDropdownExtended`.
81
- - Передает две render-функции: `renderButton` и `renderDropdown`.
82
-
83
- ### Desktop + mobile dropdown
84
-
85
- - Desktop: рендерится `DropdownList` с `DropdownList.Item`.
86
- - Mobile: через `mobileViewProps` рендерится `DropdownMobileHeader` + `DropdownMobileList`.
87
- - После выбора любого пункта вызывается `option.onSelect?.()` и dropdown закрывается.
50
+ | `...rest` | `IDropdownListItemProps` (без ряда запрещённых полей) | Остальные настройки `DropdownList.Item` |
88
51
 
89
- ### Управление клавиатурой
90
-
91
- - На кнопке перехватываются `Space`, `ArrowUp`, `ArrowDown` с `preventDefault`.
92
- - `ArrowUp/ArrowDown` открывают dropdown, если он закрыт.
93
- - В `ButtonDropdownExtended` dropdown закрывается по `Escape` и (из-за `closeOnTab`) по `Tab`.
94
-
95
- ### ARIA-связка trigger/list
96
-
97
- - Кнопка получает `aria-haspopup="menu"`, `aria-expanded`, `aria-controls`.
98
- - `aria-controls` и `DropdownList` связываются через `instanceId` (`uniqueId()`).
99
- - `aria-activedescendant` синхронизируется через `DropdownListContext`.
100
-
101
- ---
102
-
103
- ## Accessibility
52
+ ### Ограничения по темам
104
53
 
105
- - Триггер строится на `Button` (нативный `button`), базовая keyboard-навигация сохраняется.
106
- - Компонент не хардкодит aria-тексты: при необходимости `aria-label` нужно передавать через `buttonAttributes`.
107
- - На mobile-заголовке dropdown отображается `children` триггера; если в продукте нужен локализованный отдельный заголовок, его нужно задавать на уровне композиции (через `ButtonDropdownExtended`).
108
- - Для корректной доступности меню потребитель должен передавать понятные `label` для `options`.
54
+ - Для `EButtonDotsTheme` prop `block` запрещён типизацией (`never`).
55
+ - Dots-режим использует иконку `Dotshorizontal*` вместо текстового контента кнопки.
56
+ - Desktop рендерит `DropdownList` с `DropdownList.Item`; mobile через `mobileViewProps` рендерит `DropdownMobileHeader` + `DropdownMobileList`.
109
57
 
110
58
  ---
111
59
 
112
60
  ## Дизайн-токены
113
61
 
114
- `ButtonDropdown` не использует собственных CSS color-токенов в `ButtonDropdown.module.less`.
115
- Визуальные токены берутся из `Button` и `Dropdown`, которые рендерятся внутри.
62
+ `ButtonDropdown` не использует собственных CSS color-токенов. Визуальные токены берутся из `Button` и `Dropdown`, которые рендерятся внутри.
116
63
 
117
64
  ---
118
65
 
@@ -126,17 +73,27 @@ src/components/Button/
126
73
 
127
74
  ---
128
75
 
76
+ ## Accessibility
77
+
78
+ - Триггер строится на `Button` (нативный `<button>`), базовая keyboard-навигация сохраняется.
79
+ - Кнопка получает `aria-haspopup="menu"`, `aria-expanded`, `aria-controls`. Связка trigger/list осуществляется через `instanceId` (`uniqueId()`); `aria-activedescendant` синхронизируется через `DropdownListContext`.
80
+ - На кнопке перехватываются `Space`, `ArrowUp`, `ArrowDown` с `preventDefault`. `ArrowUp` / `ArrowDown` открывают dropdown, если он закрыт. Dropdown закрывается по `Escape` и (из-за `closeOnTab`) по `Tab`.
81
+ - Компонент **не хардкодит** aria-тексты: при необходимости `aria-label` нужно передавать через `buttonAttributes`. Для корректной доступности меню потребитель должен передавать понятные `label` для `options`.
82
+ - На mobile-заголовке dropdown отображается `children` триггера; если в продукте нужен локализованный отдельный заголовок, его нужно задавать на уровне композиции (через `ButtonDropdownExtended`).
83
+
84
+ ---
85
+
129
86
  ## Связанные компоненты
130
87
 
131
- - `Button` (`src/components/Button/Button.tsx`) - базовый триггер, на котором построен `ButtonDropdown`.
132
- - `ButtonDropdownExtended` (`src/components/Button/ButtonDropdownExtended.tsx`) - управляет открытием/закрытием dropdown.
133
- - `Dropdown` (`src/components/Dropdown`) - контейнер и пункты списка для desktop/mobile представления.
88
+ - `Button` (`src/components/Button/Button.tsx`) — базовый триггер, на котором построен `ButtonDropdown`.
89
+ - `ButtonDropdownExtended` (`src/components/Button/ButtonDropdownExtended.tsx`) — управляет открытием/закрытием dropdown через render-функции `renderButton` и `renderDropdown`.
90
+ - `Dropdown` (`src/components/Dropdown`) — контейнер и пункты списка для desktop/mobile представления.
134
91
 
135
92
  ---
136
93
 
137
94
  ## Stories
138
95
 
139
- `stories/Buttons/ButtonDropdown.stories.tsx`
96
+ Основные истории: `stories/Buttons/ButtonDropdown.stories.tsx`
140
97
  Файлы примеров: `stories/Buttons/examples/ButtonDropdown/`
141
98
 
142
99
  | Story | Example file | Что демонстрирует |
@@ -157,6 +114,7 @@ src/components/Button/
157
114
  | Дата | Изменение |
158
115
  |---|---|
159
116
  | 2026-04-15 | Создан документ AI-ready для `ButtonDropdown`. |
117
+ | 2026-04-27 | Приведён в соответствие с `docs/ai/template-ai.md`: убраны секции «Файловая структура» и «Ключевые особенности реализации», их содержимое перенесено в `Ограничения по темам` и `Accessibility`, добавлена колонка `Example file`. |
160
118
 
161
119
  ---
162
120