@sberbusiness/triplex-next-mcp-server 1.40.0-beta → 1.41.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.
@@ -1,9 +1,7 @@
1
1
  ---
2
2
  component: Island
3
3
  category: Layout
4
- figma-node: ""
5
- figma-file: ""
6
- related: [IslandAccordion, IslandWidget, Card, Page]
4
+ related: [IslandAccordion, IslandWidget, BodyPage, HeaderPage, FooterPage, Confirm, ModalWindowBody]
7
5
  tokens:
8
6
  - --triplex-next-Island-Type1_Background
9
7
  - --triplex-next-Island-Type1_Shadow
@@ -19,179 +17,273 @@ version: "1.0"
19
17
 
20
18
  ## Назначение
21
19
 
22
- Карточка-"остров" — контейнер со скруглениями, фоном и тенью. **Compound-компонент**
23
- с тремя дочерними слотами: `Island.Header`, `Island.Body`, `Island.Footer`.
24
- Имеет три визуальных типа (`TYPE_1` / `TYPE_2` / `TYPE_3`) — отличаются
25
- цветом фона и тенью — и три размера (`SM`/`MD`/`LG`) — отличаются
26
- радиусом, padding'ом и межблочным margin'ом.
20
+ Базовый контейнер-карточка библиотеки: `<div>` с фоном, тенью, скруглением и внутренними
21
+ отступами. Собственной логики не имеет — задаёт поверхность, на которой лежит контент, и
22
+ вертикальные отступы между своими частями `Island.Header`, `Island.Body`, `Island.Footer`.
27
23
 
28
- Используй когда: нужна базовая карточка с заголовком, контентом и футером.
29
- Для аккордеона предпочитай `IslandAccordion`, для виджета с иконкой —
30
- `IslandWidget`.
24
+ Используй когда: контент нужно визуально выделить карточкой на фоне страницы — блок страницы
25
+ (`BodyPage`, `HeaderPage`, `FooterPage`), диалог подтверждения (`Confirm`), элемент аккордеона
26
+ (`IslandAccordion`), виджет (`IslandWidget`).
31
27
 
32
- ---
33
-
34
- ## Файловая структура
35
-
36
- ```text
37
- src/components/Island/
38
- ├── Island.tsx # Object.assign → Body/Header/Footer
39
- ├── enums.ts # EIslandType (TYPE_1/TYPE_2/TYPE_3)
40
- ├── utils.ts # mapTypeToClassName(type) → styles.type1/2/3
41
- ├── index.ts # Barrel
42
- ├── components/
43
- │ ├── IslandHeader.tsx # <div .islandHeader>
44
- │ ├── IslandBody.tsx # <div .islandBody>
45
- │ └── IslandFooter.tsx # <div .islandFooter>
46
- └── styles/
47
- ├── Island.module.less # Типы, размеры, mobile-ресайз
48
- ├── IslandHeader.module.less
49
- ├── IslandBody.module.less
50
- └── IslandFooter.module.less
51
- ```
28
+ Не используй когда:
29
+ - Нужна интерактивность (клик по карточке, раскрытие, удаление) — Island не кнопка и не
30
+ управляет состоянием. Возьми `IslandAccordion` (раскрывающиеся элементы) или `IslandWidget`
31
+ (виджет со сворачиванием в адаптиве), либо положи интерактивный элемент внутрь Island.
32
+ - Нужен только отступ или сетка без визуальной карточки — возьми `Gap`, `Row`/`Col`.
33
+ - Нужен overlay/модальное окно — возьми `ModalWindow`, `LightBox`, `LightBoxSideOverlay`.
52
34
 
53
35
  ---
54
36
 
55
- ## Compound-структура
37
+ ## Варианты и props
56
38
 
57
- ```tsx
58
- <Island type={EIslandType.TYPE_1} size={EComponentSize.MD}>
59
- <Island.Header>Заголовок</Island.Header>
60
- <Island.Body>Основной контент</Island.Body>
61
- <Island.Footer>
62
- <Button>Действие</Button>
63
- </Island.Footer>
64
- </Island>
65
- ```
39
+ ### Обязательные props
66
40
 
