@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.
Files changed (73) hide show
  1. package/README.md +10 -0
  2. package/bundle/components/AlertContext.md +268 -0
  3. package/bundle/components/BodyPage.md +186 -0
  4. package/bundle/components/Button.md +93 -85
  5. package/bundle/components/ButtonDropdown.md +36 -70
  6. package/bundle/components/ButtonDropdownExtended.md +422 -0
  7. package/bundle/components/ButtonIcon.md +41 -48
  8. package/bundle/components/Caption.md +291 -0
  9. package/bundle/components/Checkbox.md +43 -122
  10. package/bundle/components/CheckboxXGroup.md +109 -46
  11. package/bundle/components/CheckboxYGroup.md +77 -49
  12. package/bundle/components/CodeText.md +197 -0
  13. package/bundle/components/Col.md +311 -162
  14. package/bundle/components/CollapsibleTree.md +584 -0
  15. package/bundle/components/CollapsibleTreeExtended.md +422 -0
  16. package/bundle/components/Dropdown.md +748 -346
  17. package/bundle/components/EmptyView.md +14 -15
  18. package/bundle/components/FooterPage.md +203 -0
  19. package/bundle/components/Gap.md +115 -57
  20. package/bundle/components/HeaderPage.md +226 -0
  21. package/bundle/components/HelpBox.md +45 -84
  22. package/bundle/components/ImageGallery.md +423 -0
  23. package/bundle/components/ImageGalleryExtended.md +606 -0
  24. package/bundle/components/LightBox.md +2252 -233
  25. package/bundle/components/List.md +216 -183
  26. package/bundle/components/ListActionItem.md +157 -0
  27. package/bundle/components/ListItem.md +276 -0
  28. package/bundle/components/ListItemControlsButton.md +103 -0
  29. package/bundle/components/ListItemControlsButtonDropdown.md +107 -0
  30. package/bundle/components/ListItemSelectable.md +99 -0
  31. package/bundle/components/ListItemTable.md +390 -0
  32. package/bundle/components/ListMaster.md +876 -156
  33. package/bundle/components/ListMasterFooter.md +80 -0
  34. package/bundle/components/ListMasterHeader.md +86 -0
  35. package/bundle/components/ListSortable.md +153 -0
  36. package/bundle/components/ListSortableItem.md +175 -0
  37. package/bundle/components/ListTableItem.md +391 -0
  38. package/bundle/components/ModalWindow.md +427 -237
  39. package/bundle/components/MonthYearField.md +145 -109
  40. package/bundle/components/Notification.md +625 -155
  41. package/bundle/components/Overlay.md +271 -170
  42. package/bundle/components/Page.md +284 -186
  43. package/bundle/components/Pagination.md +206 -229
  44. package/bundle/components/Portal.md +131 -67
  45. package/bundle/components/Row.md +171 -69
  46. package/bundle/components/Text.md +320 -0
  47. package/bundle/components/TextField.md +364 -97
  48. package/bundle/components/Title.md +296 -0
  49. package/bundle/guides/codestyle.md +38 -11
  50. package/bundle/guides/commits.md +119 -26
  51. package/bundle/guides/context.md +34 -30
  52. package/bundle/guides/stories.md +110 -40
  53. package/bundle/guides/template.md +3 -7
  54. package/bundle/guides/tests.md +48 -2
  55. package/bundle/manifest.json +275 -30
  56. package/bundle/release-notes/1.26.0.mdx +33 -2
  57. package/bundle/release-notes/1.27.0.mdx +43 -0
  58. package/bundle/release-notes/1.28.0.mdx +25 -0
  59. package/bundle/release-notes/1.29.0.mdx +47 -0
  60. package/bundle/release-notes/1.30.0.mdx +56 -0
  61. package/bundle/release-notes/1.31.0.mdx +52 -0
  62. package/bundle/release-notes/1.32.0.mdx +35 -0
  63. package/bundle/release-notes/1.33.0.mdx +61 -0
  64. package/bundle/release-notes/1.34.0.mdx +35 -0
  65. package/bundle/release-notes/1.35.0.mdx +21 -0
  66. package/bundle/release-notes/1.35.1.mdx +11 -0
  67. package/bundle/release-notes/1.36.0.mdx +21 -0
  68. package/bundle/release-notes/1.37.0.mdx +71 -0
  69. package/bundle/release-notes/1.38.0.mdx +36 -0
  70. package/bundle/release-notes/1.39.0.mdx +66 -0
  71. package/bundle/release-notes/1.40.0.mdx +27 -0
  72. package/bundle/release-notes/1.41.0.mdx +7 -0
  73. package/package.json +1 -1
