@sberbusiness/triplex-next-mcp-server 1.46.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.
Files changed (48) hide show
  1. package/README.md +7 -7
  2. package/bundle/components/AmountField.md +222 -323
  3. package/bundle/components/Avatar.md +138 -146
  4. package/bundle/components/CardStatic.md +402 -0
  5. package/bundle/components/CheckboxTree.md +157 -215
  6. package/bundle/components/CheckboxTreeExtended.md +7 -7
  7. package/bundle/components/ChipGroup.md +1 -1
  8. package/bundle/components/ChipMonthYearPicker.md +4 -4
  9. package/bundle/components/ChipSuggest.md +7 -6
  10. package/bundle/components/CollapsibleTree.md +8 -4
  11. package/bundle/components/CollapsibleTreeExtended.md +8 -6
  12. package/bundle/components/Confirm.md +346 -281
  13. package/bundle/components/DateRange.md +351 -210
  14. package/bundle/components/FooterPage.md +1 -0
  15. package/bundle/components/FormField.md +11 -4
  16. package/bundle/components/FormGroup.md +3 -2
  17. package/bundle/components/LightBox.md +8 -0
  18. package/bundle/components/ListMaster.md +2 -2
  19. package/bundle/components/ListTableItem.md +6 -6
  20. package/bundle/components/MonthYearField.md +15 -4
  21. package/bundle/components/Slider.md +389 -97
  22. package/bundle/components/SliderRange.md +486 -105
  23. package/bundle/components/StatusTracker.md +386 -94
  24. package/bundle/components/Stepper.md +355 -102
  25. package/bundle/components/StepperExtended.md +633 -0
  26. package/bundle/components/SuggestField.md +1049 -96
  27. package/bundle/components/SwipeableArea.md +7 -7
  28. package/bundle/components/Tabs.md +265 -92
  29. package/bundle/components/TabsExtended.md +18 -18
  30. package/bundle/components/TabsLine.md +301 -127
  31. package/bundle/components/TagColor.md +187 -66
  32. package/bundle/components/TagGroup.md +184 -48
  33. package/bundle/components/TextareaField.md +278 -70
  34. package/bundle/components/TopOverlay.md +17 -1
  35. package/bundle/components/TreeView.md +16 -7
  36. package/bundle/components/TriggerClickOnKeyDownEvent.md +277 -0
  37. package/bundle/components/UploadZone.md +318 -0
  38. package/bundle/guides/codestyle.md +16 -1
  39. package/bundle/guides/commits.md +47 -1
  40. package/bundle/guides/tests.md +23 -2
  41. package/bundle/manifest.json +48 -21
  42. package/bundle/release-notes/1.47.0.mdx +115 -0
  43. package/bundle/release-notes/1.48.0.mdx +68 -0
  44. package/bundle/release-notes/1.49.0.mdx +7 -0
  45. package/bundle/tokens.json +3 -3
  46. package/dist/index.js +220 -6
  47. package/dist/index.js.map +1 -1
  48. package/package.json +1 -1