67
- Все три слота опциональны. Отступы между ними задаются смежным CSS-селектором
68
- на корне:
69
- ```less
70
- .islandHeader + .islandBody,
71
- .islandHeader + .islandFooter,
72
- .islandBody + .islandFooter { margin-top: <size>; }
73
- ```
74
- Размер зазора меняется со `size` (8/12/16 px).
41
+ Обязательных props нет — `<Island>` рендерится с типом `TYPE_1` и размером `MD`.
75
42
 
76
- ---
43
+ ### Опциональные props
77
44
 
78
- ## Варианты и props
79
-
80
- ### Island — `IIslandProps extends HTMLAttributes<HTMLDivElement>`
81
-
82
- | Prop | Тип | Default | Описание |
45
+ | Prop | Тип | По умолчанию | Описание |
83
46
  |---|---|---|---|
84
- | `type` | `EIslandType` | `TYPE_1` | Визуальный тип (цвет фона + тень). |
85
- | `size` | `EComponentSize` | `MD` | Размер — влияет на padding и border-radius. |
86
- | `className`, `style`, `...HTMLDivAttributes` | — | — | Стандартные. |
47
+ | `type` | `EIslandType` | `EIslandType.TYPE_1` | Визуальный тип. Влияет только на фон и тень, на размеры — нет |
48
+ | `size` | `EComponentSize` | `EComponentSize.MD` | Скругление, внутренние отступы карточки и отступы между Header / Body / Footer |
87
49
 
88
- ### EIslandType
89
-
90
- | Значение | Токены |
91
- |---|---|
92
- | `TYPE_1` | `--triplex-next-Island-Type1_Background` + `Type1_Shadow` |
93
- | `TYPE_2` | `...Type2...` |
94
- | `TYPE_3` | `...Type3...` |
50
+ Компонент расширяет `React.HTMLAttributes<HTMLDivElement>` — все стандартные атрибуты `<div>`
51
+ (`className`, `style`, `role`, `aria-*`, `data-*`, обработчики событий) попадают на корневой элемент.
95
52
 
96
53
  ### Размеры
97
54
 
98
- | Size | Desktop (≥ sm+1) | Mobile (≤ @screen-sm-max) | Gap между слотами |
99
- |---|---|---|---|
100
- | `SM` | radius 16, padding 16 | radius 16, padding 16 | 8 |
101
- | `MD` | radius 24, padding 24 | radius 16, padding 16 | 12 |
102
- | `LG` | radius 32, padding 32 | radius 24, padding 24 | 16 |
103
-
104
- На mobile `MD` уменьшается до SM-параметров; `LG` — до размеров близких к MD. Это сохранить при добавлении новых размеров.
55
+ | `size` | `border-radius` | `padding` | Отступ между частями | Адаптив (`max-width: @screen-sm-max`) |
56
+ |---|---|---|---|---|
57
+ | `SM` | 16px | 16px | 8px | без изменений |
58
+ | `MD` | 24px | 24px | 12px | схлопывается до 16px / 16px |
59
+ | `LG` | 32px | 32px | 16px | схлопывается до 24px / 24px |
60
+
61
+ Отступ между частями схлопыванию в адаптиве не подвергается — меняются только скругление и padding.
62
+
63
+ ### Составные части
64
+
65
+ `Island.Header`, `Island.Body`, `Island.Footer` (они же `IslandHeader`, `IslandBody`,
66
+ `IslandFooter` из barrel) — тривиальные `<div>`-обёртки: `className + ...rest + forwardRef`,
67
+ собственных props нет. Их единственная задача — нести класс, по которому Island расставляет
68
+ вертикальные отступы. Использовать их все необязательно: у карточки может быть только Body.
69
+
70
+ ### Ограничения
71
+
72
+ - **Части должны идти соседними элементами в порядке Header → Body → Footer.**
73
+ Отступы задаются смежными селекторами (`.islandHeader + .islandBody`,
74
+ `.islandHeader + .islandFooter`, `.islandBody + .islandFooter`) внутри классов размера.
75
+ Обёртка вокруг части, чужой элемент между частями или другой порядок ломают отступы.
76
+ Пример из библиотеки: `IslandAccordion` оборачивает `Island.Body` и `Island.Footer` в
77
+ `ExpandAnimation`, поэтому `.islandHeader + .islandBody` там не срабатывает и отступ между
78
+ шапкой и телом задаётся собственными паддингами аккордеона; тело и подвал при этом остаются
79
+ соседями внутри одной обёртки, так что `.islandBody + .islandFooter` сработал бы — аккордеон
80
+ гасит его явно (`margin-top: 0 !important` у `.footer` в `IslandAccordion.module.less`),
81
+ потому что владеет своей схемой отступов.
82
+ - **Отступы даёт только родительский Island.** Отрендеренный отдельно `IslandBody` — просто `<div>`.
83
+ - **`className`, переданный в часть, дополняет базовый класс, а не заменяет его** (с 1.41.0).
84
+ Часть остаётся под селекторами отступов Island и сохраняет `position: relative` из своего
85
+ LESS-модуля. Потребителю, которому нужна собственная вёрстка внутри карточки, придётся гасить
86
+ `margin-top` с достаточной специфичностью: правило Island — четыре класса
87
+ (`.island.md .islandBody + .islandFooter`), поэтому в самой библиотеке для этого используется
88
+ `!important` (см. `IslandAccordion.module.less`), а не цепочка селекторов.
89
+ - **Вложенный Island:** правила отступов написаны через потомка (`.island.md .islandHeader + .islandBody`),
90
+ поэтому части вложенной карточки попадают и под селекторы внешней. Специфичность у правил всех
91
+ размеров одинаковая, так что при разных `size` побеждает правило, которое ниже в
92
+ `Island.module.less` (порядок в файле — `sm` → `md` → `lg`), а не ближайший родитель.
105
93
 