@@ -12,7 +12,7 @@ React-библиотека компонентов дизайн-системы.
12
12
  Основная линия разработки: `main` → v1.x на React 18 + TypeScript strict.
13
13
  Поддерживаемая legacy-линия: `release-0` → v0.x на React 17, синхронизируется мейнтейнерами.
14
14
 
15
- **Основной сценарий работы агента:** добавить новый prop или вариант к существующему компоненту по описанию и/или макету из Figma.
15
+ **Основной сценарий работы агента:** добавить новый prop или вариант к существующему компоненту по текстовому описанию или скриншоту.
16
16
 
17
17
  ---
18
18
 
@@ -48,7 +48,7 @@ src/components/Button/
48
48
  │ └── ButtonGeneral.module.less # Стили по теме/варианту
49
49
  ├── __tests__/
50
50
  │ └── Button.test.tsx # Unit-тесты
51
- └── Button-AI.md # Документация для AI-агентов (если компонент уже задокументирован)
51
+ └── Button-ai.md # Документация для AI-агентов (если компонент уже задокументирован)
52
52
  ```
53
53
 
54
54
  Истории (stories) хранятся отдельно. В репозитории встречаются как flat-path файлы
@@ -90,7 +90,7 @@ src/components/Button/
90
90
 
91
91
  Токены генерируются в `src/generated/themesCssVariables.css`. Не редактируй этот файл вручную.
92
92
 
93
- Если добавляешь новое визуальное состояние — нужен новый токен. Имя токена согласуй с дизайнером в Figma.
93
+ Если добавляешь новое визуальное состояние — нужен новый токен. Имя токена согласуй с дизайнером.
94
94
 
95
95
  ---
96
96
 
@@ -115,14 +115,14 @@ src/components/Button/
115
115
 
116
116
  Если инструкции расходятся, ориентируйся на них в таком порядке:
117
117
 
118
- 1. `src/components/{ComponentName}/{ComponentName}-AI.md` для конкретного компонента, если файл существует
118
+ 1. `src/components/{ComponentName}/{ComponentName}-ai.md` для конкретного компонента, если файл существует
119
119
  2. `docs/ai/codestyle.md`
120
120
  3. Профильный подробный гайд по зоне изменений (`codestyle.md`, `tests.md`, `stories-guide.md`, `commits.md`)
121
121
  4. Этот файл (`docs/ai/CONTEXT.md`)
122
122
  5. Локальный паттерн исходников компонента, stories и тестов
123
123
 
124
124
  Не предполагай, что у каждого компонента уже есть AI-документация. Если файла
125
- `{ComponentName}-AI.md` нет, ориентируйся на код, stories, тесты и общие правила
125
+ `{ComponentName}-ai.md` нет, ориентируйся на код, stories, тесты и общие правила
126
126
  репозитория.
127
127
 
128
128
  ---
@@ -227,28 +227,6 @@ className={clsx(styles.button, styles.general, { [styles.loading]: loading })}
227
227
 
228
228
  ---
229
229
 
230
- ## Figma и MCP
231
-
232
- Не у каждого компонента уже есть `{ComponentName}-AI.md`. Если компонент
233
- задокументирован, Figma-метаданные лежат в frontmatter этого файла.
234
-
235
- Формат frontmatter:
236
- - `figma-node` — значение параметра `node-id` из URL Figma (например, `1-328`)
237
- - `figma-file` — URL файла / design-ссылка на Figma-файл
238
-
239
- Если в вашем окружении настроен Figma MCP или другой интеграционный инструмент:
240
- 1. Возьми `figma-node` из frontmatter компонента
241
- 2. Возьми `figma-file` из frontmatter компонента, если инструменту нужен file key или URL
242
- 3. Используй их для получения дизайн-спецификации
243
- 4. Сравни с текущим кодом и внеси нужные изменения
244
-
245
- Если Figma-метаданные для компонента отсутствуют, не выдумывай их: попроси
246
- ссылку у разработчика или работай по текстовому описанию/скриншоту.
247
-
248
- Настройка MCP: см. `docs/ai/ROADMAP.md` → секция "Figma MCP Setup".
249
-
250
- ---
251
-
252
230
  ## Минимальная проверка перед завершением задачи
253
231
 
254
232
  - Правки документации и конфигов: проверь ссылки, команды, примеры кода и согласованность между файлами.
@@ -262,11 +240,37 @@ className={clsx(styles.button, styles.general, { [styles.loading]: loading })}
262
240
  ## Навигация по документации компонентов
263
241
 
264
242
  Если компонент уже задокументирован, его AI-документация лежит в:
265
- `src/components/{ComponentName}/{ComponentName}-AI.md`
243
+ `src/components/{ComponentName}/{ComponentName}-ai.md`
244
+
245
+ Пример: `src/components/Button/Button-ai.md`
266
246
 
267
- Пример: `src/components/Button/Button-AI.md`
247
+ Шаблон для новых компонентов: `docs/ai/template-ai.md`
248
+
249
+ ### Когда создавать `{ComponentName}-ai.md`
250
+
251
+ Не для каждого экспорта из barrel `index.ts` нужен отдельный AI.md — это
252
+ раздуло бы `mcp-data.json` тривиальными wrapper'ами и снизило сигнал/шум для
253
+ AI-агента.
254
+
255
+ Создавай отдельный AI.md, когда выполняются **оба** условия:
256
+
257
+ 1. Компонент экспортируется из barrel `src/components/{Family}/index.ts`
258
+ (внутренние/приватные — нет).
259
+ 2. У компонента есть нетривиальный API или поведение: собственные props
260
+ сверх `className + ref + ...rest`, либо state, контекст, клавиатура,
261
+ callback'и, accessibility-контракт, render-prop, discriminated union.
262
+
263
+ Тривиальные wrapper'ы (`className + spread + forwardRef` без своей логики)
264
+ описывай разделом **«Связанные компоненты»** в AI.md родителя.
265
+
266
+ **Пример из семейства List:**
267
+
268
+ | Создаём AI.md | Описываем в родителе |
269
+ |---|---|
270
+ | `List`, `ListItem`, `ListActionItem`, `ListTableItem`, `ListItemSelectable`, `ListItemControlsButton`, `ListItemControlsButtonDropdown`, `ListSortable`, `ListSortableItem` | `ListItemControls`, `ListItemContent`, `ListEmptyState`, `ListItemLoading`, `ListItemTailLeft`, `ListItemTailRight`, `ListSortableItemTarget`, `ListSortableItemControls` |
268
271
 
269
- Шаблон для новых компонентов: `docs/ai/template-AI.md`
272
+ В ROADMAP-таблице каждый AI.md-достойный компонент идёт отдельной строкой —
273
+ так считается прогресс покрытия и видны субкомпоненты с собственным API.
270
274
 
271
275
  ---
272
276
 
@@ -32,13 +32,13 @@ Stories выполняют несколько функций (в порядке
32
32
 
33
33
  ### Всегда
34
34
 
35
- - **Playground** — добавить новый prop в `args` (дефолтное значение) и в `argTypes` (описание и контрол).
36
- - **`ArgTypes`** в docs page автоматически покажет новый prop, если он типизирован в интерфейсе.
35
+ - **Playground** — добавить новый prop в `args` (дефолтное значение) и в `argTypes` (контрол).
36
+ - **`argTypes`** в docs page автоматически покажет новый prop, если он типизирован в интерфейсе.
37
37
 
38
38
  ### Если prop имеет несколько визуальных вариантов (enum, размер, тема)
39
39
 
40
40
  - Добавить новую named story: `Sizes`, `Themes`, `Statuses` — в зависимости от типа prop.
41
- - Создать файл `examples/{PropName}Example.tsx` и реэкспортировать через `examples/index.ts`.
41
+ - Создать файл `examples/{PropName}.tsx` и реэкспортировать через `examples/index.ts`.
42
42
 
43
43
  ### Если prop — boolean-состояние с видимым эффектом
44
44
 
@@ -79,10 +79,10 @@ stories/
79
79
  ComponentName.stories.tsx # основной файл стори
80
80
  examples/
81
81
  index.ts # реэкспорт всех примеров
82
- DefaultExample.tsx
83
- SizesExample.tsx
84
- StatusesExample.tsx
85
- ProductionExample.tsx
82
+ Default.tsx
83
+ Sizes.tsx
84
+ Statuses.tsx
85
+ Production.tsx
86
86
  ...
87
87
  ```
