@sberbusiness/triplex-next-mcp-server 1.45.0-beta → 1.48.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 +7 -7
- package/bundle/components/AmountField.md +222 -323
- package/bundle/components/Avatar.md +138 -146
- package/bundle/components/CardAction.md +615 -0
- package/bundle/components/CardStatic.md +402 -0
- package/bundle/components/CheckboxTree.md +157 -215
- package/bundle/components/CheckboxTreeExtended.md +7 -7
- package/bundle/components/Chip.md +7 -6
- package/bundle/components/ChipGroup.md +1 -1
- package/bundle/components/ChipMonthYearPicker.md +344 -0
- package/bundle/components/ChipMultiselect.md +1251 -0
- package/bundle/components/ChipOptions.md +329 -0
- package/bundle/components/ChipSort.md +333 -0
- package/bundle/components/ChipSuggest.md +588 -0
- package/bundle/components/CollapsibleTree.md +8 -4
- package/bundle/components/CollapsibleTreeExtended.md +8 -6
- package/bundle/components/Confirm.md +346 -281
- package/bundle/components/DateField.md +222 -303
- package/bundle/components/DatePickerExtended.md +3 -1
- package/bundle/components/DateRange.md +351 -210
- package/bundle/components/FooterPage.md +1 -0
- package/bundle/components/FormField.md +14 -5
- package/bundle/components/FormGroup.md +3 -2
- package/bundle/components/HelpBox.md +7 -1
- package/bundle/components/IslandAccordion.md +443 -152
- package/bundle/components/IslandWidget.md +622 -157
- package/bundle/components/LightBox.md +8 -0
- package/bundle/components/ListMaster.md +2 -2
- package/bundle/components/ListTableItem.md +6 -6
- package/bundle/components/MaskedField.md +2 -1
- package/bundle/components/MonthYearField.md +18 -5
- package/bundle/components/Slider.md +389 -97
- package/bundle/components/SliderRange.md +486 -105
- package/bundle/components/StatusTracker.md +386 -94
- package/bundle/components/Stepper.md +355 -102
- package/bundle/components/StepperExtended.md +633 -0
- package/bundle/components/SuggestField.md +1049 -96
- package/bundle/components/SwipeableArea.md +7 -7
- package/bundle/components/Tabs.md +265 -92
- package/bundle/components/TabsExtended.md +18 -18
- package/bundle/components/TabsLine.md +301 -127
- package/bundle/components/TagColor.md +187 -66
- package/bundle/components/TagGroup.md +184 -48
- package/bundle/components/TextareaField.md +278 -70
- package/bundle/components/TopOverlay.md +17 -1
- package/bundle/components/TreeView.md +16 -7
- package/bundle/components/TriggerClickOnKeyDownEvent.md +277 -0
- package/bundle/components/UploadZone.md +318 -0
- package/bundle/guides/codestyle.md +16 -1
- package/bundle/guides/commits.md +47 -1
- package/bundle/guides/tests.md +23 -2
- package/bundle/manifest.json +92 -24
- package/bundle/release-notes/1.46.0.mdx +59 -0
- package/bundle/release-notes/1.47.0.mdx +122 -0
- package/bundle/release-notes/1.48.0.mdx +68 -0
- package/bundle/release-notes/1.49.0.mdx +7 -0
- package/bundle/tokens.json +3 -3
- package/dist/index.js +220 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: TagColor
|
|
3
|
-
category:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
category: Tag
|
|
4
|
+
related: [Tag, MarkerStatus]
|
|
5
|
+
tokens:
|
|
6
|
+
- TagColor.Background_Default
|
|
7
|
+
- TagColor.Background_Error
|
|
8
|
+
- TagColor.Background_Info
|
|
9
|
+
- TagColor.Background_Success
|
|
10
|
+
- TagColor.Background_Warning
|
|
11
|
+
- TagColor.Text_Color_Default
|
|
8
12
|
stories: stories/TagColor/TagColor.stories.tsx
|
|
9
13
|
version: "1.0"
|
|
10
14
|
---
|
|
@@ -13,104 +17,140 @@ version: "1.0"
|
|
|
13
17
|
|
|
14
18
|
## Назначение
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
(`DEFAULT
|
|
18
|
-
|
|
20
|
+
Неинтерактивная цветная плашка с текстом — метка для маркировки и классификации. Цвет фона
|
|
21
|
+
кодирует статус (`DEFAULT` / `SUCCESS` / `INFO` / `WARNING` / `ERROR`), содержимое произвольное.
|
|
22
|
+
Типичное место — статус заявки в строке таблицы, тип документа в списке, признак записи рядом
|
|
23
|
+
с заголовком.
|
|
19
24
|
|
|
20
|
-
Используй когда: нужно
|
|
21
|
-
|
|
22
|
-
`Tag`.
|
|
25
|
+
Используй когда: нужно показать состояние или категорию объекта короткой цветной меткой,
|
|
26
|
+
и пользователь с ней не взаимодействует.
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
Не используй когда:
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
├── types.ts # ITagColorProps
|
|
33
|
-
├── styles/TagColor.module.less
|
|
34
|
-
├── __tests__/
|
|
35
|
-
└── index.ts
|
|
36
|
-
```
|
|
30
|
+
- Метку нужно снимать или редактировать — возьми `Tag`: у него есть кнопки удаления
|
|
31
|
+
и редактирования, но нет `status`.
|
|
32
|
+
- Статус достаточно показать точкой без текста — возьми `Marker`, а с подписью и описанием —
|
|
33
|
+
`MarkerStatus`.
|
|
34
|
+
- Нужен интерактивный элемент (выбор фильтра, выпадающий список) — это семейство `Chip`.
|
|
35
|
+
`TagColor` рендерит `<span>` без роли, фокуса, обработчиков и клавиатуры.
|
|
37
36
|
|
|
38
37
|
---
|
|
39
38
|
|
|
40
|
-
##
|
|
39
|
+
## Варианты и props
|
|
41
40
|
|
|
42
|
-
###
|
|
41
|
+
### Обязательные props
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
| Prop | Тип | Описание |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| `size` | `EComponentSize` | Размер: SM (высота 16px) / MD (20px) / LG (28px). Задаёт высоту, внутренние отступы, `border-radius` (4 / 4 / 8px) и размер текста (10 / 12 / 14px) |
|
|
47
46
|
|
|
48
|
-
|
|
47
|
+
### Опциональные props
|
|
48
|
+
|
|
49
|
+
| Prop | Тип | По умолчанию | Описание |
|
|
49
50
|
|---|---|---|---|
|
|
50
|
-
| `
|
|
51
|
-
| `
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
```ts
|
|
58
|
-
enum ETagColorStatus {
|
|
59
|
-
DEFAULT = "default",
|
|
60
|
-
SUCCESS = "success",
|
|
61
|
-
INFO = "info",
|
|
62
|
-
WARNING = "warning",
|
|
63
|
-
ERROR = "error",
|
|
64
|
-
}
|
|
65
|
-
```
|
|
51
|
+
| `status` | `ETagColorStatus` | `ETagColorStatus.DEFAULT` | Определяет **только** цвет фона. На размер, типографику и цвет текста не влияет |
|
|
52
|
+
| `children` | `React.ReactNode` | — | Содержимое метки. Рендерится во вложенном `<span>`, длинный текст обрезается многоточием |
|
|
53
|
+
|
|
54
|
+
Остальные props — стандартные атрибуты `span`
|
|
55
|
+
(`ITagColorProps extends React.HTMLAttributes<HTMLSpanElement>`), уходят на корневой элемент
|
|
56
|
+
через `...restProps`.
|
|
66
57
|
|
|
67
|
-
|
|
58
|
+
### Размер и статус независимы
|
|
59
|
+
|
|
60
|
+
Две оси не пересекаются: `size` отвечает за геометрию и типографику, `status` — за один
|
|
61
|
+
`background`. Любая комбинация допустима, поэтому матрицу 3×5 целиком покрывает story
|
|
62
|
+
`VisualTests`, а документационные stories показывают оси по отдельности.
|
|
68
63
|
|
|
69
64
|
---
|
|
70
65
|
|
|
71
|
-
##
|
|
66
|
+
## Дизайн-токены
|
|
67
|
+
|
|
68
|
+
Переопределяются через `ThemeProvider` (prop `tokens`) — см. `ThemeProvider-ai.md` →
|
|
69
|
+
«Как переопределять токены». Значения по умолчанию — `src/components/DesignTokens/components/TagColor.ts`.
|
|
72
70
|
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
```text
|
|
72
|
+
TagColor.Background_Default
|
|
73
|
+
TagColor.Background_Success
|
|
74
|
+
TagColor.Background_Info
|
|
75
|
+
TagColor.Background_Warning
|
|
76
|
+
TagColor.Background_Error
|
|
77
|
+
TagColor.Text_Color_Default
|
|
77
78
|
```
|
|
78
79
|
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
Пять фоновых токенов — по одному на значение `ETagColorStatus`; добавление статуса означает
|
|
81
|
+
и новый токен. Цвет текста один на все статусы (`Text_Color_Default`, тёмный нейтральный
|
|
82
|
+
в обеих темах): фоны подобраны так, чтобы контраст держался без переключения цвета текста.
|
|
83
|
+
|
|
84
|
+
Размерные значения (высоты 16/20/28px, отступы, `border-radius`, `font-size` / `line-height`)
|
|
85
|
+
заданы литералами в `styles/TagColor.module.less` — они специфичны для компонента.
|
|
81
86
|
|
|
82
87
|
---
|
|
83
88
|
|
|
84
89
|
## Инварианты
|
|
85
90
|
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
|
|
89
|
-
-
|
|
90
|
-
-
|
|
91
|
-
|
|
92
|
-
-
|
|
91
|
+
- `forwardRef<HTMLSpanElement>` — не убирать. Ref всегда указывает на корневой `<span>`.
|
|
92
|
+
- Корневой элемент — `<span>` (метка инлайновая, встаёт в строку текста и в ячейку таблицы).
|
|
93
|
+
Менять на `div` нельзя.
|
|
94
|
+
- `displayName` — `"TagColor"`. Не менять.
|
|
95
|
+
- Двухуровневая разметка обязательна: корневой `<span class="tagColor">` держит фон, размеры
|
|
96
|
+
и `display: inline-flex`, вложенный `<span class="content">` — обрезку текста
|
|
97
|
+
(`min-width: 0`, `overflow: hidden`, `text-overflow: ellipsis`, `white-space: nowrap`)
|
|
98
|
+
и цвет текста. Схлопывать в один элемент нельзя: обрезка перестанет работать, а `max-width: 100%`
|
|
99
|
+
на корне — ограничивать ширину.
|
|
100
|
+
- `status` имеет значение по умолчанию `ETagColorStatus.DEFAULT` — оно проставляется
|
|
101
|
+
в деструктуризации props, а не в `defaultProps`. Класс статуса на корне есть всегда.
|
|
102
|
+
- Значения `ETagColorStatus` (`"default"` / `"success"` / `"info"` / `"warning"` / `"error"`)
|
|
103
|
+
совпадают с именами CSS-классов в `TagColor.module.less` и с суффиксами токенов
|
|
104
|
+
`TagColor.Background_*`. Переименование значения enum — ломающее изменение сразу в трёх местах.
|
|
105
|
+
- `className` деструктурируется из props и мерджится через `clsx`, поэтому в `...restProps`
|
|
106
|
+
его нет и перезатереть базовые классы он не может.
|
|
107
|
+
- Экспорты `TagColor`, `ITagColorProps` и `ETagColorStatus` идут в barrel
|
|
108
|
+
`src/components/TagColor/index.ts` — сохранять.
|
|
93
109
|
|
|
94
110
|
---
|
|
95
111
|
|
|
96
112
|
## Accessibility
|
|
97
113
|
|
|
98
|
-
-
|
|
99
|
-
|
|
100
|
-
-
|
|
114
|
+
- Собственной ARIA-роли нет: корневой `<span>` — неинтерактивный контейнер, в порядок фокуса
|
|
115
|
+
не попадает, обработчиков клавиатуры у компонента нет.
|
|
116
|
+
- Цвет — единственный визуальный носитель статуса, поэтому **смысл обязан быть в тексте
|
|
117
|
+
метки**. Не полагайся на один цвет: `<TagColor status={ETagColorStatus.ERROR}>Отклонено</TagColor>`,
|
|
118
|
+
а не пустая красная плашка.
|
|
119
|
+
- Если метка расшифровывает соседний элемент (статус строки таблицы), связывай их через
|
|
120
|
+
`aria-describedby` / `aria-labelledby` на стороне потребителя — компонент никаких
|
|
121
|
+
ARIA-атрибутов сам не проставляет, но пробрасывает любые через `...restProps`.
|
|
122
|
+
- Компонент не хардкодит текстовые строки: содержимое и любые `aria-label` / `title` задаёт
|
|
123
|
+
потребитель. Библиотека мультиязычная.
|
|
101
124
|
|
|
102
125
|
---
|
|
103
126
|
|
|
104
127
|
## Связанные компоненты
|
|
105
128
|
|
|
106
|
-
- `Tag` —
|
|
107
|
-
|
|
129
|
+
- `Tag` — визуально похожая плашка, но интерактивная: с кнопками удаления и редактирования
|
|
130
|
+
и без `status`. Берётся, когда значение можно снять; `TagColor` — когда метку только показывают.
|
|
131
|
+
- `Marker` — цветная точка-индикатор статуса без текста. Тот же набор смыслов, минимальная форма:
|
|
132
|
+
берётся, когда на подпись нет места.
|
|
133
|
+
- `MarkerStatus` — точка с подписью и описанием. Альтернатива, когда статусу нужен не компактный
|
|
134
|
+
бейдж, а строка с пояснением.
|
|
108
135
|
|
|
109
136
|
---
|
|
110
137
|
|
|
111
138
|
## Stories
|
|
112
139
|
|
|
113
140
|
Основные истории: `stories/TagColor/TagColor.stories.tsx`
|
|
141
|
+
Файлы примеров: `stories/TagColor/examples/`
|
|
142
|
+
|
|
143
|
+
| Story | Example file | Что демонстрирует |
|
|
144
|
+
|---|---|---|
|
|
145
|
+
| `Playground` | `Playground.tsx` | Интерактивный контроль `children`, `size` и `status` |
|
|
146
|
+
| `Default` | `Default.tsx` | Минимальная метка: обязателен только `size`, статус по умолчанию |
|
|
147
|
+
| `Sizes` | `Sizes.tsx` | Размеры SM / MD / LG при одном статусе |
|
|
148
|
+
| `Statuses` | `Statuses.tsx` | Все пять значений `ETagColorStatus` при одном размере |
|
|
149
|
+
| `WithOverflow` | `WithOverflow.tsx` | Обрезка длинного контента многоточием по ширине контейнера |
|
|
150
|
+
| `VisualTests` | `VisualTests.tsx` | Матрица размеров × статусов плюс пустая метка и обрезка — для скриншот-тестов |
|
|
151
|
+
|
|
152
|
+
Story ID участвуют в именах baseline-скриншотов (`tagcolor--sizes--xs.png` и т.п.), поэтому
|
|
153
|
+
переименование story требует перегенерации baseline и удаления осиротевших файлов.
|
|
114
154
|
|
|
115
155
|
---
|
|
116
156
|
|
|
@@ -118,4 +158,85 @@ enum ETagColorStatus {
|
|
|
118
158
|
|
|
119
159
|
| Дата | Изменение |
|
|
120
160
|
|---|---|
|
|
121
|
-
| 2026-
|
|
161
|
+
| 2026-09-22 | Создан документ. AI-рефакторинг (JSDoc на props, enum и map'ах класса, типизация `STATUS_TO_CLASS_NAME_MAP`, исправлен JSDoc `ITagColorProps`), расширены unit-тесты, stories переведены на modern pattern и дополнены `VisualTests`. |
|
|
162
|
+
| 2026-09-22 | По ревью: `children` объявлен в `ITagColorProps` явно (тип прежний, приходил из `React.HTMLAttributes`) — попадает в таблицу Props в Storybook. JSDoc компонента сокращён, детали разметки остались здесь. |
|
|
163
|
+
| 2026-09-22 | По решению ревьюера stories `DifferentSizes` / `DifferentStatuses` переименованы в `Sizes` / `Statuses` — единообразно с `Tag`, `Marker`, `NumberField`. Baseline перегенерированы, старые удалены. |
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Примеры использования
|
|
168
|
+
|
|
169
|
+
### Default
|
|
170
|
+
|
|
171
|
+
`stories/TagColor/examples/Default.tsx`
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
import React from "react";
|
|
175
|
+
import { TagColor, EComponentSize } from "@sberbusiness/triplex-next";
|
|
176
|
+
|
|
177
|
+
export const Default = () => <TagColor size={EComponentSize.MD}>Tag text</TagColor>;
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Sizes
|
|
181
|
+
|
|
182
|
+
`stories/TagColor/examples/Sizes.tsx`
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
import React from "react";
|
|
186
|
+
import { TagColor, EComponentSize, ETagColorStatus } from "@sberbusiness/triplex-next";
|
|
187
|
+
|
|
188
|
+
const SIZES = Object.values(EComponentSize);
|
|
189
|
+
|
|
190
|
+
export const Sizes = () => (
|
|
191
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
|
192
|
+
{SIZES.map((size) => (
|
|
193
|
+
<div key={size}>
|
|
194
|
+
<div style={{ marginBottom: 8, fontSize: 16, fontWeight: 700 }}>{size.toUpperCase()}</div>
|
|
195
|
+
<TagColor size={size} status={ETagColorStatus.INFO}>
|
|
196
|
+
Tag text
|
|
197
|
+
</TagColor>
|
|
198
|
+
</div>
|
|
199
|
+
))}
|
|
200
|
+
</div>
|
|
201
|
+
);
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Statuses
|
|
205
|
+
|
|
206
|
+
`stories/TagColor/examples/Statuses.tsx`
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
import React from "react";
|
|
210
|
+
import { TagColor, EComponentSize, ETagColorStatus } from "@sberbusiness/triplex-next";
|
|
211
|
+
|
|
212
|
+
const STATUSES = Object.values(ETagColorStatus);
|
|
213
|
+
|
|
214
|
+
export const Statuses = () => (
|
|
215
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
|
216
|
+
{STATUSES.map((status) => (
|
|
217
|
+
<div key={status}>
|
|
218
|
+
<div style={{ marginBottom: 8, fontSize: 16, fontWeight: 700 }}>{status.toUpperCase()}</div>
|
|
219
|
+
<TagColor size={EComponentSize.MD} status={status}>
|
|
220
|
+
Tag text
|
|
221
|
+
</TagColor>
|
|
222
|
+
</div>
|
|
223
|
+
))}
|
|
224
|
+
</div>
|
|
225
|
+
);
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### WithOverflow
|
|
229
|
+
|
|
230
|
+
`stories/TagColor/examples/WithOverflow.tsx`
|
|
231
|
+
|
|
232
|
+
```tsx
|
|
233
|
+
import React from "react";
|
|
234
|
+
import { TagColor, EComponentSize } from "@sberbusiness/triplex-next";
|
|
235
|
+
|
|
236
|
+
export const WithOverflow = () => (
|
|
237
|
+
<div style={{ maxWidth: 400 }}>
|
|
238
|
+
<TagColor size={EComponentSize.LG}>Very long tag text that should be truncated with ellipsis</TagColor>
|
|
239
|
+
</div>
|
|
240
|
+
);
|
|
241
|
+
```
|
|
242
|
+
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: TagGroup
|
|
3
|
-
category:
|
|
4
|
-
|
|
5
|
-
figma-file: ""
|
|
6
|
-
related: [Tag, TagColor]
|
|
3
|
+
category: TagGroup
|
|
4
|
+
related: [Tag, ChipGroup]
|
|
7
5
|
tokens: []
|
|
8
6
|
stories: stories/TagGroup/TagGroup.stories.tsx
|
|
9
7
|
version: "1.0"
|
|
@@ -13,83 +11,121 @@ version: "1.0"
|
|
|
13
11
|
|
|
14
12
|
## Назначение
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
Контейнер-раскладка для тегов: выстраивает `Tag` в общем потоке с одинаковым отступом,
|
|
15
|
+
переносит их на новые строки, когда ширины контейнера не хватает, и помечает набор
|
|
16
|
+
как группу (`role="group"`).
|
|
19
17
|
|
|
20
|
-
Используй когда:
|
|
21
|
-
и
|
|
22
|
-
|
|
18
|
+
Используй когда: на экране идёт ряд снимаемых значений — применённые фильтры, выбранные
|
|
19
|
+
в форме опции — и им нужен единый отступ и общая семантика группы.
|
|
20
|
+
Не используй когда: группируются чипсы семейства `Chip*` — для них есть `ChipGroup`
|
|
21
|
+
(flex + `gap`, опциональный режим одной строки с прокруткой). Нужен контейнер общего
|
|
22
|
+
назначения с отступами — бери собственную вёрстку.
|
|
23
23
|
|
|
24
24
|
---
|
|
25
25
|
|
|
26
|
-
##
|
|
26
|
+
## Варианты и props
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
src/components/TagGroup/
|
|
30
|
-
├── TagGroup.tsx # forwardRef<HTMLDivElement, ITagGroupProps>
|
|
31
|
-
├── types.ts # ITagGroupProps
|
|
32
|
-
├── styles/TagGroup.module.less
|
|
33
|
-
├── __tests__/
|
|
34
|
-
└── index.ts
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
---
|
|
38
|
-
|
|
39
|
-
## Публичный API
|
|
28
|
+
### Обязательные props
|
|
40
29
|
|
|
41
|
-
|
|
30
|
+
| Prop | Тип | Описание |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| `size` | `EComponentSize` | Отступ между тегами: SM — 4px, MD — 8px, LG — 12px. Размер самих тегов не меняет |
|
|
42
33
|
|
|
43
|
-
|
|
44
|
-
extends HTMLAttributes<HTMLDivElement>
|
|
45
|
-
```
|
|
34
|
+
### Опциональные props
|
|
46
35
|
|
|
47
|
-
| Prop | Тип |
|
|
36
|
+
| Prop | Тип | По умолчанию | Описание |
|
|
48
37
|
|---|---|---|---|
|
|
49
|
-
| `
|
|
50
|
-
| `
|
|
51
|
-
|
|
38
|
+
| `children` | `React.ReactNode` | — | Обычно набор `Tag`. Отступ получает **любой** прямой ребёнок — селектор в LESS-модуле `& > *` |
|
|
39
|
+
| `...HTMLDivAttributes` | `React.HTMLAttributes<HTMLDivElement>` | — | Все атрибуты `<div>`, включая `style`, `aria-*`, `data-*` и `role` |
|
|
40
|
+
|
|
41
|
+
### Ограничения использования
|
|
42
|
+
|
|
43
|
+
- **`size` не пробрасывается в теги.** Он задаёт только отступ группы; `size` каждого `Tag`
|
|
44
|
+
потребитель выставляет сам. Рассинхрон размеров группы и тегов ничем не подсвечивается —
|
|
45
|
+
во всех stories они совпадают.
|
|
46
|
+
- **Отступ сделан на margin'ах, а не на `gap`.** Каждый прямой ребёнок получает
|
|
47
|
+
`margin: 2px / 4px / 6px`, а сам контейнер — такой же отрицательный margin. Следствия:
|
|
48
|
+
крайние теги прижаты к границам группы, но визуально группа «вылезает» за свой бокс
|
|
49
|
+
на эти 2/4/6px и может наехать на соседний контент, если у родителя нет отступа.
|
|
50
|
+
- **Собственного `display` у контейнера нет** — это обычный блок, теги переносятся
|
|
51
|
+
как инлайновые элементы. Если записать теги в одну строку JSX через пробел
|
|
52
|
+
(`<Tag/> <Tag/>`), между ними добавится пробельный текстовый узел и горизонтальный
|
|
53
|
+
зазор станет чуть больше вертикального. При переносе тегов по строкам или рендере
|
|
54
|
+
из `map()` такого узла не возникает и зазоры одинаковы.
|
|
55
|
+
- **Ширину контейнер не ограничивает.** Перенос по строкам появится только там, где
|
|
56
|
+
ширину задал родитель.
|
|
57
|
+
- **`role="group"` можно переопределить.** Атрибут выставляется **до** `{...restProps}`,
|
|
58
|
+
поэтому `role`, переданный потребителем, побеждает (в отличие от `data-tx` в `ChipGroup`,
|
|
59
|
+
который стоит после spread и переопределению не поддаётся).
|
|
52
60
|
|
|
53
61
|
---
|
|
54
62
|
|
|
55
|
-
##
|
|
63
|
+
## Дизайн-токены
|
|
56
64
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
{children}
|
|
60
|
-
</div>
|
|
61
|
-
```
|
|
65
|
+
Компонент не использует собственных CSS-переменных: в стилях только раскладка (margin'ы
|
|
66
|
+
по размерам). Цвета и размеры приходят от вложенных тегов — см. `Tag-ai.md`.
|
|
62
67
|
|
|
63
68
|
---
|
|
64
69
|
|
|
65
70
|
## Инварианты
|
|
66
71
|
|
|
67
|
-
- **`forwardRef<HTMLDivElement>`** —
|
|
68
|
-
-
|
|
69
|
-
|
|
70
|
-
- **`
|
|
71
|
-
|
|
72
|
-
-
|
|
72
|
+
- **`forwardRef<HTMLDivElement>`** — обязателен, `ref` указывает на корневой `<div>`. Не убирать.
|
|
73
|
+
- **Корневой элемент — `<div>` с `role="group"` по умолчанию.** Роль — часть наблюдаемого
|
|
74
|
+
контракта: на неё опираются unit-тесты (`screen.getByRole("group")`) и потребители.
|
|
75
|
+
- **`size` — обязательный prop без значения по умолчанию.** Делать его опциональным
|
|
76
|
+
или менять числа отступов нельзя без согласования: это изменит вёрстку у потребителей.
|
|
77
|
+
- **Порядок в JSX: `className` → `role` → `{...restProps}` → `ref`.** Перестановка `role`
|
|
78
|
+
за spread отнимет у потребителя возможность переопределить роль.
|
|
79
|
+
- **`className` добавляется последним аргументом `clsx`** и собственные классы не затирает.
|
|
80
|
+
- **Классы `.sm` / `.md` / `.lg` берутся из общего хелпера** `createSizeToClassNameMap`
|
|
81
|
+
(`src/utils/classNameMaps.ts`) — локальный маппинг не заводить.
|
|
82
|
+
- **`displayName`** — `"TagGroup"`. Не менять.
|
|
83
|
+
- **Публичный API** — `TagGroup` и `ITagGroupProps` через `src/components/TagGroup/index.ts`
|
|
84
|
+
(`export * from "./types"` + `export * from "./TagGroup"`) и общий barrel
|
|
85
|
+
`src/components/index.ts`. Сохранять.
|
|
86
|
+
- **Story ids `taggroup--*`** завязаны на baseline-скриншоты в `__screenshots__/` —
|
|
87
|
+
переименование story требует синхронного обновления baseline.
|
|
73
88
|
|
|
74
89
|
---
|
|
75
90
|
|
|
76
91
|
## Accessibility
|
|
77
92
|
|
|
78
|
-
- `role="group"` —
|
|
79
|
-
-
|
|
93
|
+
- Корневой `<div>` несёт `role="group"` — набор тегов объявляется скринридеру как группа.
|
|
94
|
+
- **Доступное имя группе задаёт потребитель**: `aria-label` или `aria-labelledby` через
|
|
95
|
+
`...restProps`. Компонент строку не подставляет — библиотека мультиязычная. Без имени
|
|
96
|
+
роль `group` остаётся безымянной и пользы от неё немного.
|
|
97
|
+
- Если набор тегов по смыслу не группа контролов, а список, роль переопределяется снаружи:
|
|
98
|
+
`role="list"` (тогда детям нужен `role="listitem"`).
|
|
99
|
+
- Клавиатуру и фокус компонент не трогает: порядок фокуса задаётся порядком тегов в DOM,
|
|
100
|
+
а кнопки внутри — это `ButtonIcon` внутри `Tag`, `aria-label` для них передаётся
|
|
101
|
+
на самих тегах (`editButtonProps` / `removeButtonProps`).
|
|
80
102
|
|
|
81
103
|
---
|
|
82
104
|
|
|
83
105
|
## Связанные компоненты
|
|
84
106
|
|
|
85
|
-
- `Tag` —
|
|
86
|
-
|
|
107
|
+
- `Tag` — основной ребёнок группы: плашка с выбранным значением, кнопкой удаления и,
|
|
108
|
+
при переданном `onEdit`, кнопкой редактирования. Свой `size` получает отдельно от группы.
|
|
109
|
+
- `ChipGroup` — аналогичный контейнер для семейства `Chip*`. Отличия: раскладка на
|
|
110
|
+
`display: flex` + `gap`, `size` опционален, роли по умолчанию нет, зато есть режим
|
|
111
|
+
`oneLine` с горизонтальной прокруткой.
|
|
112
|
+
- `TagColor` — неинтерактивная цветная метка статуса. В группу её класть можно (отступ
|
|
113
|
+
получает любой прямой ребёнок), но типовой ребёнок `TagGroup` — именно `Tag`.
|
|
87
114
|
|
|
88
115
|
---
|
|
89
116
|
|
|
90
117
|
## Stories
|
|
91
118
|
|
|
92
119
|
Основные истории: `stories/TagGroup/TagGroup.stories.tsx`
|
|
120
|
+
Файлы примеров: `stories/TagGroup/examples/`
|
|
121
|
+
|
|
122
|
+
| Story | Example file | Что демонстрирует |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| `Playground` | `Playground.tsx` | Интерактивный контроль `size` и наличия кнопки редактирования у тегов |
|
|
125
|
+
| `Default` | `Default.tsx` | Минимальная группа тегов с отступом `LG` |
|
|
126
|
+
| `Sizes` | `Sizes.tsx` | Отступы для размеров SM / MD / LG |
|
|
127
|
+
| `WithOverflow` | `WithOverflow.tsx` | Перенос тегов по строкам в контейнере фиксированной ширины |
|
|
128
|
+
| `VisualTests` | `VisualTests.tsx` | Матрица размеров с переносом строк и группа из одного длинного тега — для скриншот-тестов |
|
|
93
129
|
|
|
94
130
|
---
|
|
95
131
|
|
|
@@ -97,4 +133,104 @@ extends HTMLAttributes<HTMLDivElement>
|
|
|
97
133
|
|
|
98
134
|
| Дата | Изменение |
|
|
99
135
|
|---|---|
|
|
100
|
-
| 2026-
|
|
136
|
+
| 2026-09-18 | Создан документ. AI-рефакторинг (TRI-95): `displayName`, маппинг размеров в UPPER_SNAKE_CASE, JSDoc на props и компоненте, явный `children` в `ITagGroupProps`; расширены unit-тесты, stories переведены на modern pattern. Публичный API, DOM и внешний вид не изменены |
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Примеры использования
|
|
141
|
+
|
|
142
|
+
### Default
|
|
143
|
+
|
|
144
|
+
`stories/TagGroup/examples/Default.tsx`
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
import React from "react";
|
|
148
|
+
import { Tag, TagGroup, EComponentSize } from "@sberbusiness/triplex-next";
|
|
149
|
+
|
|
150
|
+
const TAGS = ["Selected value", "Another value", "One more value"];
|
|
151
|
+
|
|
152
|
+
export const Default = () => (
|
|
153
|
+
<TagGroup size={EComponentSize.LG} aria-label="Применённые фильтры">
|
|
154
|
+
{TAGS.map((label, index) => (
|
|
155
|
+
<Tag
|
|
156
|
+
key={label}
|
|
157
|
+
id={`default-tag-${index}`}
|
|
158
|
+
size={EComponentSize.LG}
|
|
159
|
+
onRemove={() => {}}
|
|
160
|
+
removeButtonProps={{ "aria-label": "Удалить" }}
|
|
161
|
+
>
|
|
162
|
+
{label}
|
|
163
|
+
</Tag>
|
|
164
|
+
))}
|
|
165
|
+
</TagGroup>
|
|
166
|
+
);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Sizes
|
|
170
|
+
|
|
171
|
+
`stories/TagGroup/examples/Sizes.tsx`
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
import React from "react";
|
|
175
|
+
import { Tag, TagGroup, EComponentSize } from "@sberbusiness/triplex-next";
|
|
176
|
+
|
|
177
|
+
const SIZES = Object.values(EComponentSize);
|
|
178
|
+
const TAGS = ["Selected value", "Another value", "One more value"];
|
|
179
|
+
|
|
180
|
+
export const Sizes = () => (
|
|
181
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 24 }}>
|
|
182
|
+
{SIZES.map((size) => (
|
|
183
|
+
<div key={size}>
|
|
184
|
+
<div style={{ marginBottom: 8, fontSize: 16, fontWeight: 700 }}>{size.toUpperCase()}</div>
|
|
185
|
+
<TagGroup size={size} aria-label={`Применённые фильтры, размер ${size}`}>
|
|
186
|
+
{TAGS.map((label, index) => (
|
|
187
|
+
<Tag
|
|
188
|
+
key={label}
|
|
189
|
+
id={`${size}-tag-${index}`}
|
|
190
|
+
size={size}
|
|
191
|
+
onEdit={() => {}}
|
|
192
|
+
onRemove={() => {}}
|
|
193
|
+
editButtonProps={{ "aria-label": "Редактировать" }}
|
|
194
|
+
removeButtonProps={{ "aria-label": "Удалить" }}
|
|
195
|
+
>
|
|
196
|
+
{label}
|
|
197
|
+
</Tag>
|
|
198
|
+
))}
|
|
199
|
+
</TagGroup>
|
|
200
|
+
</div>
|
|
201
|
+
))}
|
|
202
|
+
</div>
|
|
203
|
+
);
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### WithOverflow
|
|
207
|
+
|
|
208
|
+
`stories/TagGroup/examples/WithOverflow.tsx`
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
import React from "react";
|
|
212
|
+
import { Tag, TagGroup, EComponentSize } from "@sberbusiness/triplex-next";
|
|
213
|
+
|
|
214
|
+
const TAGS = Array.from({ length: 6 }, (_, index) => `Selected value ${index + 1}`);
|
|
215
|
+
|
|
216
|
+
export const WithOverflow = () => (
|
|
217
|
+
// maxWidth, а не width: на широком экране в строку помещается четыре тега,
|
|
218
|
+
// на узком контейнер сжимается по месту и теги переносятся раньше.
|
|
219
|
+
<div style={{ maxWidth: 760, border: "1px dashed #D0D4D9" }}>
|
|
220
|
+
<TagGroup size={EComponentSize.LG} aria-label="Применённые фильтры">
|
|
221
|
+
{TAGS.map((label, index) => (
|
|
222
|
+
<Tag
|
|
223
|
+
key={label}
|
|
224
|
+
id={`overflow-tag-${index}`}
|
|
225
|
+
size={EComponentSize.LG}
|
|
226
|
+
onRemove={() => {}}
|
|
227
|
+
removeButtonProps={{ "aria-label": "Удалить" }}
|
|
228
|
+
>
|
|
229
|
+
{label}
|
|
230
|
+
</Tag>
|
|
231
|
+
))}
|
|
232
|
+
</TagGroup>
|
|
233
|
+
</div>
|
|
234
|
+
);
|
|
235
|
+
```
|
|
236
|
+
|