106
- ### Дочерние — `IIslandHeaderProps`, `IIslandBodyProps`, `IIslandFooterProps`
94
+ ---
107
95
 
108
- Все extends `React.HTMLProps<HTMLDivElement>` (не `HTMLAttributes` — заметь разницу: `HTMLProps` шире, включает DOM-специфичные проп'ы вроде `form`, `crossOrigin`). Интерфейсы пустые, просто именованные alias'ы.
96
+ ## Дизайн-токены
109
97
 
110
- `IslandBody` merge'ит `className` через `clsx` — можно передавать свой класс.
98
+ ```
99
+ --triplex-next-Island-Type1_Background
100
+ --triplex-next-Island-Type1_Shadow
101
+ --triplex-next-Island-Type2_Background
102
+ --triplex-next-Island-Type2_Shadow
103
+ --triplex-next-Island-Type3_Background
104
+ --triplex-next-Island-Type3_Shadow
105
+ ```
111
106
 
112
- **`IslandHeader` / `IslandFooter`** — НЕ merge'ят `className` из props (баг-или-легаси): `clsx(styles.islandHeader)` без пропагации `className`. **Если нужно передать свой класс — исправляй компонент.** Не полагайся на это поведение.
107
+ Значения задаются в `src/components/DesignTokens/components/Island.ts`. `Type1_Shadow` и
108
+ `Type3_Shadow` — `none`, `Type2_Shadow` — внутренняя рамка `0 0 0 1px … inset`. Скругления и
109
+ отступы токенами не описываются, они заданы литералами в `styles/Island.module.less`.
113
110
 
114
111
  ---
115
112
 
116
- ## Ключевые особенности реализации
113
+ ## Инварианты
117
114
 
118
- ### Compound через `Object.assign`
115
+ - **`forwardRef`** — обязателен у `Island` и всех трёх частей, `ref` идёт на корневой `<div>`.
116
+ - **Корневой элемент — `<div>`** у Island и у каждой части. Смена тега меняет дефолтный
117
+ `display` и семантику, то есть ломает вёрстку потребителей (селекторы отступов
118
+ класс-базированные и тег не проверяют).
119
+ - **Имена CSS-классов `islandHeader` / `islandBody` / `islandFooter` совпадают в четырёх
120
+ LESS-модулях намеренно.** `scripts/generate-scoped-name.ts` хеширует класс по
121
+ `имя папки компонента + имя класса + версия библиотеки`, поэтому `.islandHeader` в
122
+ `styles/Island.module.less` и в `styles/IslandHeader.module.less` дают один и тот же хеш —
123
+ на этом и держатся селекторы отступов. Следствия:
124
+ - файлы частей и их стили нельзя выносить из `src/components/Island/`;
125
+ - переименовывать класс можно только одновременно во всех модулях, где он встречается.
126
+ - **У классов `.islandHeader` / `.islandBody` / `.islandFooter` должно оставаться хотя бы одно
127
+ CSS-свойство** (сейчас `position: relative` / `display: block`), иначе класс не попадёт в сборку
128
+ и отступы перестанут работать. В LESS-файлах об этом есть комментарии — не удаляй.
129
+ - **Публичный API:** `Island`, `IslandHeader`, `IslandBody`, `IslandFooter`, `EIslandType`,
130
+ `IIslandProps`, `IIslandHeaderProps`, `IIslandBodyProps`, `IIslandFooterProps` экспортируются из
131
+ `src/components/Island/index.ts`. Значения `EIslandType` — строки `type_1` / `type_2` / `type_3`.
132
+ - **Части типизированы через `React.HTMLProps<HTMLDivElement>`** (не `HTMLAttributes`). Сужение
133
+ типа — breaking change для потребителей.
134
+ - **`mapTypeToClassName` (`utils.ts`) — внутренняя утилита**, в barrel не экспортируется.
119
135
 
120
- ```tsx
121
- export const Island = Object.assign(
122
- React.forwardRef<HTMLDivElement, IIslandProps>(...),
123
- { Body: IslandBody, Header: IslandHeader, Footer: IslandFooter },
124
- );
125
- ```
136
+ ---
126
137
 
127
- ### Margin между слотами — через смежный селектор
138
+ ## Accessibility
128
139
 
129
- Отступы между Header/Body/Footer заданы на корне Island, а не на самих слотах. Это значит:
130
- - если пропустить Body и написать `<Header /><Footer />` — между ними всё равно сработает `margin-top`.
131
- - если вставить произвольный контент между слотами — смежный селектор не сработает, и придётся выставлять отступ вручную.
140
+ Island — неинтерактивный контейнер: не выставляет роль, не управляет фокусом и не обрабатывает
141
+ клавиатуру. Всё это задаёт потребитель через spread — например, `Confirm` передаёт
142
+ `role="dialog"` и `aria-modal="true"`, `IslandAccordion` кладёт внутрь `Island.Header` собственную
143
+ `<button>` с `aria-expanded` / `aria-controls`.
132
144
 
133
- ### Mobile resize
145
+ Важно: `Island.Header` — это `<div>`, а не заголовок. Если нужен семантический заголовок карточки,
146
+ положи внутрь `Title` с нужным тегом или собственный `<h*>`. Текстовые строки компонент не
147
+ хардкодит (библиотека мультиязычная).
134
148
 
135
- `@media (max-width: @screen-sm-max)` перекрывает padding/radius для всех размеров — карточка визуально уменьшается на мобильных. Это поведение реализует автоматическую адаптивность без дополнительных пропсов.
149
+ ---
136
150
 
137
- ### `IslandHeader` / `IslandFooter` игнорируют `className`
151
+ ## Связанные компоненты
138
152
 
139
- В отличие от `IslandBody`, они не мержат `className`. Это по факту баг/неполная реализация, но изменение может затронуть потребителей — не исправлять без согласования.
153
+ - `IslandHeader` (`components/IslandHeader.tsx`, он же `Island.Header`) — шапка карточки.
154
+ Тривиальная `<div>`-обёртка без собственных props; отдельного AI.md не имеет.
155
+ - `IslandBody` (`components/IslandBody.tsx`, он же `Island.Body`) — основное содержимое.
156
+ Тривиальная `<div>`-обёртка без собственных props.
157
+ - `IslandFooter` (`components/IslandFooter.tsx`, он же `Island.Footer`) — подвал карточки.
158
+ Тривиальная `<div>`-обёртка без собственных props.
159
+ - `IslandAccordion` — строит на Island раскрывающиеся элементы; `IslandAccordionFooter`
160
+ оборачивает `Island.Footer` и передаёт в него свой `className`.
161
+ - `IslandWidget` — виджет со сворачиванием в адаптиве; использует Island как контейнер, но своими
162
+ Header / Body / Footer, а не частями Island.
163
+ - `BodyPage`, `HeaderPage`, `FooterPage` — оборачивают контент страницы в Island с `TYPE_1`, если
164
+ их тип `FIRST`.
165
+ - `Confirm` — диалог подтверждения на Island `TYPE_1` с `Island.Body` внутри.
166
+ - `ModalWindowBody` — тело модального окна; `IModalWindowBodyProps` расширяет `IIslandProps`,
167
+ поэтому `type` и `size` Island доступны и там.
140
168
 
141
169
  ---
142
170
 
143
- ## Инварианты
171
+ ## Stories
172
+
173
+ Основные истории: `stories/Island/Island.stories.tsx`
174
+ Файлы примеров: `stories/Island/examples/`
144
175
 
145
- - **`EIslandType` значения `TYPE_1`/`TYPE_2`/`TYPE_3`** — публичный API. Не переименовывать.
146
- - **Compound через `Object.assign`** — `Island.Header`, `Island.Body`, `Island.Footer`. Barrel также экспортирует `IslandHeader`/`IslandBody`/`IslandFooter` напрямую.
147
- - **Margin-логика через `+`-селектор** — изменение на gap-flex сломает существующие интеграции (padding/layout рассчитаны на margin-top).
148
- - **`forwardRef`** — на всех четырёх компонентах.
149
- - **Дефолт `type=TYPE_1`, `size=MD`** — не менять.
150
- - **`HTMLProps<HTMLDivElement>`** (не `HTMLAttributes`) в интерфейсах слотов — сохранить, если потребитель опирается на расширенные атрибуты.
176
+ | Story | Example file | Что демонстрирует |
177
+ |---|---|---|
178
+ | `Playground` | — | Интерактивный контроль `type`, `size` и состава (Header / Body / Footer) |
179
+ | `Default` | `DefaultExample.tsx` | Минимальная карточка: Header + Body + Footer |
180
+ | `Types` | `TypesExample.tsx` | Все значения `EIslandType` рядом |
181
+ | `Sizes` | `SizesExample.tsx` | Размеры SM / MD / LG: скругление, паддинги, отступы между частями |
151
182
 
152
183
  ---
153
184
 
154
- ## Accessibility
185
+ ## История изменений
155
186
 
156
- - `<div>`-контейнеры, без ARIA-ролей. Если остров используется как диалог/секция/регион — потребитель передаёт `role` и `aria-label` через `...rest`.
157
- - `IslandHeader` — не `<header>`/`<h*>`; потребитель сам передаёт нужные семантические теги/заголовки внутрь.
158
- - Tab-навигация — через интерактивные дети.
187
+ | Дата | Изменение |
188
+ |---|---|
189
+ | 2026-08-04 | Создан документ. AI-рефакторинг: JSDoc на props, значениях `EIslandType` и субкомпонентах, `displayName` у `IslandHeader` / `IslandBody` / `IslandFooter`, unit-тесты на все типы и размеры, части острова и `mapTypeToClassName`. Исправлено: `className` в `IslandHeader` и `IslandFooter` больше не затирает базовый класс компонента; побочный отступ в `IslandAccordion` погашен в стилях аккордеона, вид библиотечных компонентов не изменился. Публичный API не изменён. |
159
190
 
160
191
  ---
161
192
 
162
- ## Дизайн-токены
193
+ ## Примеры использования
163
194
 
164
- ```
165
- --triplex-next-Island-Type1_Background
166
- --triplex-next-Island-Type1_Shadow
167
- --triplex-next-Island-Type2_Background
168
- --triplex-next-Island-Type2_Shadow
169
- --triplex-next-Island-Type3_Background
170
- --triplex-next-Island-Type3_Shadow
171
- ```
195
+ ### Default
172
196
 
173
- Размеры (`16/24/32` px) и gap'ы (`8/12/16` px) заданы литералами.
174
-
175
- ---
197
+ `stories/Island/examples/DefaultExample.tsx`
176
198
 
177
- ## Связанные компоненты
199
+ ```tsx
200
+ import React from "react";
201
+ import { Island, EIslandType, EComponentSize } from "@sberbusiness/triplex-next";
202
+
203
+ export const DefaultExample = () => (
204
+ <div style={{ maxWidth: 360 }}>
205
+ <Island type={EIslandType.TYPE_1} size={EComponentSize.MD}>
206
+ <Island.Header>Island Header</Island.Header>
207
+ <Island.Body>Island Body</Island.Body>
208
+ <Island.Footer>Island Footer</Island.Footer>
209
+ </Island>
210
+ </div>
211
+ );
212
+ ```
178
213
 
179
- - `IslandAccordion` — остров с раскрытием (содержит `hoverable` chevron-иконку).
180
- - `IslandWidget` — остров-виджет с иконкой и действиями.
181
- - `Card` — другая карточная абстракция (интерактивная, с hover-состояниями).
182
- - `Page` — типовой контейнер страницы, часто содержит Island'ы.
214
+ ### Types
183
215
 
184
- ---
216
+ `stories/Island/examples/TypesExample.tsx`
185
217
 
186
- ## Stories
218
+ ```tsx
219
+ import React from "react";
220
+ import { Island, EIslandType } from "@sberbusiness/triplex-next";
221
+
222
+ export const TypesExample = () => (
223
+ <div
224
+ style={{
225
+ display: "grid",
226
+ gap: 16,
227
+ gridTemplateColumns: "repeat(auto-fit, minmax(260px, 1fr))",
228
+ }}
229
+ >
230
+ <div>
231
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>TYPE_1</div>
232
+ <Island type={EIslandType.TYPE_1}>
233
+ <Island.Header>Header content</Island.Header>
234
+ <Island.Body>Body content</Island.Body>
235
+ <Island.Footer>Footer content</Island.Footer>
236
+ </Island>
237
+ </div>
238
+ <div>
239
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>TYPE_2</div>
240
+ <Island type={EIslandType.TYPE_2}>
241
+ <Island.Header>Header content</Island.Header>
242
+ <Island.Body>Body content</Island.Body>
243
+ <Island.Footer>Footer content</Island.Footer>
244
+ </Island>
245
+ </div>
246
+ <div>
247
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>TYPE_3</div>
248
+ <Island type={EIslandType.TYPE_3}>
249
+ <Island.Header>Header content</Island.Header>
250
+ <Island.Body>Body content</Island.Body>
251
+ <Island.Footer>Footer content</Island.Footer>
252
+ </Island>
253
+ </div>
254
+ </div>
255
+ );
256
+ ```
187
257
 
188
- `stories/Island/Island.stories.tsx`
189
- Примеры: `stories/Island/examples/`
258
+ ### Sizes
190
259
 
191
- ---
260
+ `stories/Island/examples/SizesExample.tsx`
192
261
 
193
- ## История изменений
262
+ ```tsx
263
+ import React from "react";
264
+ import { Island, EIslandType, EComponentSize } from "@sberbusiness/triplex-next";
265
+
266
+ const SIZES = Object.values(EComponentSize);
267
+
268
+ export const SizesExample = () => (
269
+ <div
270
+ style={{
271
+ display: "grid",
272
+ gap: 16,
273
+ gridTemplateColumns: "repeat(auto-fit, minmax(260px, 1fr))",
274
+ }}
275
+ >
276
+ {SIZES.map((size) => (
277
+ <div key={size}>
278
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>{size.toUpperCase()}</div>
279
+ <Island type={EIslandType.TYPE_1} size={size}>
280
+ <Island.Header>Header content</Island.Header>
281
+ <Island.Body>Body content</Island.Body>
282
+ <Island.Footer>Footer content</Island.Footer>
283
+ </Island>
284
+ </div>
285
+ ))}
286
+ </div>
287
+ );
288
+ ```
194
289
 
195
- | Дата | Изменение |
196
- |---|---|
197
- | 2026-04-18 | Создан документ |