88
88
 
@@ -90,6 +90,14 @@ stories/
90
90
 
91
91
  Все стори выносятся в подпапку `examples/` и импортируются в основной файл. Если в основной папке находятся несколько файлов с расширением `.stories.tsx`, внутри подпапки `examples` необходимо создать подпапки по названию компонентов из файлов с расширением `.stories.tsx`. Также необходимо в каждой такой подпапке создать отдельный файл `index.ts`.
92
92
 
93
+ ### Именование файлов примеров
94
+
95
+ - Имя файла = имя story: `Default` → `Default.tsx`, `Sizes` → `Sizes.tsx`, `Playground` → `Playground.tsx`.
96
+ - **Без постфикса `Example`** — не пиши `DefaultExample.tsx`, `SizesExample.tsx`. Если встретишь старые файлы с этим постфиксом, сохраняй локальный паттерн до миграции, но новые файлы создавай без него.
97
+ - Имя экспорта-функции внутри файла совпадает с именем файла: `export const Default = () => ...`.
98
+ - Source-константа: `{StoryName}Source` — `DefaultSource`, `SizesSource`, `PlaygroundSource`.
99
+ - В story-файле импорт компонента-примера делается через alias, чтобы избежать коллизии со story-экспортом: `Default as DefaultRender`, `Playground as PlaygroundRender`. Source без alias.
100
+
93
101
  ---