@@ -234,7 +234,7 @@ export const Default = () => (
234
234
  rightSwipeableArea={
235
235
  <ListItemControls>
236
236
  <ListItemControlsButton
237
- icon={<AttachmentStrokeSrvIcon20 paletteIndex={5} />}
237
+ icon={<AttachmentStrokeSrvIcon20 paletteIndex={0} />}
238
238
  onClick={() => {}}
239
239
  >
240
240
  Скачать
@@ -279,7 +279,7 @@ export const WithBothAreas = () => (
279
279
  leftSwipeableArea={
280
280
  <ListItemControls>
281
281
  <ListItemControlsButton
282
- icon={<DefaulticonStrokePrdIcon20 paletteIndex={5} />}
282
+ icon={<DefaulticonStrokePrdIcon20 paletteIndex={0} />}
283
283
  onClick={() => {}}
284
284
  >
285
285
  В архив
@@ -289,7 +289,7 @@ export const WithBothAreas = () => (
289
289
  rightSwipeableArea={
290
290
  <ListItemControls>
291
291
  <ListItemControlsButton
292
- icon={<AttachmentStrokeSrvIcon20 paletteIndex={5} />}
292
+ icon={<AttachmentStrokeSrvIcon20 paletteIndex={0} />}
293
293
  onClick={() => {}}
294
294
  >
295
295
  Скачать
@@ -368,7 +368,7 @@ export const ImperativeApi = () => {
368
368
  leftSwipeableArea={
369
369
  <ListItemControls>
370
370
  <ListItemControlsButton
371
- icon={<DefaulticonStrokePrdIcon20 paletteIndex={5} />}
371
+ icon={<DefaulticonStrokePrdIcon20 paletteIndex={0} />}
372
372
  onClick={() => {}}
373
373
  >
374
374
  В архив
@@ -378,7 +378,7 @@ export const ImperativeApi = () => {
378
378
  rightSwipeableArea={
379
379
  <ListItemControls>
380
380
  <ListItemControlsButton
381
- icon={<AttachmentStrokeSrvIcon20 paletteIndex={5} />}
381
+ icon={<AttachmentStrokeSrvIcon20 paletteIndex={0} />}
382
382
  onClick={() => {}}
383
383
  >
384
384
  Скачать
@@ -451,13 +451,13 @@ export const Example = () => {
451
451
  rightSwipeableArea={
452
452
  <ListItemControls>
453
453
  <ListItemControlsButton
454
- icon={<AttachmentStrokeSrvIcon20 paletteIndex={5} />}
454
+ icon={<AttachmentStrokeSrvIcon20 paletteIndex={0} />}
455
455
  onClick={() => {}}
456
456
  >
457
457
  Скачать
458
458
  </ListItemControlsButton>
459
459
  <ListItemControlsButtonDropdown
460
- icon={<DotshorizontalStrokeSrvIcon20 paletteIndex={5} />}
460
+ icon={<DotshorizontalStrokeSrvIcon20 paletteIndex={0} />}
461
461
  options={[
462
462
  { id: `${payment.id}-option-1`, label: "Повторить", onSelect: () => {} },
463
463
  { id: `${payment.id}-option-2`, label: "Удалить", onSelect: () => {} },
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  component: Tabs
3
- category: Navigation
4
- figma-node: ""
5
- figma-file: ""
6
- related: [TabsExtended, TabsLine]
7
- tokens: []
3
+ category: Tabs
4
+ related: [TabsExtended, TabsLine, ButtonDropdown]
5
+ tokens:
6
+ - Tabs.Type1_Tab_Background_Selected
7
+ - Tabs.Type2_Tab_Background_Selected
8
8
  stories: stories/Tabs/Tabs.stories.tsx
9
9
  version: "1.0"
10
10
  ---
@@ -13,135 +13,121 @@ version: "1.0"
13
13
 
14
14
  ## Назначение
15
15
 
16
- Готовый компонент табов с overflow-меню (`ButtonDropdown` с `DOTS_SECONDARY`):
17
- табы, не помещающиеся в строку, автоматически уезжают в выпадающее меню.
18
- Принимает простой массив `tabs: ITabsItem[]` вместо children-композиции.
19
- Под капотом — `TabsExtended` с фиксированной разметкой
20
- `Content > TabsWrapper + DropdownWrapper`.
16
+ Готовые табы-переключатели поверх `TabsExtended`: компонент принимает массив табов, сам собирает разметку, сам рендерит кнопку выпадающего списка для не поместившихся табов и сам обрабатывает навигацию стрелками.
21
17
 
22
- Используй когда: нужен стандартный таб-бар с поддержкой переполнения.
23
- Для кастомной разметки (свой dropdown, иконки, другие заголовки) —
24
- `TabsExtended` напрямую. Для простых горизонтальных линейных табов без
25
- overflow — `TabsLine`.
18
+ Используй когда: нужен типовой набор табов «лейбл + опциональный значок уведомлений» — это первый выбор для обычной задачи.
19
+ Не используй когда:
26
20
 
27
- ---
28
-
29
- ## Файловая структура
30
-
31
- ```text
32
- src/components/Tabs/
33
- ├── Tabs.tsx # FC<ITabsProps>; склейка TabsExtended + ButtonDropdown
34
- ├── styles/
35
- │ ├── Tabs.module.less
36
- │ └── Tabs.less
37
- └── index.ts
38
- ```
21
+ - содержимое таба нестандартное (своя разметка, свой триггер выпадающего списка) — бери `TabsExtended`, `Tabs` собран поверх него;
22
+ - нужны табы-подчёркивания в шапке раздела — бери `TabsLine`;
23
+ - нужен набор переключателей без семантики табов — это не задача компонента.
39
24
 
40
25
  ---
41
26
 
42
- ## Публичный API
27
+ ## Варианты и props
43
28
 
44
- ### `ITabsItem`
29
+ `ITabsProps` расширяет `ITabsExtendedProps` (а тот — `React.HTMLAttributes<HTMLDivElement>`). Неизвестные компоненту атрибуты уходят на корневой `<div>` с `role="tablist"`.
45
30
 
46
- ```ts
47
- extends Omit<ITabsExtendedTabProps, "children" | "onSelect">
48
- ```
31
+ ### Обязательные props
49
32
 
50
- | Поле | Тип | Описание |
33
+ | Prop | Тип | Описание |
51
34
  |---|---|---|
52
- | `id` | `string` | Уникальный id таба (используется в `selectedId`/`onSelectTab`). |
53
- | `label` | `ReactNode` | Содержимое кнопки таба. |
54
- | `showNotificationIcon?` | `boolean` | Точка-индикатор уведомления на табе. |
55
-
56
- ### `ITabsProps`
35
+ | `tabs` | `Array<ITabsItem>` | Состав табов. Порядок массива = порядок в строке и порядок обхода стрелками |
36
+ | `selectedId` | `string` | Id выбранного таба. Компонент полностью управляемый: своего состояния выбора у него нет |
37
+ | `onSelectTab` | `(selectedId: string) => void` | Запрос на смену выбранного таба. Вызывается **только** если выбирают таб, отличный от `selectedId` — и из строки, и из выпадающего списка |
57
38
 
58
- ```ts
59
- extends ITabsExtendedProps
60
- ```
39
+ ### Опциональные props
61
40
 
62
- | Prop | Тип | Default | Описание |
41
+ | Prop | Тип | По умолчанию | Описание |
63
42
  |---|---|---|---|
64
- | `tabs` | `ITabsItem[]` | — | Массив табов. **Обязателен.** |
65
- | `selectedId` | `string` | — | Id выбранного таба (от `ITabsExtendedProps`). |
66
- | `onSelectTab` | `(selectedId: string) => void` | — | Колбэк выбора таба. |
67
- | `size?` | `EComponentSize` | `MD` | Размер табов и кнопки overflow. |
68
- | `buttonDropdownAttributes?` | `ButtonHTMLAttributes<HTMLButtonElement>` | — | HTML-атрибуты для `ButtonDropdown`. |
69
- | `type?` | `ETabsExtendedType` | `TYPE_1` | Тип оформления (от `TabsExtended`). |
70
- | `children` | `never` | — | Запрещено — структура фиксирована. |
71
- | `...HTMLAttributes<HTMLDivElement>` | — | — | На корневой `TabsExtended` (role=tablist). |
43
+ | `type` | `ETabsExtendedType` | `TYPE_1` | Тип оформления, выбирается по фону **страницы**: `TYPE_1` — серый фон контейнера, для белой страницы; `TYPE_2` — белый фон контейнера, для затенённой страницы |
44
+ | `size` | `EComponentSize` | `MD` | Один prop на весь компонент: задаёт и контейнер, и кнопки табов, и кнопку выпадающего списка (в отличие от `TabsExtended`, где размеры независимы) |
45
+ | `buttonDropdownAttributes` | `React.ButtonHTMLAttributes<HTMLButtonElement>` | — | HTML-атрибуты кнопки выпадающего списка. Сюда передаётся `aria-label`; переданный `className` мерджится с собственными классами кнопки, не затирая их |
46
+ | `children` | `never` | — | Запрещены типом: состав задаётся только через `tabs` |
72
47
 
73
- ---
48
+ ### Поля элемента `tabs` (`ITabsItem`)
74
49
 
75
- ## Поведение
50
+ `ITabsItem` расширяет `Omit<ITabsExtendedTabProps, "children" | "onSelect">`, то есть принимает и HTML-атрибуты контейнера таба (`<span>`).
76
51
 
77
- ### Overflow → Dropdown
52
+ | Поле | Тип | Описание |
53
+ |---|---|---|
54
+ | `id` | `string` | Id таба. Сопоставляется с `selectedId` и приходит в `onSelectTab` |
55
+ | `label` | `React.ReactNode` | Содержимое кнопки таба |
56
+ | `showNotificationIcon` | `boolean` | Значок новых уведомлений (`Badge.Dot`) на кнопке таба и на пункте выпадающего списка |
57
+
58
+ ### Ограничения
78
59
 
79
- `TabsExtended.Content.DropdownWrapper` получает массив `dropdownItemsIds`
80
- табов, не поместившихся в строку. `Tabs` фильтрует `tabs` по этим id и
81
- передаёт в `ButtonDropdown.options`. Выбранный таб подсвечивается через
82
- `selected={tabs.find(t => t.id === selectedId)}`.
60
+ - `label` и `showNotificationIcon` — поля кнопки таба, на контейнер таба они не уходят; остальные поля элемента считаются HTML-атрибутами `<span>`-контейнера.
61
+ - Компонент не хардкодит текст: без `buttonDropdownAttributes["aria-label"]` кнопка выпадающего списка остаётся без доступного имени.
62
+ - Выпадающий список рендерится всегда, но скрыт (`hidden`), пока все табы помещаются в строку.
63
+ - `id` табов должны быть уникальными: по ним идёт и сопоставление с `selectedId`, и React-ключи, и замеры ширины.
83
64
 
84
- ### Keyboard navigation
65
+ ---
85
66
 
86
- Roving tabindex по `availableToFocusTabId`:
67
+ ## Дизайн-токены
87
68
 
88
- - `ArrowLeft` → переключает фокус на предыдущий таб (если не первый в строке —
89
- `isFirstInlineTab`), `preventDefault` для подавления скролла.
90
- - `ArrowRight` → следующий таб (если не последний в строке — `isLastInlineTab`).
91
- - При `onFocus` запоминаются id `prevAvailableToFocusTabId` /
92
- `nextAvailableToFocusTabId` — на их кнопки навешиваются `prevTabRef` /
93
- `nextTabRef` для программного `.focus()`.
69
+ Переопределяются через `ThemeProvider` (prop `tokens`) — см. `ThemeProvider-ai.md` →
70
+ «Как переопределять токены». Значения по умолчанию — `src/components/DesignTokens/components/Tabs.ts`.
94
71
 
95
- `isFirstInlineTab`/`isLastInlineTab` приходят через render-prop
96
- `<TabsExtended.Content.Tab>{({ selected, isFirstInlineTab, isLastInlineTab }) => ...}</TabsExtended.Content.Tab>`.
72
+ Собственные стили компонента — только подсветка кнопки выпадающего списка, когда выбранный таб уехал в список:
97
73
 
98
- ### Defaults
74
+ ```text
75
+ Tabs.Type1_Tab_Background_Selected
76
+ Tabs.Type2_Tab_Background_Selected
77
+ ```
99
78
 
100
- - `size = EComponentSize.MD`
101
- - `availableToFocusTabId` инициализируется как `selectedId || tabs[0].id` —
102
- при пустом `tabs` упадёт (`tabs[0].id`); потребитель должен передавать
103
- непустой массив.
79
+ Оформление контейнера и самих кнопок табов приходит из `TabsExtended` — он использует всё семейство токенов `Tabs`, общее для обоих компонентов.
104
80
 
105
81
  ---
106
82
 
107
83
  ## Инварианты
108
84
 
109
- - **`children: never`** — структура зашита (`TabsWrapper` + `DropdownWrapper`).
110
- Для своей разметки — `TabsExtended`.
111
- - **`tabs` непустой** — `tabs[0].id` читается без guard'а.
112
- - **`onSelectTab` обязателен** — таб не сам мутирует `selectedId`, контроллер на потребителе.
113
- - **`selectedId` управляемый** — `TabsExtended` гарантирует, что повторный выбор того же id не триггерит `onSelectTab` (`if (selectedId !== id)`).
114
- - **Overflow-кнопка — `ButtonDropdown` с `EButtonDotsTheme.DOTS_SECONDARY`** — менять только осознанно (UX-канон).
115
- - **Размер кнопки overflow привязан к `size`** через `clsx(styles.tabButtonDropdown, styles[size])`.
116
- - **`FC, не forwardRef`** — ref не пробрасывается. Если нужен ref на `tablist` — использовать `TabsExtended` напрямую.
85
+ - `Tabs` объявлен через `React.forwardRef<HTMLDivElement, ITabsProps>`, `ref` уходит на корневой `<div role="tablist">` компонента `TabsExtended`; `displayName = "Tabs"`. Зафиксировано unit-тестом.
86
+ - Публичные имена `ITabsProps`, `ITabsItem`, `Tabs` и barrel `src/components/Tabs/index.ts` — часть публичного API.
87
+ - Компонент строится на `TabsExtended` и опирается на флаги `isFirstInlineTab` / `isLastInlineTab` из render-prop `Tab`: на них держатся границы навигации стрелками. Изменение их семантики ломает клавиатуру `Tabs`.
88
+ - Класс `.tabButtonDropdown` согласует высоту кнопки выпадающего списка с высотой кнопки таба (SM — 20px, MD — 32px, LG — 40px). Без него `ButtonDropdown` выше таба того же размера и растягивает и контейнер, и сами табы.
89
+ - `label` и `showNotificationIcon` обязаны вырезаться из элемента `tabs` перед спредом на `TabsExtended.Content.Tab`: иначе они уходят на `<span>` как неизвестные DOM-атрибуты, и React пишет предупреждение в консоль. Зафиксировано unit-тестом.
90
+ - Массив `tabs` может быть пустым — компонент не должен падать (поэтому запасной id фокусируемого таба читается через optional chaining).
91
+ - Таб с `tabIndex = 0` вычисляется на каждом рендере, а не хранится готовым в state: в state лежит только таб, последним получивший фокус, вместе с `selectedId` на тот момент. `inlineItemsIds` сверяется с текущим `tabs`: `TabsExtended` пересчитывает раскладку только при смене числа табов или ширины, и после замены `tabs` на массив той же длины там остаются старые id. Распределение табов по строке и выпадающему списку `Tabs` читает из `TabsExtendedContext` (`inlineItemsIds`) через `Consumer` внутри render-prop таба — сам `Tabs` рендерит провайдер и прочитать контекст хуком не может.
117
92
 
118
93
  ---
119
94
 
120
95
  ## Accessibility
121
96
 
122
- - Корневой `<div role="tablist">` приходит из `TabsExtended`.
123
- - На каждой кнопке таба (`TabsExtended.Content.TabButton`) — `role="tab"` + `aria-selected`.
124
- - Roving tabindex (`tabIndex = availableToFocusTabId === item.id ? 0 : -1`) —
125
- только один таб в tab-стопе клавиатуры.
126
- - ArrowLeft/ArrowRight перемещают фокус **только в пределах inline-табов**
127
- (не в overflow-меню) — для overflow используется родной dropdown.
128
- - `showNotificationIcon` — визуальный индикатор; для screen-reader потребитель
129
- должен добавить текстовый намёк в `label` сам.
97
+ - Роли приходят из `TabsExtended`: корневой элемент — `role="tablist"`, кнопка таба — `<button role="tab">` с `aria-selected`, контейнер таба — `role="presentation"`.
98
+ - **Roving tabindex:** в таб-порядок страницы попадает ровно один таб (`tabIndex = 0`), остальные получают `tabIndex = -1`. Кандидаты по приоритету: таб, последним получивший фокус (стрелкой или кликом), — пока выбран тот же таб, что и в момент фокуса (смена выбора, в том числе извне через `selectedId`, его сбрасывает); затем выбранный таб (или первый, если `selectedId` пуст). `tabIndex = 0` получает первый кандидат, отображаемый в строке, а если оба в выпадающем списке или отсутствуют в `tabs` — первый таб строки. Строка табов не выпадает из обхода по `Tab`.
99
+ - **Клавиатура:** `ArrowLeft` / `ArrowRight` переносят фокус на соседний таб **в строке** и делают его единственным табом с `tabIndex = 0`. На краях строки фокус остаётся на месте (переноса на противоположный край нет). Для обеих стрелок вызывается `preventDefault` — чтобы страница не скроллилась; другие клавиши компонент не перехватывает.
100
+ - Выбор таба — нативной активацией `<button>` (`Enter` / `Space`) и кликом. Стрелки только перемещают фокус и **не меняют** выбранный таб.
101
+ - Табы, уехавшие в выпадающий список, скрыты через `display: none` и из обхода клавиатурой выпадают: попасть в них можно только через список.
102
+ - `aria-label` кнопки выпадающего списка задаёт потребитель через `buttonDropdownAttributes` — компонент текста не хардкодит.
103
+ - `aria-controls` и связку с панелью контента компонент не выставляет: если табы переключают панели, их `id` и `aria-controls` проставляет потребитель.
130
104
 
131
105
  ---
132
106
 
133
107
  ## Связанные компоненты
134
108
 
135
- - `TabsExtended` — низкоуровневый базовый компонент со слотами.
136
- - `TabsLine` — горизонтальные табы без overflow-меню.
137
- - `ButtonDropdown` (`EButtonDotsTheme.DOTS_SECONDARY`) — кнопка overflow.
109
+ - `TabsExtended` — база: `Tabs` рендерит его субкомпоненты и наследует от его интерфейсов (`ITabsProps extends ITabsExtendedProps`, `ITabsItem extends Omit<ITabsExtendedTabProps, ...>`). Туда же уходят за нестандартной разметкой табов.
110
+ - `TabsLine` — альтернативный визуальный паттерн табов (подчёркивание вместо залитой кнопки).
111
+ - `ButtonDropdown` — кнопка выпадающего списка, которую `Tabs` рендерит сам для не поместившихся табов; тема подбирается по `type` (`DOTS_SECONDARY` / `DOTS_SECONDARY_LIGHT`).
112
+ - `Badge` — `Badge.Dot` как значок новых уведомлений на кнопке таба (рендерится внутри `TabsExtended.Content.TabButton`).
138
113
 
139
114
  ---
140
115
 
141
116
  ## Stories
142
117
 
143
118
  Основные истории: `stories/Tabs/Tabs.stories.tsx`
144
- Примеры: `stories/Tabs/examples/`
119
+ Файлы примеров: `stories/Tabs/examples/`
120
+
121
+ | Story | Example file | Что демонстрирует |
122
+ |---|---|---|
123
+ | `Playground` | `Playground.tsx` | Интерактивный контроль типа, размера, количества табов, ширины контейнера и значка уведомлений |
124
+ | `Default` | `Default.tsx` | Минимальный набор: три таба, всё помещается в строку |
125
+ | `Types` | `Types.tsx` | Типы оформления `TYPE_1` и `TYPE_2` |
126
+ | `Sizes` | `Sizes.tsx` | Размеры SM / MD / LG |
127
+ | `WithNotificationIcon` | `WithNotificationIcon.tsx` | Значок новых уведомлений на табе |
128
+ | `WithDropdown` | `WithDropdown.tsx` | Узкий контейнер: часть табов уезжает в выпадающий список |
129
+ | `VisualTests` | `VisualTests.tsx` | Типы × размеры, значок уведомлений, переполнение строки, подсветка кнопки списка при выбранном скрытом табе, `:focus-visible` на табе (через `play`) |
130
+ | `VisualTestsOpen` | `VisualTests.tsx` | Скриншот-регрессия раскрытого выпадающего списка (открывается через `play`) |
145
131
 
146
132
  ---
147
133
 
@@ -149,4 +135,191 @@ Roving tabindex по `availableToFocusTabId`:
149
135
 
150
136
  | Дата | Изменение |
151
137
  |---|---|
152
- | 2026-04-18 | Создан документ |
138
+ | 2026-09-22 | Создан документ AI-ready для `Tabs` (TRI-90). |
139
+ | 2026-09-22 | AI-рефакторинг: `label` и `showNotificationIcon` больше не уходят на контейнер таба как DOM-атрибуты (React-предупреждение в консоли), React-ключ таба переведён с индекса на `id`, пустой массив `tabs` не роняет компонент, JSDoc проставлен на всех публичных props. Публичный API (имена props, значения enum, barrel-экспорты) не изменён. Добавлены unit-тесты на клавиатуру, выбор таба и выпадающий список, stories переписаны по modern pattern. |
140
+ | 2026-09-22 | `Tabs` переведён на `forwardRef` (ref на корневой `<div role="tablist">`) и получил `displayName`; тип компонента — `React.ForwardRefExoticComponent`. Roving tabindex синхронизирован с `selectedId` и с распределением табов по строке и выпадающему списку: строка табов больше не выпадает из обхода по `Tab`. Добавлены unit-тесты на `ref`, `displayName` и перенос `tabIndex = 0` (внешняя смена выбора, клик, уезд таба в выпадающий список, замена `tabs`). |
141
+
142
+ ---
143
+
144
+ ## Примеры использования
145
+
146
+ ### Default
147
+
148
+ `stories/Tabs/examples/Default.tsx`
149
+
150
+ ```tsx
151
+ import React, { useState } from "react";
152
+ import { Tabs } from "@sberbusiness/triplex-next";
153
+
154
+ const TABS = [
155
+ { id: "tab-1", label: "Tab Name 1" },
156
+ { id: "tab-2", label: "Tab Name 2" },
157
+ { id: "tab-3", label: "Tab Name 3" },
158
+ ];
159
+
160
+ export const Default = () => {
161
+ const [selectedId, setSelectedId] = useState("tab-1");
162
+
163
+ return (
164
+ <Tabs
165
+ tabs={TABS}
166
+ selectedId={selectedId}
167
+ onSelectTab={setSelectedId}
168
+ buttonDropdownAttributes={{ "aria-label": "Ещё табы" }}
169
+ />
170
+ );
171
+ };
172
+ ```
173
+
174
+ ### Types
175
+
176
+ `stories/Tabs/examples/Types.tsx`
177
+
178
+ ```tsx
179
+ import React, { useState } from "react";
180
+ import { ETabsExtendedType, Tabs } from "@sberbusiness/triplex-next";
181
+
182
+ const TABS = [
183
+ { id: "tab-1", label: "Tab Name 1" },
184
+ { id: "tab-2", label: "Tab Name 2" },
185
+ { id: "tab-3", label: "Tab Name 3" },
186
+ ];
187
+
188
+ interface ITypeItemProps {
189
+ caption: string;
190
+ type: ETabsExtendedType;
191
+ }
192
+
193
+ const TypeItem = ({ caption, type }: ITypeItemProps) => {
194
+ const [selectedId, setSelectedId] = useState("tab-1");
195
+
196
+ return (
197
+ <div>
198
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>{caption}</div>
199
+ <Tabs
200
+ tabs={TABS}
201
+ type={type}
202
+ selectedId={selectedId}
203
+ onSelectTab={setSelectedId}
204
+ buttonDropdownAttributes={{ "aria-label": "Ещё табы" }}
205
+ />
206
+ </div>
207
+ );
208
+ };
209
+
210
+ export const Types = () => (
211
+ <div style={{ display: "flex", flexDirection: "column", gap: "24px" }}>
212
+ <TypeItem caption="TYPE_1 — для белой страницы" type={ETabsExtendedType.TYPE_1} />
213
+ <TypeItem caption="TYPE_2 — для затенённой страницы" type={ETabsExtendedType.TYPE_2} />
214
+ </div>
215
+ );
216
+ ```
217
+
218
+ ### Sizes
219
+
220
+ `stories/Tabs/examples/Sizes.tsx`
221
+
222
+ ```tsx
223
+ import React, { useState } from "react";
224
+ import { EComponentSize, Tabs } from "@sberbusiness/triplex-next";
225
+
226
+ const TABS = [
227
+ { id: "tab-1", label: "Tab Name 1" },
228
+ { id: "tab-2", label: "Tab Name 2" },
229
+ { id: "tab-3", label: "Tab Name 3" },
230
+ ];
231
+
232
+ interface ISizeItemProps {
233
+ size: EComponentSize;
234
+ }
235
+
236
+ const SizeItem = ({ size }: ISizeItemProps) => {
237
+ const [selectedId, setSelectedId] = useState("tab-1");
238
+
239
+ return (
240
+ <div>
241
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>{size.toUpperCase()}</div>
242
+ <Tabs
243
+ tabs={TABS}
244
+ size={size}
245
+ selectedId={selectedId}
246
+ onSelectTab={setSelectedId}
247
+ buttonDropdownAttributes={{ "aria-label": "Ещё табы" }}
248
+ />
249
+ </div>
250
+ );
251
+ };
252
+
253
+ const SIZES = Object.values(EComponentSize);
254
+
255
+ export const Sizes = () => (
256
+ <div style={{ display: "flex", flexDirection: "column", gap: "24px" }}>
257
+ {SIZES.map((size) => (
258
+ <SizeItem key={size} size={size} />
259
+ ))}
260
+ </div>
261
+ );
262
+ ```
263
+
264
+ ### WithNotificationIcon
265
+
266
+ `stories/Tabs/examples/WithNotificationIcon.tsx`
267
+
268
+ ```tsx
269
+ import React, { useState } from "react";
270
+ import { Tabs } from "@sberbusiness/triplex-next";
271
+
272
+ const TABS = [
273
+ { id: "tab-1", label: "Tab Name 1" },
274
+ { id: "tab-2", label: "Tab Name 2", showNotificationIcon: true },
275
+ { id: "tab-3", label: "Tab Name 3" },
276
+ ];
277
+
278
+ export const WithNotificationIcon = () => {
279
+ const [selectedId, setSelectedId] = useState("tab-1");
280
+
281
+ return (
282
+ <Tabs
283
+ tabs={TABS}
284
+ selectedId={selectedId}
285
+ onSelectTab={setSelectedId}
286
+ buttonDropdownAttributes={{ "aria-label": "Ещё табы" }}
287
+ />
288
+ );
289
+ };
290
+ ```
291
+
292
+ ### WithDropdown
293
+
294
+ `stories/Tabs/examples/WithDropdown.tsx`
295
+
296
+ ```tsx
297
+ import React, { useState } from "react";
298
+ import { Tabs } from "@sberbusiness/triplex-next";
299
+
300
+ const TABS = [
301
+ { id: "tab-1", label: "Tab Name 1" },
302
+ { id: "tab-2", label: "Tab Name 2" },
303
+ { id: "tab-3", label: "Tab Name 3" },
304
+ { id: "tab-4", label: "Tab Name 4" },
305
+ { id: "tab-5", label: "Tab Name 5" },
306
+ { id: "tab-6", label: "Tab Name 6" },
307
+ ];
308
+
309
+ export const WithDropdown = () => {
310
+ const [selectedId, setSelectedId] = useState("tab-1");
311
+
312
+ return (
313
+ // Ширина контейнера ограничена, чтобы часть табов не поместилась в строку и уехала в выпадающий список.
314
+ <div style={{ maxWidth: "420px" }}>
315
+ <Tabs
316
+ tabs={TABS}
317
+ selectedId={selectedId}
318
+ onSelectTab={setSelectedId}
319
+ buttonDropdownAttributes={{ "aria-label": "Ещё табы" }}
320
+ />
321
+ </div>
322
+ );
323
+ };
324
+ ```
325
+
@@ -203,9 +203,9 @@ import React, { useState } from "react";
203
203
  import { TabsExtended } from "@sberbusiness/triplex-next";
204
204
 
205
205
  const TABS = [
206
- { id: "tab-1", label: "Обзор" },
207
- { id: "tab-2", label: "Операции" },
208
- { id: "tab-3", label: "Документы" },
206
+ { id: "tab-1", label: "Tab Name 1" },
207
+ { id: "tab-2", label: "Tab Name 2" },
208
+ { id: "tab-3", label: "Tab Name 3" },
209
209
  ];
210
210
 
211
211
  export const Default = () => {
@@ -240,9 +240,9 @@ import React, { useState } from "react";
240
240
  import { ETabsExtendedType, TabsExtended } from "@sberbusiness/triplex-next";
241
241
 
242
242
  const TABS = [
243
- { id: "tab-1", label: "Обзор" },
244
- { id: "tab-2", label: "Операции" },
245
- { id: "tab-3", label: "Документы" },
243
+ { id: "tab-1", label: "Tab Name 1" },
244
+ { id: "tab-2", label: "Tab Name 2" },
245
+ { id: "tab-3", label: "Tab Name 3" },
246
246
  ];
247
247
 
248
248
  interface ITypeItemProps {
@@ -294,9 +294,9 @@ import React, { useState } from "react";
294
294
  import { EComponentSize, TabsExtended } from "@sberbusiness/triplex-next";
295
295
 
296
296
  const TABS = [
297
- { id: "tab-1", label: "Обзор" },
298
- { id: "tab-2", label: "Операции" },
299
- { id: "tab-3", label: "Документы" },
297
+ { id: "tab-1", label: "Tab Name 1" },
298
+ { id: "tab-2", label: "Tab Name 2" },
299
+ { id: "tab-3", label: "Tab Name 3" },
300
300
  ];
301
301
 
302
302
  interface ISizeItemProps {
@@ -349,9 +349,9 @@ import React, { useState } from "react";
349
349
  import { TabsExtended } from "@sberbusiness/triplex-next";
350
350
 
351
351
  const TABS = [
352
- { id: "tab-1", label: "Обзор", showNotificationIcon: false },
353
- { id: "tab-2", label: "Операции", showNotificationIcon: true },
354
- { id: "tab-3", label: "Документы", showNotificationIcon: true },
352
+ { id: "tab-1", label: "Tab Name 1", showNotificationIcon: false },
353
+ { id: "tab-2", label: "Tab Name 2", showNotificationIcon: true },
354
+ { id: "tab-3", label: "Tab Name 3", showNotificationIcon: true },
355
355
  ];
356
356
 
357
357
  export const WithNotificationIcon = () => {
@@ -395,12 +395,12 @@ import {
395
395
  } from "@sberbusiness/triplex-next";
396
396
 
397
397
  const TABS = [
398
- { id: "tab-1", label: "Обзор" },
399
- { id: "tab-2", label: "Операции" },
400
- { id: "tab-3", label: "Документы" },
401
- { id: "tab-4", label: "Реквизиты" },
402
- { id: "tab-5", label: "Выписка" },
403
- { id: "tab-6", label: "Настройки" },
398
+ { id: "tab-1", label: "Tab Name 1" },
399
+ { id: "tab-2", label: "Tab Name 2" },
400
+ { id: "tab-3", label: "Tab Name 3" },
401
+ { id: "tab-4", label: "Tab Name 4" },
402
+ { id: "tab-5", label: "Tab Name 5" },
403
+ { id: "tab-6", label: "Tab Name 6" },
404
404
  ];
405
405
 
406
406
  /**