@sberbusiness/triplex-next-mcp-server 0.24.0-beta2 → 1.40.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.
- package/README.md +10 -0
- package/bundle/components/AlertContext.md +268 -0
- package/bundle/components/BodyPage.md +186 -0
- package/bundle/components/Button.md +93 -85
- package/bundle/components/ButtonDropdown.md +36 -70
- package/bundle/components/ButtonDropdownExtended.md +422 -0
- package/bundle/components/ButtonIcon.md +41 -48
- package/bundle/components/Caption.md +291 -0
- package/bundle/components/Checkbox.md +43 -122
- package/bundle/components/CheckboxXGroup.md +109 -46
- package/bundle/components/CheckboxYGroup.md +77 -49
- package/bundle/components/CodeText.md +197 -0
- package/bundle/components/Col.md +311 -162
- package/bundle/components/CollapsibleTree.md +584 -0
- package/bundle/components/CollapsibleTreeExtended.md +422 -0
- package/bundle/components/Dropdown.md +748 -346
- package/bundle/components/EmptyView.md +14 -15
- package/bundle/components/FooterPage.md +203 -0
- package/bundle/components/Gap.md +115 -57
- package/bundle/components/HeaderPage.md +226 -0
- package/bundle/components/HelpBox.md +45 -84
- package/bundle/components/ImageGallery.md +423 -0
- package/bundle/components/ImageGalleryExtended.md +606 -0
- package/bundle/components/LightBox.md +2252 -233
- package/bundle/components/List.md +216 -183
- package/bundle/components/ListActionItem.md +157 -0
- package/bundle/components/ListItem.md +276 -0
- package/bundle/components/ListItemControlsButton.md +103 -0
- package/bundle/components/ListItemControlsButtonDropdown.md +107 -0
- package/bundle/components/ListItemSelectable.md +99 -0
- package/bundle/components/ListItemTable.md +390 -0
- package/bundle/components/ListMaster.md +876 -156
- package/bundle/components/ListMasterFooter.md +80 -0
- package/bundle/components/ListMasterHeader.md +86 -0
- package/bundle/components/ListSortable.md +153 -0
- package/bundle/components/ListSortableItem.md +175 -0
- package/bundle/components/ListTableItem.md +391 -0
- package/bundle/components/ModalWindow.md +427 -237
- package/bundle/components/MonthYearField.md +145 -109
- package/bundle/components/Notification.md +625 -155
- package/bundle/components/Overlay.md +271 -170
- package/bundle/components/Page.md +284 -186
- package/bundle/components/Pagination.md +206 -229
- package/bundle/components/Portal.md +131 -67
- package/bundle/components/Row.md +171 -69
- package/bundle/components/Text.md +320 -0
- package/bundle/components/TextField.md +364 -97
- package/bundle/components/Title.md +296 -0
- package/bundle/guides/codestyle.md +38 -11
- package/bundle/guides/commits.md +119 -26
- package/bundle/guides/context.md +34 -30
- package/bundle/guides/stories.md +110 -40
- package/bundle/guides/template.md +3 -7
- package/bundle/guides/tests.md +48 -2
- package/bundle/manifest.json +275 -30
- package/bundle/release-notes/1.26.0.mdx +33 -2
- package/bundle/release-notes/1.27.0.mdx +43 -0
- package/bundle/release-notes/1.28.0.mdx +25 -0
- package/bundle/release-notes/1.29.0.mdx +47 -0
- package/bundle/release-notes/1.30.0.mdx +56 -0
- package/bundle/release-notes/1.31.0.mdx +52 -0
- package/bundle/release-notes/1.32.0.mdx +35 -0
- package/bundle/release-notes/1.33.0.mdx +61 -0
- package/bundle/release-notes/1.34.0.mdx +35 -0
- package/bundle/release-notes/1.35.0.mdx +21 -0
- package/bundle/release-notes/1.35.1.mdx +11 -0
- package/bundle/release-notes/1.36.0.mdx +21 -0
- package/bundle/release-notes/1.37.0.mdx +71 -0
- package/bundle/release-notes/1.38.0.mdx +36 -0
- package/bundle/release-notes/1.39.0.mdx +66 -0
- package/bundle/release-notes/1.40.0.mdx +27 -0
- package/bundle/release-notes/1.41.0.mdx +7 -0
- package/package.json +1 -1
|
@@ -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
|
-
| Значение | Описание |
|
|
97
|
-
|
|
98
|
-
| `GENERAL` | Основной стиль (primary) |
|
|
99
|
-
| `SECONDARY` | Вторичный |
|
|
100
|
-
| `SECONDARY_LIGHT` | Вторичный светлый (для тёмных фонов) |
|
|
101
|
-
| `DANGER` | Деструктивное действие |
|
|
102
|
-
| `LINK` | Текстовая ссылка |
|
|
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
|
-
###
|
|
85
|
+
### Обязательные props (`Button`)
|
|
86
|
+
|
|
87
|
+
| Prop | Тип | Описание |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| `theme` | `EButtonTheme` | Визуальный стиль |
|
|
90
|
+
|
|
91
|
+
### Опциональные props (`Button`)
|
|
113
92
|
|
|
114
|
-
| Prop | Тип |
|
|
93
|
+
| Prop | Тип | По умолчанию | Описание |
|
|
115
94
|
|---|---|---|---|
|
|
116
|
-
| `
|
|
117
|
-
| `
|
|
118
|
-
| `
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
121
|
-
| `
|
|
122
|
-
| `...HTMLButtonAttributes` | — | — | Все стандартные атрибуты `<button>` |
|
|
95
|
+
| `size` | `EComponentSize` | `MD` | Размер |
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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`). Раскрытым считается только значение `true` или `"true"` — строковое `"false"` трактуется как закрытое состояние (тип атрибута `Booleanish`). Передаётся потребителем при использовании кнопки как триггера 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,10 @@ 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 |
|
|
188
|
+
| 2026-07-15 | Добавлен `vertical-align: middle` корневому классу `.button` — icon-only кнопка (`icon` без `children`) смещалась по вертикали относительно соседней текстовой кнопки из-за baseline-выравнивания inline-элементов; `middle` также центрирует кнопку относительно строки инлайн-текста |
|
|
189
|
+
| 2026-07-28 | AI-рефакторинг (TRI-13): codestyle-чистка `Button.tsx` (JSDoc компонента, поясняющие комментарии, эквивалентные упрощения в `clsx`), unit-тесты расширены с 10 до 24 кейсов; публичный API и поведение не изменены |
|
|
190
|
+
| 2026-07-28 | Правки по ревью PR #481 (TRI-13): исправлена трактовка строкового `aria-expanded="false"` (больше не считается раскрытым состоянием); `size` стал опциональным с default `EComponentSize.MD` (типы приведены в соответствие с реализацией); `ButtonBase` убран из barrel-экспорта `index.ts` — импорт только по прямому пути `components/Button/ButtonBase` |
|
|
231
191
|
|
|
232
192
|
---
|
|
233
193
|
|
|
@@ -475,6 +435,27 @@ export const WithIconExample = () => (
|
|
|
475
435
|
);
|
|
476
436
|
```
|
|
477
437
|
|
|
438
|
+
### WithNotificationIcon
|
|
439
|
+
|
|
440
|
+
`stories/Buttons/examples/Button/WithNotificationIconExample.tsx`
|
|
441
|
+
|
|
442
|
+
```tsx
|
|
443
|
+
import React from "react";
|
|
444
|
+
import { Badge, Button, EButtonTheme, EComponentSize } from "@sberbusiness/triplex-next";
|
|
445
|
+
import { BellStrokeNavIcon20 } from "@sberbusiness/icons-next";
|
|
446
|
+
|
|
447
|
+
export const WithNotificationIconExample = () => (
|
|
448
|
+
<div style={{ position: "relative", display: "inline-flex" }}>
|
|
449
|
+
<Button
|
|
450
|
+
icon={<BellStrokeNavIcon20 paletteIndex={0} />}
|
|
451
|
+
size={EComponentSize.MD}
|
|
452
|
+
theme={EButtonTheme.SECONDARY}
|
|
453
|
+
/>
|
|
454
|
+
<Badge.Dot size={EComponentSize.MD} style={{ position: "absolute", top: 4, right: 4 }} />
|
|
455
|
+
</div>
|
|
456
|
+
);
|
|
457
|
+
```
|
|
458
|
+
|
|
478
459
|
### BlockMode
|
|
479
460
|
|
|
480
461
|
`stories/Buttons/examples/Button/BlockModeExample.tsx`
|
|
@@ -508,3 +489,30 @@ export const BlockModeExample = () => (
|
|
|
508
489
|
);
|
|
509
490
|
```
|
|
510
491
|
|
|
492
|
+
### TextWithIcon
|
|
493
|
+
|
|
494
|
+
`stories/Buttons/examples/Button/TextWithIconExample.tsx`
|
|
495
|
+
|
|
496
|
+
```tsx
|
|
497
|
+
import React from "react";
|
|
498
|
+
import { DefaulticonStrokePrdIcon16, DefaulticonStrokePrdIcon20 } from "@sberbusiness/icons-next";
|
|
499
|
+
import { Button, EButtonTheme, EComponentSize } from "@sberbusiness/triplex-next";
|
|
500
|
+
|
|
501
|
+
export const TextWithIconExample = () => (
|
|
502
|
+
<div style={{ display: "flex", alignItems: "flex-start", gap: 16 }}>
|
|
503
|
+
<Button theme={EButtonTheme.LINK} size={EComponentSize.SM}>
|
|
504
|
+
<DefaulticonStrokePrdIcon16 paletteIndex={5} />
|
|
505
|
+
<span style={{ marginLeft: 8 }}>Button text</span>
|
|
506
|
+
</Button>
|
|
507
|
+
<Button theme={EButtonTheme.LINK} size={EComponentSize.MD}>
|
|
508
|
+
<DefaulticonStrokePrdIcon20 paletteIndex={5} />
|
|
509
|
+
<span style={{ marginLeft: 8 }}>Button text</span>
|
|
510
|
+
</Button>
|
|
511
|
+
<Button theme={EButtonTheme.LINK} size={EComponentSize.LG}>
|
|
512
|
+
<DefaulticonStrokePrdIcon20 paletteIndex={5} />
|
|
513
|
+
<span style={{ marginLeft: 8 }}>Button text</span>
|
|
514
|
+
</Button>
|
|
515
|
+
</div>
|
|
516
|
+
);
|
|
517
|
+
```
|
|
518
|
+
|
|
@@ -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
|
-
Не используй когда: нужна одиночная кнопка без списка
|
|
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,16 @@ src/components/Button/
|
|
|
49
32
|
|
|
50
33
|
| Prop | Тип | По умолчанию | Описание |
|
|
51
34
|
|---|---|---|---|
|
|
52
|
-
| `children` | `React.ReactNode` |
|
|
53
|
-
| `selected` | `IButtonDropdownOption` |
|
|
35
|
+
| `children` | `React.ReactNode` | — | Текст/контент кнопки (используется также в mobile header) |
|
|
36
|
+
| `selected` | `IButtonDropdownOption` | — | Предвыбранная опция для подсветки в списке |
|
|
54
37
|
| `disabled` | `boolean` | `false` | Блокирует кнопку и открытие списка |
|
|
55
|
-
| `buttonAttributes` | `React.ButtonHTMLAttributes<HTMLButtonElement>` |
|
|
56
|
-
| `
|
|
38
|
+
| `buttonAttributes` | `React.ButtonHTMLAttributes<HTMLButtonElement>` | — | Дополнительные HTML-атрибуты нативной кнопки (включая `aria-label`) |
|
|
39
|
+
| `dropdownAttributes` | `React.HTMLAttributes<HTMLDivElement> & DataAttributes` | — | HTML-атрибуты выпадающего блока, включая `data-*`; `className` мержится с внутренними классами; применяется в desktop-отображении |
|
|
40
|
+
| `block` | `boolean` | `false` | Полноширинный режим (только для non-dots тем) |
|
|
41
|
+
| `opened` | `boolean` | — | Управляемое состояние открытости dropdown (controlled-режим); задаётся в паре с `setOpened`, без него состояние зафиксировано |
|
|
42
|
+
| `setOpened` | `(opened: boolean) => void` | — | Обработчик изменения состояния открытости (пара к `opened`) |
|
|
57
43
|
| `className` | `string` | — | Дополнительный CSS-класс контейнера |
|
|
58
|
-
| `...rest` | `React.HTMLAttributes<HTMLDivElement>` | — | Атрибуты корневого
|
|
59
|
-
|
|
60
|
-
### Ограничения по темам
|
|
61
|
-
|
|
62
|
-
- Для `EButtonDotsTheme` prop `block` запрещен типизацией (`never`).
|
|
63
|
-
- Dots-режим использует иконку `Dotshorizontal*` вместо текстового контента кнопки.
|
|
44
|
+
| `...rest` | `React.HTMLAttributes<HTMLDivElement>` | — | Атрибуты корневого `<div>` компонента |
|
|
64
45
|
|
|
65
46
|
### Структура опции (`IButtonDropdownOption`)
|
|
66
47
|
|
|
@@ -69,50 +50,19 @@ src/components/Button/
|
|
|
69
50
|
| `id` | `string` | Уникальный идентификатор опции |
|
|
70
51
|
| `label` | `React.ReactNode` | Видимый контент пункта меню |
|
|
71
52
|
| `onSelect` | `() => void` | Callback выбора опции |
|
|
72
|
-
| `...rest` | `IDropdownListItemProps` (без ряда
|
|
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 закрывается.
|
|
53
|
+
| `...rest` | `IDropdownListItemProps` (без ряда запрещённых полей) | Остальные настройки `DropdownList.Item` |
|
|
88
54
|
|
|
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
|
|
55
|
+
### Ограничения по темам
|
|
104
56
|
|
|
105
|
-
-
|
|
106
|
-
-
|
|
107
|
-
-
|
|
108
|
-
- Для корректной доступности меню потребитель должен передавать понятные `label` для `options`.
|
|
57
|
+
- Для `EButtonDotsTheme` prop `block` запрещён типизацией (`never`).
|
|
58
|
+
- Dots-режим использует иконку `Dotshorizontal*` вместо текстового контента кнопки.
|
|
59
|
+
- Desktop рендерит `DropdownList` с `DropdownList.Item`; mobile через `mobileViewProps` рендерит `DropdownMobileHeader` + `DropdownMobileList`.
|
|
109
60
|
|
|
110
61
|
---
|
|
111
62
|
|
|
112
63
|
## Дизайн-токены
|
|
113
64
|
|
|
114
|
-
`ButtonDropdown` не использует собственных CSS color
|
|
115
|
-
Визуальные токены берутся из `Button` и `Dropdown`, которые рендерятся внутри.
|
|
65
|
+
`ButtonDropdown` не использует собственных CSS color-токенов. Визуальные токены берутся из `Button` и `Dropdown`, которые рендерятся внутри.
|
|
116
66
|
|
|
117
67
|
---
|
|
118
68
|
|
|
@@ -126,17 +76,27 @@ src/components/Button/
|
|
|
126
76
|
|
|
127
77
|
---
|
|
128
78
|
|
|
79
|
+
## Accessibility
|
|
80
|
+
|
|
81
|
+
- Триггер строится на `Button` (нативный `<button>`), базовая keyboard-навигация сохраняется.
|
|
82
|
+
- Кнопка получает `aria-haspopup="menu"`, `aria-expanded`, `aria-controls`. Связка trigger/list осуществляется через `instanceId` (`uniqueId()`); `aria-activedescendant` синхронизируется через `DropdownListContext`.
|
|
83
|
+
- На кнопке перехватываются `Space`, `ArrowUp`, `ArrowDown` с `preventDefault` (сравнение по `event.code`, как в `ButtonDropdownExtended`). `Space` явно переключает dropdown (нативная click-активация отменена `preventDefault`), `ArrowUp` / `ArrowDown` открывают его, если закрыт. Dropdown закрывается по `Escape` и (из-за `closeOnTab`) по `Tab`.
|
|
84
|
+
- Компонент **не хардкодит** aria-тексты: при необходимости `aria-label` нужно передавать через `buttonAttributes`. Для корректной доступности меню потребитель должен передавать понятные `label` для `options`.
|
|
85
|
+
- На mobile-заголовке dropdown отображается `children` триггера; если в продукте нужен локализованный отдельный заголовок, его нужно задавать на уровне композиции (через `ButtonDropdownExtended`).
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
129
89
|
## Связанные компоненты
|
|
130
90
|
|
|
131
|
-
- `Button` (`src/components/Button/Button.tsx`)
|
|
132
|
-
- `ButtonDropdownExtended` (`src/components/Button/ButtonDropdownExtended.tsx`)
|
|
133
|
-
- `Dropdown` (`src/components/Dropdown`)
|
|
91
|
+
- `Button` (`src/components/Button/Button.tsx`) — базовый триггер, на котором построен `ButtonDropdown`.
|
|
92
|
+
- `ButtonDropdownExtended` (`src/components/Button/ButtonDropdownExtended.tsx`) — управляет открытием/закрытием dropdown через render-функции `renderButton` и `renderDropdown`.
|
|
93
|
+
- `Dropdown` (`src/components/Dropdown`) — контейнер и пункты списка для desktop/mobile представления.
|
|
134
94
|
|
|
135
95
|
---
|
|
136
96
|
|
|
137
97
|
## Stories
|
|
138
98
|
|
|
139
|
-
`stories/Buttons/ButtonDropdown.stories.tsx`
|
|
99
|
+
Основные истории: `stories/Buttons/ButtonDropdown.stories.tsx`
|
|
140
100
|
Файлы примеров: `stories/Buttons/examples/ButtonDropdown/`
|
|
141
101
|
|
|
142
102
|
| Story | Example file | Что демонстрирует |
|
|
@@ -157,6 +117,12 @@ src/components/Button/
|
|
|
157
117
|
| Дата | Изменение |
|
|
158
118
|
|---|---|
|
|
159
119
|
| 2026-04-15 | Создан документ AI-ready для `ButtonDropdown`. |
|
|
120
|
+
| 2026-04-27 | Приведён в соответствие с `docs/ai/template-ai.md`: убраны секции «Файловая структура» и «Ключевые особенности реализации», их содержимое перенесено в `Ограничения по темам` и `Accessibility`, добавлена колонка `Example file`. |
|
|
121
|
+
| 2026-07-09 | Добавлено свойство `dropdownAttributes` — HTML-атрибуты выпадающего блока (в том числе возможность поднять `z-index` через `style`). |
|
|
122
|
+
| 2026-07-15 | В `ButtonDropdownExtended` `vertical-align: top` заменён на `middle` (синхронно с `Button`) — общая опорная точка для кнопок разной высоты и центрирование относительно строки инлайн-текста. |
|
|
123
|
+
| 2026-07-28 | AI-рефакторинг: dots- и обычный триггер объединены в один `renderButton` (общая a11y-обвязка), тема кнопки и палитра каретки выведены через мапы вместо `as`-каста и `switch`, удалено обращение к несуществующему CSS-классу `buttonDropdownMenu` (в LESS его не было, в сборке значение было `undefined`). Публичный API и разметка не изменились. Unit-тесты расширены до 32 кейсов. |
|
|
124
|
+
| 2026-07-29 | Правки по ревью PR #482: типы триггера и props-интерфейсы вариантов теперь экспортируются (`TButtonDropdownButtonTheme`, `TButtonDropdownTheme`, `IButtonDropdownBaseProps`, `IButtonDotsProps`) — по конвенции все типы barrel-компонентов публичны; поведение не изменилось. |
|
|
125
|
+
| 2026-07-29 | Отработка находок AI-рефакторинга: починен перехват пробела на триггере (сравнение по `event.code`; `Space` явно переключает dropdown), `label` опции больше не утекает в DOM-атрибут элемента списка, легализован controlled-режим — публичные props `opened` / `setOpened` (ранее `opened` работал только через нетипизированный spread). |
|
|
160
126
|
|
|
161
127
|
---
|
|
162
128
|
|