94
102
 
95
103
  ## Обязательные стори
@@ -105,7 +113,9 @@ stories/
105
113
 
106
114
  > **Правило:** только Playground имеет Controls. Документационные стори (`Default`, `Sizes / Themes / Statuses`, `Edge cases`, `Examples`) не имеют Controls и показывают пример кода. `Visual tests` не имеют Controls и не показывают код.
107
115
 
108
- > **MCP bundle:** `Playground` и `VisualTests` не попадают в `mcp-data.json` — у них нет кода для агента (Playground интерактивен, VisualTests — скриншот-регрессия). Фильтр реализован в `scripts/generateMcpData.ts` (`EXCLUDED_STORIES`). В остальном для MCP нужен файл примера в `examples/{Component}/` и ссылка на него из колонки `Example file` в `{Component}-AI.md`.
116
+ > **Исключение для компонентов без props:** если у компонента нет настраиваемых props (например, контейнер-обёртка с одним только `children` и стандартными HTML-атрибутами), Playground не создаётся — интерактивность нечего показывать. Также убирается блок `Heading>Props` + `ArgTypes` — таблица будет пустой. В этом случае из docs page убираются `Heading>Props`, `ArgTypes`, `Heading>Playground`, `Primary` и `Controls of={Playground}` (см. раздел [Структура docs page](#структура-docs-page)).
117
+
118
+ > **MCP bundle:** `Playground` и `VisualTests` не попадают в `mcp-data.json` — у них нет кода для агента (Playground интерактивен, VisualTests — скриншот-регрессия). Фильтр реализован в `scripts/generateMcpData.ts` (`EXCLUDED_STORIES`). В остальном для MCP нужен файл примера в `examples/{Component}/` и ссылка на него из колонки `Example file` в `{Component}-ai.md`.
109
119
 
110
120
  ---
111
121
 
@@ -144,6 +154,22 @@ parameters: {
144
154
  },
145
155
  ```
146
156
 
157
+ Если у компонента нет настраиваемых props (см. [Когда не создавать Playground](#когда-не-создавать-playground)), блок Playground убирается, а вместе с ним и блок `Props` — `ArgTypes` без props покажет пустую таблицу:
158
+
159
+ ```tsx
160
+ parameters: {
161
+ docs: {
162
+ page: () => (
163
+ <>
164
+ <Title />
165
+ <Description />
166
+ <Stories />
167
+ </>
168
+ ),
169
+ },
170
+ },
171
+ ```
172
+
147
173
  ---
148
174
 
149
175
  ## Playground
@@ -152,6 +178,21 @@ parameters: {
152
178
 
153
179
  Playground показывает только те props, которые реально помогают исследовать компонент.
154
180
 
181
+ ### Когда не создавать Playground
182
+
183
+ Если у компонента нет настраиваемых props (только `children` и стандартные HTML-атрибуты), Playground не создаётся — нечего класть в Controls. Типичный случай: контейнер-обёртка вроде `ListItem`, `ListItemControls`, `ChipGroup`.
184
+
185
+ В этом случае:
186
+
187
+ - В файле stories нет экспорта `Playground` и связанного с ним example-файла.
188
+ - В docs page убираются:
189
+ - `Heading>Props` и `ArgTypes of={Component}` — без props таблица будет пустой.
190
+ - `Heading>Playground`, `Primary`, `Controls of={Playground}`.
191
+ - См. вариант без Playground в разделе [Структура docs page](#структура-docs-page).
192
+ - Из импортов `@storybook/addon-docs/blocks` удаляются `ArgTypes`, `Heading`, `Primary`, `Controls` — остаются только `Title`, `Description`, `Stories`.
193
+
194
+ Если props всего один-два, но они имеют визуальный эффект — Playground всё равно создаётся, и блок `Props` остаётся.
195
+
155
196
  ### Разделение Controls
156
197
 
157
198
  - Свойства, являющиеся props компонента, располагаются в блоке **Props** (по умолчанию).
@@ -210,7 +251,7 @@ tags: ["!autodocs"],
210
251
  | Заполнение необязательного props | Название props **как есть** | `disabled` → `Disabled` |
211
252
  | Нестандартные варианты наполнения | `With` + вариант | `WithTextAndIcon` |
212
253
  | Композиции / production примеры | `Example` | `Example` |
213
- | Скриншот-тесты | `Visual tests` | `Visual tests` |
254
+ | Скриншот-тесты | `VisualTests` | `VisualTests` |
214
255
 
215
256
  ---
216
257
 
@@ -255,6 +296,25 @@ import { Button } from "../../../src/components/Button/Button";
255
296
 
256
297
  Для legacy story-файлов не нужно устраивать отдельную миграцию импортов без задачи.
257
298
 
299
+ ### Колбэки в примерах
300
+
301
+ | Файл | Что использовать | Почему |
302
+ | --- | --- | --- |
303
+ | `Playground.tsx`, `VisualTests`-рендеры | `action("eventName")` из `storybook/actions` | Код не показывается через `?raw`, поэтому storybook-импорт допустим. Помогает увидеть события во вкладке Actions при ручной проверке. |
304
+ | Копируемые примеры (`Default`, `Sizes`, `Statuses`, `With*`, production-like `Example`) | Пустые функции `() => {}` | Код показывается через `?raw` и должен копироваться без правок. `alert()`, `console.log()` и `import { action } from "storybook/actions"` ломают копируемость и создают side effects. |
305
+
306
+ ```tsx
307
+ // Playground.tsx
308
+ import { action } from "storybook/actions";
309
+
310
+ <Component onChange={action("onChange")} />;
311
+ ```
312
+
313
+ ```tsx
314
+ // Default.tsx
315
+ <Component onChange={() => {}} />;
316
+ ```
317
+
258
318
  ---
259
319
 
260
320
  ## Скриншот-тесты
@@ -386,12 +446,14 @@ render: () => (
386
446
  В modern pattern для каждого примера экспортируются два значения — сам компонент и его исходный код:
387
447
 
388
448
  ```ts
389
- export * from "./DefaultExample";
390
- export { default as DefaultExampleSource } from "./DefaultExample?raw";
391
- export * from "./SizesExample";
392
- export { default as SizesExampleSource } from "./SizesExample?raw";
449
+ export * from "./Default";
450
+ export { default as DefaultSource } from "./Default?raw";
451
+ export * from "./Sizes";
452
+ export { default as SizesSource } from "./Sizes?raw";
393
453
  ```
394
454
 
455
+ > **Имена.** Файл и экспорт-функция называются как story (`Default.tsx` экспортирует `Default`). Source-константа добавляет суффикс `Source` (`DefaultSource`). Из-за коллизии с именем story-экспорта в `*.stories.tsx` импорт компонента-примера делается через alias — см. ниже.
456
+
395
457
  ---
396
458
 
397
459
  ## Подключение примеров к стори
@@ -399,14 +461,15 @@ export { default as SizesExampleSource } from "./SizesExample?raw";
399
461
  В modern pattern каждая документационная стори подключает пример и его исходный код через `?raw`:
400
462
 
401
463
  ```tsx
464
+ import { Default as DefaultRender, DefaultSource } from "./examples";
465
+
402
466
  export const Default: StoryObj<typeof Component> = {
403
- name: "Default",
404
- render: DefaultExample,
467
+ render: DefaultRender,
405
468
  parameters: {
469
+ controls: { disable: true },
406
470
  docs: {
407
- controls: { disable: true },
408
471
  source: {
409
- code: DefaultExampleSource,
472
+ code: DefaultSource,
410
473
  language: "tsx",
411
474
  },
412
475
  },
@@ -414,6 +477,8 @@ export const Default: StoryObj<typeof Component> = {
414
477
  };
415
478
  ```
416
479
 
480
+ > **Alias обязателен.** Имя экспорта-функции (`Default`) совпадает с именем story-экспорта (`Default: StoryObj`), поэтому в импорте используется `Default as DefaultRender`. Source-константа конфликта не вызывает (`DefaultSource`), её импортируем без alias. То же относится к Playground (`Playground as PlaygroundRender`).
481
+
417
482
  ---
418
483
 
419
484
  ## Полный пример
@@ -425,7 +490,13 @@ import React, { useState, useCallback } from "react";
425
490
  import { Meta, StoryObj } from "@storybook/react";
426
491
  import { Title, Description, Primary, Controls, Stories, ArgTypes, Heading } from "@storybook/addon-docs/blocks";
427
492
  import { ComponentName, EComponentSize } from "@sberbusiness/triplex-next";
428
- import { DefaultExample, DefaultExampleSource, SizesExample, SizesExampleSource } from "./examples";
493
+ import {
494
+ Default as DefaultRender,
495
+ DefaultSource,
496
+ Playground as PlaygroundRender,
497
+ Sizes as SizesRender,
498
+ SizesSource,
499
+ } from "./examples";
429
500
 
430
501
  const meta = {
431
502
  title: "Components/Group/ComponentName",
@@ -457,9 +528,7 @@ export const Playground: StoryObj<typeof meta> = {
457
528
  size: EComponentSize.LG,
458
529
  // ...остальные args
459
530
  },
460
- render: ({ ...args }) => {
461
- // ...render с useState и обработчиками
462
- },
531
+ render: PlaygroundRender,
463
532
  argTypes: {
464
533
  size: {
465
534
  control: { type: "select" },
@@ -481,13 +550,12 @@ export const Playground: StoryObj<typeof meta> = {
481
550
  };
482
551
 
483
552
  export const Default: StoryObj<typeof ComponentName> = {
484
- name: "Default",
485
- render: DefaultExample,
553
+ render: DefaultRender,
486
554
  parameters: {
487
555
  controls: { disable: true },
488
556
  docs: {
489
557
  source: {
490
- code: DefaultExampleSource,
558
+ code: DefaultSource,
491
559
  language: "tsx",
492
560
  },
493
561
  },
@@ -495,13 +563,12 @@ export const Default: StoryObj<typeof ComponentName> = {
495
563
  };
496
564
 
497
565
  export const Sizes: StoryObj<typeof ComponentName> = {
498
- name: "Sizes",
499
- render: SizesExample,
566
+ render: SizesRender,
500
567
  parameters: {
501
568
  controls: { disable: true },
502
569
  docs: {
503
570
  source: {
504
- code: SizesExampleSource,
571
+ code: SizesSource,
505
572
  language: "tsx",
506
573
  },
507
574
  },
@@ -509,13 +576,13 @@ export const Sizes: StoryObj<typeof ComponentName> = {
509
576
  };
510
577
  ```
511
578
 
512
- ### `examples/DefaultExample.tsx`
579
+ ### `examples/Default.tsx`
513
580
 
514
581
  ```tsx
515
582
  import React, { useState } from "react";
516
583
  import { ComponentName, EComponentSize } from "@sberbusiness/triplex-next";
517
584
 
518
- export const DefaultExample = () => {
585
+ export const Default = () => {
519
586
  const [value, setValue] = useState<string>("");
520
587
 
521
588
  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => setValue(event.target.value);
@@ -528,7 +595,7 @@ export const DefaultExample = () => {
528
595
  };
529
596
  ```
530
597
 
531
- ### `examples/SizesExample.tsx`
598
+ ### `examples/Sizes.tsx`
532
599
 
533
600
  ```tsx
534
601
  import React, { useState } from "react";
@@ -553,7 +620,7 @@ const SizeItem = ({ size }: ISizeItemProps) => {
553
620
 
554
621
  const SIZES = Object.values(EComponentSize);
555
622
 
556
- export const SizesExample = () => (
623
+ export const Sizes = () => (
557
624
  <div style={{ maxWidth: "300px", display: "flex", flexDirection: "column", gap: "16px" }}>
558
625
  {SIZES.map((size) => (
559
626
  <SizeItem key={size} size={size} />
@@ -565,10 +632,10 @@ export const SizesExample = () => (
565
632
  ### `examples/index.ts`
566
633
 
567
634
  ```ts
568
- export * from "./DefaultExample";
569
- export { default as DefaultExampleSource } from "./DefaultExample?raw";
570
- export * from "./SizesExample";
571
- export { default as SizesExampleSource } from "./SizesExample?raw";
635
+ export * from "./Default";
636
+ export { default as DefaultSource } from "./Default?raw";
637
+ export * from "./Sizes";
638
+ export { default as SizesSource } from "./Sizes?raw";
572
639
  ```
573
640
 
574
641
  ---
@@ -579,10 +646,10 @@ export { default as SizesExampleSource } from "./SizesExample?raw";
579
646
 
580
647
  ### Документация и структура
581
648
 
582
- - [ ] Есть docs page с `Title`, `Description`, `ArgTypes`, `Playground`, `Stories`
583
- - [ ] Playground имеет Controls, не показывает код (`sourceState: "none"`)
584
- - [ ] Playground исключён из скриншот-тестов (`testRunner: { skip: true }`)
585
- - [ ] Playground скрыт из autodocs (`tags: ["!autodocs"]`)
649
+ - [ ] Есть docs page с `Title`, `Description`, `ArgTypes`, `Playground`, `Stories` (если у компонента нет настраиваемых props — блоки `Props`/`ArgTypes` и `Playground` пропускаются)
650
+ - [ ] Playground имеет Controls, не показывает код (`sourceState: "none"`) — если есть
651
+ - [ ] Playground исключён из скриншот-тестов (`testRunner: { skip: true }`) — если есть
652
+ - [ ] Playground скрыт из autodocs (`tags: ["!autodocs"]`) — если есть
586
653
  - [ ] Все документационные стори, кроме Playground, **не** имеют Controls (`controls: { disable: true }`)
587
654
  - [ ] Все документационные стори, кроме Playground, показывают пример кода через `source.code`
588
655
  - [ ] Все примеры вынесены в `examples/` и реэкспортированы через `index.ts`
@@ -595,14 +662,17 @@ export { default as SizesExampleSource } from "./SizesExample?raw";
595
662
 
596
663
  ### Скриншот-тесты
597
664
 
665
+ Скриншот-тесты обязательны для UI-компонентов. Без baseline-файлов в `__screenshots__/` регрессию не поймать.
666
+
598
667
  - [ ] Playground исключён из скриншот-тестов (`testRunner: { skip: true }`)
599
668
  - [ ] Нет дублирующих скриншотов — визуально идентичные стори исключены через `testRunner: { skip: true }`
600
669
  - [ ] Все ключевые визуальные состояния покрыты (размеры, статусы, заполненные поля, фокус)
601
670
  - [ ] Если основных сторей недостаточно — создана Visual tests с недостающими состояниями
602
671
  - [ ] Visual tests скрыта из autodocs (`tags: ["!autodocs"]`) и не показывает код
603
- - [ ] Если компонент требует взаимодействия (dropdown, tooltip) — реализован `play` в Visual tests
672
+ - [ ] Если компонент требует взаимодействия (dropdown, tooltip, modal, popover) — реализован `play` в Visual tests
604
673
  - [ ] Данные в Visual tests захардкожены (нет `new Date()`, `Math.random()`)
605
674
  - [ ] Контейнеры имеют фиксированные размеры для стабильного layout'а
675
+ - [ ] Для каждой не-skip story есть baseline-файлы в `__screenshots__/{story-id}--xs.png` и `--xl.png`. Если baseline ещё нет — запустить `GitHub Actions → Update Visual Snapshots` на ветке (CI закоммитит скриншоты), и только потом мержить.
606
676
 
607
677
  ### Для небольших изменений в legacy stories
608
678
 
@@ -1,8 +1,6 @@
1
1
  ---
2
2
  component: ComponentName
3
3
  category: Category
4
- figma-node: "NODE_ID"
5
- figma-file: "https://www.figma.com/design/FILE_KEY/..."
6
4
  related: []
7
5
  tokens: []
8
6
  stories: stories/Category/ComponentName.stories.tsx
@@ -19,8 +17,6 @@ version: "1.0"
19
17
  Описывай намерения и ограничения, которые не видны из кода.
20
18
 
21
19
  Frontmatter:
22
- - figma-node: raw-значение параметра `node-id` из URL Figma, например `1-328`
23
- - figma-file: URL файла / design-ссылка, из которой при необходимости можно получить file key
24
20
  - tokens: список CSS-переменных, которые компонент использует
25
21
  - related: список связанных компонентов (строки, имена)
26
22
 
@@ -139,9 +135,9 @@ VisualTests — скриншот-регрессия. Но в таблице их
139
135
 
140
136
  | Story | Example file | Что демонстрирует |
141
137
  |---|---|---|
142
- | `Playground` | `PlaygroundExample.tsx` | Интерактивный контроль всех props |
143
- | `Themes` | `ThemesExample.tsx` | Все визуальные темы |
144
- | `Sizes` | `SizesExample.tsx` | Размеры SM / MD / LG |
138
+ | `Playground` | `Playground.tsx` | Интерактивный контроль всех props |
139
+ | `Themes` | `Themes.tsx` | Все визуальные темы |
140
+ | `Sizes` | `Sizes.tsx` | Размеры SM / MD / LG |
145
141
 
146
142
  ---
147
143
 
@@ -38,6 +38,48 @@ src/components/{ComponentName}/__tests__/{ComponentName}.test.tsx
38
38
 
39
39
  Один файл на компонент. Если компонент имеет субкомпоненты (ButtonIcon, ButtonDropdown), тесты субкомпонентов — в отдельных файлах той же локальной папки тестов.
40
40
 
41
+ ### Импорты `vitest` — только в `*.test.ts` / `*.test.tsx` ⚠️
42
+
43
+ **Правило:** `import ... from "vitest"` (а также `@testing-library/*`, `storybook/test`)
44
+ допустим **исключительно** в файлах с постфиксом `.test.ts` / `.test.tsx`,
45
+ в `vitest.setup.ts` и в `test-utils/`. Нигде больше.
46
+
47
+ Причина — сборка. `vite.config.ts` строит entry-точку из **каждого** файла
48
+ `src/**/*.{ts,tsx}`, кроме исключённых глобом. Любой файл внутри `src/`, который
49
+ импортирует `vitest` и не подпадает под исключения, попадает в бандл, а вместе с
50
+ ним vitest уезжает в общий чанк `vendor` — тот самый, который тянут почти все
51
+ компоненты. Потребитель падает на старте:
52
+
53
+ ```text
54
+ Uncaught Error: Vitest failed to access its internal state.
55
+ ```
56
+
57
+ Именно это произошло в 1.39.0 из-за файла `src/components/Carousel/__tests__/utils.ts`
58
+ (хелпер без постфикса `.test`). Чанк `vendor` вырос с 640 KB до 1.28 MB.
59
+
60
+ **Общие тестовые хелперы** (моки `ResizeObserver`, фейковые `Touch`, подмена
61
+ размеров элементов и т.п.) кладём в `test-utils/` в корне репозитория — вне `src/`,
62
+ поэтому сборка их физически не видит:
63
+
64
+ ```typescript
65
+ // ✅ src/components/Carousel/__tests__/CarouselRoot.test.tsx
66
+ import { getResizeCallback, mockElementSize } from "../../../../test-utils/dom";
67
+
68
+ // ❌ src/components/Carousel/__tests__/utils.ts — станет entry-точкой сборки
69
+ import { vi } from "vitest";
70
+ ```
71
+
72
+ Не называй хелпер `utils.ts` рядом с `utils.test.tsx`: `__tests__/utils.test.tsx` —
73
+ это **тесты для `../utils.ts`** компонента, а не тесты хелпера. Хелперы нельзя
74
+ класть и в сам `*.test.tsx`-файл: при импорте из другого теста его `describe`
75
+ зарегистрируется повторно и тесты прогонятся несколько раз.
76
+
77
+ Страховка на уровне сборки: `npm run build` запускает `scripts/checkBundleSize.js`
78
+ и падает, если в `dist` превышен один из двух лимитов — **700 KB на отдельный
79
+ JS-файл** (ловит распухший `vendor`: всё из `node_modules` сливается в него
80
+ единственным чанком) или **2000 KB суммарно по всем JS-файлам** (ловит утечку
81
+ кода мимо `node_modules` и медленный рост множества мелких файлов).
82
+
41
83
  ### Импорты
42
84
 
43
85
  ```typescript
@@ -216,11 +258,15 @@ afterEach(() => {
216
258
 
217
259
  ### Когда нужны Visual tests
218
260
 
219
- Создавай отдельную `VisualTests` story если:
220
- - Компонент требует взаимодействия перед скриншотом (открыть dropdown, hover, focus).
261
+ **Скриншот-тесты обязательны для любого компонента с видимым UI.** По умолчанию все не-Playground stories автоматически снимаются на двух viewport'ах (xs/xl) — это базовое покрытие. Если оно недостаточно, создавай отдельную `VisualTests` story.
262
+
263
+ Создавай `VisualTests` story если:
264
+ - Компонент требует взаимодействия перед скриншотом (открыть dropdown, hover, focus, открыть модалку/попап) — **обязательно** для overlay-компонентов (Modal, Tooltip, Popover, Dropdown, LightBox).
221
265
  - Нужно покрыть состояние, которого нет в документационных stories.
222
266
  - Нужно объединить несколько вариантов в один скриншот.
223
267
 
268
+ После добавления новых stories обязательно сгенерируй baseline-скриншоты через CI workflow — без baseline скриншот-теста фактически нет, и regression не поймается.
269
+
224
270
  ### Обновление baseline
225
271
 
226
272
  ```text