@sberbusiness/triplex-next-mcp-server 1.48.0-beta → 1.50.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/bundle/components/Amount.md +2 -1
- package/bundle/components/Badge.md +2 -1
- package/bundle/components/BodyPage.md +2 -1
- package/bundle/components/Button.md +5 -1
- package/bundle/components/ButtonDropdownExtended.md +3 -1
- package/bundle/components/ButtonIcon.md +3 -1
- package/bundle/components/Calendar.md +3 -1
- package/bundle/components/CardAction.md +1 -0
- package/bundle/components/CheckboxXGroup.md +2 -1
- package/bundle/components/Chip.md +2 -1
- package/bundle/components/ChipMonthYearPicker.md +2 -1
- package/bundle/components/ChipMultiselect.md +4 -1
- package/bundle/components/ChipOptions.md +2 -1
- package/bundle/components/ChipSort.md +4 -1
- package/bundle/components/CollapsibleTree.md +2 -1
- package/bundle/components/ComposedKeyDownListener.md +4 -3
- package/bundle/components/DateField.md +7 -1
- package/bundle/components/Divider.md +85 -138
- package/bundle/components/DocumentNumberEdit.md +173 -214
- package/bundle/components/Dropdown.md +11 -1
- package/bundle/components/Ellipsis.md +131 -147
- package/bundle/components/FooterPage.md +3 -1
- package/bundle/components/FormField.md +2 -1
- package/bundle/components/Gap.md +1 -1
- package/bundle/components/Header.md +1 -0
- package/bundle/components/HeaderPage.md +3 -1
- package/bundle/components/HelpBox.md +2 -1
- package/bundle/components/IconWrapper.md +10 -6
- package/bundle/components/ImageGallery.md +2 -1
- package/bundle/components/ImageGalleryExtended.md +2 -1
- package/bundle/components/IslandAccordion.md +6 -1
- package/bundle/components/KeyDownListener.md +3 -2
- package/bundle/components/LightBox.md +7 -1
- package/bundle/components/List.md +2 -1
- package/bundle/components/ListActionItem.md +2 -1
- package/bundle/components/ListItem.md +3 -1
- package/bundle/components/ListItemControlsButton.md +2 -1
- package/bundle/components/ListItemSelectable.md +2 -1
- package/bundle/components/ListMasterFooter.md +2 -1
- package/bundle/components/ListMasterHeader.md +2 -1
- package/bundle/components/ListSortable.md +2 -1
- package/bundle/components/ListSortableItem.md +2 -1
- package/bundle/components/ListTableItem.md +4 -1
- package/bundle/components/LoaderMiddle.md +5 -1
- package/bundle/components/LoaderScreen.md +7 -1
- package/bundle/components/LoaderSmall.md +10 -4
- package/bundle/components/MaskedField.md +3 -1
- package/bundle/components/MediaWidth.md +2 -1
- package/bundle/components/MobileView.md +2 -1
- package/bundle/components/ModalWindow.md +4 -1
- package/bundle/components/MonthYearField.md +4 -1
- package/bundle/components/Notification.md +1 -0
- package/bundle/components/Overlay.md +7 -2
- package/bundle/components/Pagination.md +1 -7
- package/bundle/components/Radio.md +238 -115
- package/bundle/components/RadioXGroup.md +259 -0
- package/bundle/components/RadioYGroup.md +266 -0
- package/bundle/components/SegmentedControl.md +505 -158
- package/bundle/components/SelectExtendedField.md +2 -1
- package/bundle/components/SelectField.md +4 -1
- package/bundle/components/Skeleton.md +140 -69
- package/bundle/components/Spoiler.md +245 -63
- package/bundle/components/Step.md +257 -68
- package/bundle/components/SuggestField.md +30 -18
- package/bundle/components/Tabs.md +2 -1
- package/bundle/components/TabsLine.md +2 -1
- package/bundle/components/TextField.md +8 -4
- package/bundle/components/TextareaField.md +2 -1
- package/bundle/components/ThemeProvider.md +2 -1
- package/bundle/components/Tooltip.md +6 -3
- package/bundle/guides/context.md +4 -1
- package/bundle/manifest.json +32 -10
- package/bundle/release-notes/1.49.0.mdx +78 -0
- package/bundle/release-notes/1.50.0.mdx +35 -0
- package/bundle/release-notes/1.51.0.mdx +7 -0
- package/bundle/tokens.json +161 -11
- package/package.json +1 -1
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: Step
|
|
3
|
-
category:
|
|
4
|
-
|
|
5
|
-
figma-file: ""
|
|
6
|
-
related: [Stepper, Tooltip]
|
|
3
|
+
category: Step
|
|
4
|
+
related: [Stepper, Tooltip, Text]
|
|
7
5
|
tokens:
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
stories:
|
|
6
|
+
- Step.Background_Active
|
|
7
|
+
- Step.Background_Default
|
|
8
|
+
- Step.Background_Disabled
|
|
9
|
+
- Step.Background_Done
|
|
10
|
+
- Step.Background_Error
|
|
11
|
+
- Step.Background_Warning
|
|
12
|
+
- Step.Color_Active
|
|
13
|
+
- Step.Color_Default
|
|
14
|
+
- Step.Color_Disabled
|
|
15
|
+
- Step.Color_Done
|
|
16
|
+
- Step.Color_Error
|
|
17
|
+
- Step.Color_Warning
|
|
18
|
+
stories: stories/Step/Step.stories.tsx
|
|
21
19
|
version: "1.0"
|
|
22
20
|
---
|
|
23
21
|
|
|
@@ -25,92 +23,182 @@ version: "1.0"
|
|
|
25
23
|
|
|
26
24
|
## Назначение
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
(`
|
|
30
|
-
|
|
26
|
+
Круглый индикатор одного шага: номер внутри кружка, цвет фона и цвет номера кодируют статус
|
|
27
|
+
(`DEFAULT` / `DONE` / `ACTIVE` / `ERROR` / `WARNING` / `DISABLED`). Компонент рисует ровно один
|
|
28
|
+
шаг — ряд шагов и связь между ними собирает потребитель. Если передан `children`, кружок
|
|
29
|
+
оборачивается в `Tooltip`, раскрывающийся по наведению.
|
|
31
30
|
|
|
32
|
-
Используй когда:
|
|
33
|
-
|
|
34
|
-
Не используй когда: нужен текстовый label шага — добавь его рядом, `Step`
|
|
35
|
-
отрисовывает только номер в кружке.
|
|
31
|
+
Используй когда: нужно показать номер и состояние отдельного шага внутри своей вёрстки
|
|
32
|
+
пошагового сценария — так его использует `IslandAccordion` для нумерации секций.
|
|
36
33
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
## Файловая структура
|
|
40
|
-
|
|
41
|
-
```text
|
|
42
|
-
src/components/Step/
|
|
43
|
-
├── Step.tsx # FC
|
|
44
|
-
├── enums.ts # EStepStatus, EStepPosition
|
|
45
|
-
├── index.ts
|
|
46
|
-
└── styles/Step.module.less
|
|
47
|
-
```
|
|
34
|
+
Не используй когда:
|
|
48
35
|
|
|
49
|
-
|
|
50
|
-
`
|
|
36
|
+
- Нужен готовый горизонтальный степпер с переключением шагов, подписями и клавиатурной
|
|
37
|
+
навигацией — возьми `Stepper` (или `StepperExtended` для произвольного состава шагов).
|
|
38
|
+
`Step` в реализацию `Stepper` не входит: у того собственный `StepperStep`.
|
|
39
|
+
- Нужен кликабельный элемент. `Step` рендерит `<div>` без роли, фокуса и обработчиков —
|
|
40
|
+
интерактивность придётся навешивать снаружи, вместе с ролью и клавиатурой.
|
|
41
|
+
- Нужно показать статус без номера — возьми `Marker` или `MarkerStatus`.
|
|
51
42
|
|
|
52
43
|
---
|
|
53
44
|
|
|
54
45
|
## Варианты и props
|
|
55
46
|
|
|
56
|
-
###
|
|
47
|
+
### Обязательные props
|
|
48
|
+
|
|
49
|
+
| Prop | Тип | Описание |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| `step` | `number` | Номер шага. Рендерится внутри кружка через `Text` (`B1`, semibold). Компонент не форматирует и не ограничивает значение: двузначные номера помещаются, кружок под содержимое не растягивается |
|
|
52
|
+
| `status` | `EStepStatus` | Состояние шага. Определяет **только** пару цветов — фон кружка и цвет номера |
|
|
57
53
|
|
|
58
|
-
|
|
54
|
+
### Опциональные props
|
|
55
|
+
|
|
56
|
+
| Prop | Тип | По умолчанию | Описание |
|
|
59
57
|
|---|---|---|---|
|
|
60
|
-
| `
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `children?` | `ReactNode` | — | Контент Tooltip'а. Если не передан — Tooltip не рендерится. |
|
|
64
|
-
| `...HTMLAttributes<HTMLDivElement>` | — | — | Прокидываются на корневой `<div>`. |
|
|
58
|
+
| `size` | `EComponentSize` | `EComponentSize.MD` | Размер кружка. SM и MD дают 28px, LG — 32px |
|
|
59
|
+
| `position` | `EStepPosition` | `EStepPosition.Default` | Позиция шага в ряду. Влияет **только** на выравнивание указателя подсказки и потому имеет смысл лишь вместе с `children` |
|
|
60
|
+
| `children` | `React.ReactNode` | — | Содержимое подсказки. Без него подсказка не создаётся и `Tooltip` не монтируется вовсе |
|
|
65
61
|
|
|
66
|
-
|
|
62
|
+
Остальные props — стандартные атрибуты `div`
|
|
63
|
+
(`IStepProps extends React.HTMLAttributes<HTMLDivElement>`), уходят на корневой элемент
|
|
64
|
+
через `...rest`.
|
|
67
65
|
|
|
68
|
-
|
|
66
|
+
### Размер и статус независимы
|
|
69
67
|
|
|
70
|
-
|
|
68
|
+
Две оси не пересекаются: `size` отвечает за геометрию, `status` — за пару цветов. Любая
|
|
69
|
+
комбинация допустима, поэтому матрицу 3×6 целиком покрывает story `VisualTests`,
|
|
70
|
+
а документационные stories показывают оси по отдельности.
|
|
71
71
|
|
|
72
|
-
### `
|
|
72
|
+
### `position` и подсказка
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
`position` отображается на `alignTip` подсказки: `XFirst` → `START`, `Default` → `CENTER`,
|
|
75
|
+
`XLast` → `END`. Для крайних шагов ряда указатель прижимается к краю, чтобы подсказка
|
|
76
|
+
не выходила за границы ряда. Без `children` prop ни на что не влияет.
|
|
77
|
+
|
|
78
|
+
Значение для ряда шагов считает экспортируемый хелпер `calcPosition(stepCount, i)`:
|
|
79
|
+
`XFirst` для `i === 0`, `XLast` для последнего, иначе `Default`. Для ряда из одного шага
|
|
80
|
+
возвращает `XFirst` — проверка на первый шаг идёт первой.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Дизайн-токены
|
|
79
85
|
|
|
80
|
-
`
|
|
81
|
-
|
|
86
|
+
Переопределяются через `ThemeProvider` (prop `tokens`) — см. `ThemeProvider-ai.md` →
|
|
87
|
+
«Как переопределять токены». Значения по умолчанию — `src/components/DesignTokens/components/Step.ts`.
|
|
88
|
+
|
|
89
|
+
```text
|
|
90
|
+
Step.Background_Default
|
|
91
|
+
Step.Background_Done
|
|
92
|
+
Step.Background_Active
|
|
93
|
+
Step.Background_Error
|
|
94
|
+
Step.Background_Warning
|
|
95
|
+
Step.Background_Disabled
|
|
96
|
+
Step.Color_Default
|
|
97
|
+
Step.Color_Done
|
|
98
|
+
Step.Color_Active
|
|
99
|
+
Step.Color_Error
|
|
100
|
+
Step.Color_Warning
|
|
101
|
+
Step.Color_Disabled
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
По паре токенов (`Background_*` + `Color_*`) на каждое значение `EStepStatus`; добавление
|
|
105
|
+
статуса означает и два новых токена. Цвет номера задаётся селектором `& > span` — он попадает
|
|
106
|
+
на элемент, который рендерит `Text`.
|
|
107
|
+
|
|
108
|
+
Размеры (28 / 32px, `border-radius: 50%`) заданы литералами в `styles/Step.module.less` — они
|
|
109
|
+
специфичны для компонента. Брейкпоинт адаптива берётся из LESS-переменной `@screen-sm-max`.
|
|
82
110
|
|
|
83
111
|
---
|
|
84
112
|
|
|
85
113
|
## Инварианты
|
|
86
114
|
|
|
87
|
-
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
-
|
|
91
|
-
|
|
115
|
+
- `forwardRef<HTMLDivElement>` — не убирать. Ref всегда указывает на корневой `<div class="step">`,
|
|
116
|
+
и в варианте с подсказкой тоже: внешний ref мерджится с внутренним через
|
|
117
|
+
`setForwardedRef` (`src/helpers/setForwardedRef.ts`).
|
|
118
|
+
- Внутренний ref на корневой `<div>` обязателен: он уходит в `Tooltip` как `targetRef` —
|
|
119
|
+
относительно этого узла считается позиция подсказки. Убрать внутренний ref или перевесить
|
|
120
|
+
его на другой элемент нельзя.
|
|
121
|
+
- Корневой элемент — `<div>`. Кружок центрует содержимое через `display: flex`; смена тега
|
|
122
|
+
сломает вёрстку и типизацию props.
|
|
123
|
+
- `displayName` — `"Step"`. Не менять.
|
|
124
|
+
- Значения `EStepStatus` (`"default"` / `"done"` / `"active"` / `"error"` / `"warning"` /
|
|
125
|
+
`"disabled"`) совпадают с именами CSS-классов в `Step.module.less` и с суффиксами токенов
|
|
126
|
+
`Step.Background_*` / `Step.Color_*`. Переименование значения enum — ломающее изменение
|
|
127
|
+
сразу в трёх местах.
|
|
128
|
+
- Значения `EStepPosition` — `PascalCase` (`Default` / `XFirst` / `XLast`), в отличие от
|
|
129
|
+
остальных enum'ов библиотеки. Это часть публичного API, приводить к общему виду нельзя.
|
|
130
|
+
- Подсказка монтируется только при непустом `children`. Оборачивать кружок в `Tooltip`
|
|
131
|
+
безусловно нельзя: пустая подсказка перехватывала бы наведение.
|
|
132
|
+
- `EStepStatus.DISABLED` — только цвет. Ни `pointer-events`, ни `aria-disabled` компонент
|
|
133
|
+
не выставляет, подсказка у выключенного шага продолжает работать.
|
|
134
|
+
- Экспорты `Step`, `IStepProps`, `calcPosition`, `EStepStatus` и `EStepPosition` идут в barrel
|
|
135
|
+
`src/components/Step/index.ts` — сохранять. `calcPosition` внутри компонента не используется,
|
|
136
|
+
но входит в публичный API: он нужен потребителю, который собирает ряд шагов.
|
|
137
|
+
- `className` деструктурируется из props и мерджится через `clsx`, поэтому в `...rest`
|
|
138
|
+
его нет и перезатереть базовые классы он не может.
|
|
92
139
|
|
|
93
140
|
---
|
|
94
141
|
|
|
95
142
|
## Accessibility
|
|
96
143
|
|
|
97
|
-
-
|
|
98
|
-
|
|
99
|
-
-
|
|
144
|
+
- Собственной ARIA-роли нет: корневой `<div>` — неинтерактивный контейнер, в порядок фокуса
|
|
145
|
+
не попадает, обработчиков клавиатуры у компонента нет.
|
|
146
|
+
- Номер шага — единственное, что читает скринридер. Смысл шага в разметку не попадает:
|
|
147
|
+
текст подсказки живёт в `Tooltip`, который раскрывается по наведению или клику мышью
|
|
148
|
+
и с клавиатуры недоступен. Если шаг должен быть понятен без мыши, опиши его видимым
|
|
149
|
+
текстом рядом в своей вёрстке.
|
|
150
|
+
- Статус кодируется только цветом. Дублируй его видимым текстом на стороне потребителя.
|
|
151
|
+
- Одного `aria-label` на `Step` недостаточно: корневой `<div>` без роли получает роль
|
|
152
|
+
`generic`, которой [спецификация HTML-AAM](https://www.w3.org/TR/html-aria/) запрещает
|
|
153
|
+
доступное имя, — атрибут попадёт в разметку через `...rest`, но скринридер его не
|
|
154
|
+
озвучит. Если имя нужно именно на шаге, передай вместе с ним роль, допускающую
|
|
155
|
+
именование: `<Step step={2} status={EStepStatus.ERROR} role="img" aria-label="Шаг 2, ошибка" />`.
|
|
156
|
+
- Ряд шагов семантики списка не создаёт: если нужна структура, оберни шаги в `<ol>`/`<li>`
|
|
157
|
+
на стороне потребителя.
|
|
158
|
+
- Компонент не хардкодит текстовые строки: содержимое подсказки и любые `aria-label` задаёт
|
|
159
|
+
потребитель. Библиотека мультиязычная.
|
|
100
160
|
|
|
101
161
|
---
|
|
102
162
|
|
|
103
163
|
## Связанные компоненты
|
|
104
164
|
|
|
105
|
-
- `Stepper` — готовый
|
|
106
|
-
|
|
107
|
-
|
|
165
|
+
- `Stepper` — готовый горизонтальный степпер с подписями, выбором шага и клавиатурной
|
|
166
|
+
навигацией. Альтернатива, когда нужен весь сценарий целиком, а не отдельный индикатор.
|
|
167
|
+
Общего кода со `Step` нет: у `Stepper` собственный `StepperStep`.
|
|
168
|
+
- `Tooltip` — рендерится вокруг кружка, когда передан `children`. `Step` фиксирует
|
|
169
|
+
`size={ETooltipSize.SM}` и `toggleType="hover"`, а из props потребителя прокидывает
|
|
170
|
+
только выравнивание указателя (`alignTip`) — остальное настроить нельзя. `disableAdaptiveMode`
|
|
171
|
+
не передаётся, поэтому на экранах до `SM_MAX` включительно подсказка открывается
|
|
172
|
+
в адаптивном (полноэкранном) режиме.
|
|
173
|
+
- `Text` — рендерит номер шага (`ETextSize.B1`, `EFontWeightText.SEMIBOLD`). Цвет номера
|
|
174
|
+
задаётся снаружи, селектором `& > span` в стилях `Step`.
|
|
175
|
+
- `IslandAccordion` — потребитель: нумерует секции аккордеона, передавая `Step` номер,
|
|
176
|
+
статус и `size`. Опирается на то, что `Step` рисует только кружок, без внешних отступов.
|
|
108
177
|
|
|
109
178
|
---
|
|
110
179
|
|
|
111
180
|
## Stories
|
|
112
181
|
|
|
113
|
-
|
|
182
|
+
Основные истории: `stories/Step/Step.stories.tsx`
|
|
183
|
+
Файлы примеров: `stories/Step/examples/`
|
|
184
|
+
|
|
185
|
+
| Story | Example file | Что демонстрирует |
|
|
186
|
+
|---|---|---|
|
|
187
|
+
| `Playground` | `Playground.tsx` | Интерактивный контроль `step`, `status`, `size`, `position` и текста подсказки |
|
|
188
|
+
| `Default` | `Default.tsx` | Минимальный шаг: обязательны только `step` и `status` |
|
|
189
|
+
| `Sizes` | `Sizes.tsx` | Размеры SM / MD / LG при одном статусе |
|
|
190
|
+
| `Statuses` | `Statuses.tsx` | Все шесть значений `EStepStatus` при одном размере |
|
|
191
|
+
| `WithTooltip` | `WithTooltip.tsx` | Шаг с подсказкой: `children` становится её содержимым |
|
|
192
|
+
| `Example` | `Example.tsx` | Ряд шагов платёжного сценария, `position` считается через `calcPosition` |
|
|
193
|
+
| `VisualTests` | `VisualTests.tsx` | Матрица размеров × статусов, двузначные номера и раскрытая подсказка (открывается `play`) — для скриншот-тестов |
|
|
194
|
+
|
|
195
|
+
Из скриншот-тестов исключены (`testRunner: { skip: true }`) `Playground`, `Default` и
|
|
196
|
+
`WithTooltip`: первые два визуально дублируют `Statuses`, а закрытая подсказка
|
|
197
|
+
неотличима от обычного шага — раскрытая снимается в `VisualTests`, где её открывает
|
|
198
|
+
`play`. Не добавляй им скриншоты: это дубли, которые придётся поддерживать.
|
|
199
|
+
|
|
200
|
+
Story ID участвуют в именах baseline-скриншотов (`step--sizes--xs.png` и т.п.), поэтому
|
|
201
|
+
переименование story требует перегенерации baseline и удаления осиротевших файлов.
|
|
114
202
|
|
|
115
203
|
---
|
|
116
204
|
|
|
@@ -118,4 +206,105 @@ src/components/Step/
|
|
|
118
206
|
|
|
119
207
|
| Дата | Изменение |
|
|
120
208
|
|---|---|
|
|
121
|
-
| 2026-
|
|
209
|
+
| 2026-09-24 | Создан документ. AI-рефакторинг: `Step` переведён с `React.FC` на `forwardRef` (внешний ref мерджится с внутренним, который уходит в `Tooltip` как `targetRef`), JSDoc на компоненте и дефолтах props, `renderContent()` заменён на вычисляемый один раз элемент, приведён к порядку LESS. Добавлены unit-тесты (`__tests__/Step.test.tsx`), stories созданы по modern pattern (`stories/Step/`). |
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Примеры использования
|
|
214
|
+
|
|
215
|
+
### Default
|
|
216
|
+
|
|
217
|
+
`stories/Step/examples/Default.tsx`
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
import React from "react";
|
|
221
|
+
import { Step, EStepStatus } from "@sberbusiness/triplex-next";
|
|
222
|
+
|
|
223
|
+
export const Default = () => <Step step={1} status={EStepStatus.DEFAULT} />;
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Sizes
|
|
227
|
+
|
|
228
|
+
`stories/Step/examples/Sizes.tsx`
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
import React from "react";
|
|
232
|
+
import { Step, EComponentSize, EStepStatus } from "@sberbusiness/triplex-next";
|
|
233
|
+
|
|
234
|
+
const SIZES = Object.values(EComponentSize);
|
|
235
|
+
|
|
236
|
+
export const Sizes = () => (
|
|
237
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
|
238
|
+
{SIZES.map((size, index) => (
|
|
239
|
+
<div key={size}>
|
|
240
|
+
<div style={{ marginBottom: 8, fontSize: 16, fontWeight: 700 }}>{size.toUpperCase()}</div>
|
|
241
|
+
<Step step={index + 1} status={EStepStatus.ACTIVE} size={size} />
|
|
242
|
+
</div>
|
|
243
|
+
))}
|
|
244
|
+
</div>
|
|
245
|
+
);
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Statuses
|
|
249
|
+
|
|
250
|
+
`stories/Step/examples/Statuses.tsx`
|
|
251
|
+
|
|
252
|
+
```tsx
|
|
253
|
+
import React from "react";
|
|
254
|
+
import { Step, EStepStatus } from "@sberbusiness/triplex-next";
|
|
255
|
+
|
|
256
|
+
const STATUSES = Object.values(EStepStatus);
|
|
257
|
+
|
|
258
|
+
export const Statuses = () => (
|
|
259
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
|
260
|
+
{STATUSES.map((status, index) => (
|
|
261
|
+
<div key={status}>
|
|
262
|
+
<div style={{ marginBottom: 8, fontSize: 16, fontWeight: 700 }}>{status.toUpperCase()}</div>
|
|
263
|
+
<Step step={index + 1} status={status} />
|
|
264
|
+
</div>
|
|
265
|
+
))}
|
|
266
|
+
</div>
|
|
267
|
+
);
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### WithTooltip
|
|
271
|
+
|
|
272
|
+
`stories/Step/examples/WithTooltip.tsx`
|
|
273
|
+
|
|
274
|
+
```tsx
|
|
275
|
+
import React from "react";
|
|
276
|
+
import { Step, EStepStatus } from "@sberbusiness/triplex-next";
|
|
277
|
+
|
|
278
|
+
export const WithTooltip = () => (
|
|
279
|
+
<Step step={2} status={EStepStatus.ACTIVE}>
|
|
280
|
+
Заполните реквизиты получателя
|
|
281
|
+
</Step>
|
|
282
|
+
);
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Example
|
|
286
|
+
|
|
287
|
+
`stories/Step/examples/Example.tsx`
|
|
288
|
+
|
|
289
|
+
```tsx
|
|
290
|
+
import React from "react";
|
|
291
|
+
import { Step, calcPosition, EStepStatus } from "@sberbusiness/triplex-next";
|
|
292
|
+
|
|
293
|
+
const STEPS = [
|
|
294
|
+
{ hint: "Реквизиты получателя заполнены", status: EStepStatus.DONE },
|
|
295
|
+
{ hint: "Проверьте сумму и назначение платежа", status: EStepStatus.ACTIVE },
|
|
296
|
+
{ hint: "Подписание доступно после проверки", status: EStepStatus.DEFAULT },
|
|
297
|
+
{ hint: "Отправка доступна после подписания", status: EStepStatus.DISABLED },
|
|
298
|
+
];
|
|
299
|
+
|
|
300
|
+
export const Example = () => (
|
|
301
|
+
<div style={{ display: "flex", alignItems: "center", gap: 16 }}>
|
|
302
|
+
{STEPS.map(({ hint, status }, index) => (
|
|
303
|
+
<Step key={hint} step={index + 1} status={status} position={calcPosition(STEPS.length, index)}>
|
|
304
|
+
{hint}
|
|
305
|
+
</Step>
|
|
306
|
+
))}
|
|
307
|
+
</div>
|
|
308
|
+
);
|
|
309
|
+
```
|
|
310
|
+
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: SuggestField
|
|
3
3
|
category: TextFields
|
|
4
|
-
related: [Suggest, SelectField, TextField, FormField, Dropdown]
|
|
4
|
+
related: [Suggest, SelectField, MultiselectField, SelectExtendedField, TextField, FormField, Dropdown, LoaderSmall]
|
|
5
5
|
tokens: []
|
|
6
6
|
stories: stories/SuggestField/SuggestField.stories.tsx
|
|
7
7
|
version: "1.0"
|
|
@@ -45,7 +45,7 @@ version: "1.0"
|
|
|
45
45
|
| `options` | `T[]` | Список опций. Фильтрует потребитель в ответ на `onFilter` — компонент список не фильтрует и не сортирует |
|
|
46
46
|
| `tooltipHint` | `string` | Текст подсказки («Ничего не найдено», «Введите более 3 символов»). Обязателен, даже если `tooltipOpen` всегда `false` |
|
|
47
47
|
| `tooltipOpen` | `boolean` | Показывать подсказку. Управляется снаружи: компонент сам не решает, когда список «пустой по делу» |
|
|
48
|
-
| `onSelect` | `(value: T \| undefined) => void` | Опция выбрана. Вызывается с `undefined`, когда значение
|
|
48
|
+
| `onSelect` | `(value: T \| undefined) => void` | Опция выбрана. Вызывается с `undefined`, когда значение сбрасывается, — на обеих платформах только кнопкой очистки (см. «Инварианты»). Blur на десктопе и закрытие дропдауна на мобильном значение не сбрасывают даже при пустом вводе. Не сбрасывай значение в `onFilter("")`: при `clearInputOnFocus` десктоп зовёт его уже на фокусе |
|
|
49
49
|
| `onFilter` | `(value: string) => void` | Текст поля ввода изменился. Ответ — обновлённый `options` |
|
|
50
50
|
| `inputProps` | `IFormFieldInputProps & { ref?: React.Ref<HTMLInputElement> }` | Свойства поля ввода. **Не опционально:** передавай хотя бы `{}`. Через `inputProps.ref` потребитель получает ref на `<input>` — собственного `forwardRef` у компонента нет |
|
|
51
51
|
|
|
@@ -91,8 +91,7 @@ version: "1.0"
|
|
|
91
91
|
| Ввод текста | `onFilter(value)`, сброс `aria-activedescendant`, список снова разрешён к автооткрытию |
|
|
92
92
|
| `Escape` при открытом списке | Список закрывается, всплытие останавливается, автооткрытие подавлено до следующего ввода или mousedown |
|
|
93
93
|
| Выбор опции | `onSelect(option)`, в поле подставляется `label`, автооткрытие подавлено |
|
|
94
|
-
| Blur
|
|
95
|
-
| Blur с пустым полем и заданным `value` | `onSelect(undefined)` |
|
|
94
|
+
| Blur | В поле возвращается `label` текущего `value` (пустая строка, если `value` не задан). `onSelect` не вызывается: введённый, но не выбранный текст отбрасывается, в том числе пустой |
|
|
96
95
|
| Клик по кнопке очистки | `onSelect(undefined)` (если было значение), `onFilter("")` (если поле не пустое), затем `onClear(event)` |
|
|
97
96
|
|
|
98
97
|
`inputProps.onFocus` / `onBlur` / `onKeyDown` / `onMouseDown` / `onChange` вызываются
|
|
@@ -104,9 +103,14 @@ version: "1.0"
|
|
|
104
103
|
|
|
105
104
|
Поле-триггер — `readOnly`, по фокусу открывается полноэкранный `Dropdown` с собственным полем
|
|
106
105
|
ввода (автофокус). При `tooltipOpen` вместо списка показывается `SuggestFieldMobileDropdownHint`
|
|
107
|
-
с текстом `tooltipHint`. Закрытие дропдауна
|
|
108
|
-
|
|
109
|
-
|
|
106
|
+
с текстом `tooltipHint`. Закрытие дропдауна без выбора опции значение не сбрасывает, даже при
|
|
107
|
+
пустом поле ввода: на закрытии поле ввода дропдауна возвращается к `label` текущего `value`
|
|
108
|
+
(через `onClose` у `Dropdown`, а не только на автофокусе — при повторном открытии во время
|
|
109
|
+
анимации закрытия содержимое ещё смонтировано и автофокус не срабатывает). При следующем
|
|
110
|
+
открытии с `clearInputOnFocus` поле очищается автофокусом. Кнопка очистки работает как на
|
|
111
|
+
десктопе: `onSelect(undefined)` (если было значение), `onFilter("")` (если у значения непустой
|
|
112
|
+
`label` — его и показывает поле-триггер), затем `onClear(event)`. После закрытия поле-триггер
|
|
113
|
+
скроллится в центр экрана (в iOS открытие дропдауна уводит страницу вверх).
|
|
110
114
|
|
|
111
115
|
---
|
|
112
116
|
|
|
@@ -130,13 +134,15 @@ version: "1.0"
|
|
|
130
134
|
и переименование наблюдаемо для потребителя.
|
|
131
135
|
- `SuggestField` — `Object.assign(SuggestFieldBase, { Input: FormFieldInput })`. Дженерик
|
|
132
136
|
сохраняется только у самой функции; `SuggestField.Input` — часть публичного API.
|
|
133
|
-
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
-
|
|
138
|
-
|
|
139
|
-
|
|
137
|
+
- **Оба варианта получают все props.** `SuggestField.tsx` отдаёт `{...props}` и в
|
|
138
|
+
`SuggestFieldDesktop`, и в `SuggestFieldMobile`. Не возвращай явный список props для мобильной
|
|
139
|
+
ветки: пропущенный prop молча терялся бы на мобильной ширине (так до 1.49.0 терялись
|
|
140
|
+
`className`, `id`, `data-test-id`, `active` и `renderInput`).
|
|
141
|
+
- **Значение сбрасывает только кнопка очистки — на обеих платформах.** Blur на десктопе и
|
|
142
|
+
закрытие дропдауна на мобильном `onSelect(undefined)` не зовут, даже при пустом вводе: иначе
|
|
143
|
+
при `clearInputOnFocus` значение терялось от простого фокуса без выбора. Кнопка очистки
|
|
144
|
+
рендерится только при `onClear`, так что без него встроенного сброса нет. Возврат сброса в
|
|
145
|
+
blur / закрытие дропдауна — изменение поведения, решение мейнтейнера.
|
|
140
146
|
- Видимость десктопного списка синхронизируется **во время рендера** (`if (inputFocused) {...}`),
|
|
141
147
|
а не в `useEffect`. Так требует правило `react-hooks/set-state-in-effect`; возврат к эффекту
|
|
142
148
|
вернёт и ошибку линтера, и лишний кадр рендера.
|
|
@@ -201,8 +207,10 @@ version: "1.0"
|
|
|
201
207
|
- `Dropdown` — выпадающий список: `DropdownDesktop` + `DropdownList` + `DropdownListItem` на
|
|
202
208
|
десктопе, `DropdownMobile*` в полноэкранном мобильном варианте. `ISuggestFieldDesktopDropdownProps`
|
|
203
209
|
наследует `IDropdownDesktopProps`.
|
|
204
|
-
- `
|
|
205
|
-
|
|
210
|
+
- `LoaderSmall` — индикатор подгрузки подсказок: `SuggestFieldDesktop` и `SuggestFieldMobile`
|
|
211
|
+
показывают его с темой `BRAND`, пока `loading`.
|
|
212
|
+
- `Tooltip` — подсказка `tooltipHint` у десктопного поля. В `related` не вынесен: связь узкая,
|
|
213
|
+
а список адресатов и так шире ориентира 2–5.
|
|
206
214
|
- `MobileView` — переключает десктопный и мобильный варианты по ширине экрана. В `related` не
|
|
207
215
|
вынесен по той же причине.
|
|
208
216
|
- `SuggestFieldDesktop` / `SuggestFieldMobile` — платформенные варианты, экспортируются из barrel
|
|
@@ -238,6 +246,8 @@ version: "1.0"
|
|
|
238
246
|
| Дата | Изменение |
|
|
239
247
|
|---|---|
|
|
240
248
|
| 2026-09-23 | Создан документ |
|
|
249
|
+
| 2026-09-29 | Аудит симметрии `related` (TRI-156): добавлены обратные ссылки (`MultiselectField`, `SelectExtendedField`) и контракт по рендеру `LoaderSmall`. Публичный API и поведение не затронуты. |
|
|
250
|
+
| 2026-10-02 | Сброс значения только кнопкой очистки на обеих платформах: при `clearInputOnFocus` значение терялось от фокуса без выбора. Blur на десктопе и закрытие дропдауна на мобильном больше не вызывают `onSelect(undefined)`; мобильная кнопка очистки сама зовёт `onSelect(undefined)` и `onFilter("")` (последний — при непустом `label`), как десктопная; на закрытии мобильного дропдауна его поле ввода возвращается к `label`. `SuggestField` отдаёт мобильному варианту все props, как десктопному: на мобильной ширине больше не теряются `className`, `id`, `data-test-id`, `active` и `renderInput`. Обновлены `onSelect`, поведение обеих платформ и «Инварианты» |
|
|
241
251
|
|
|
242
252
|
---
|
|
243
253
|
|
|
@@ -715,7 +725,6 @@ export const ProductionExample = () => {
|
|
|
715
725
|
setInputPristine(false);
|
|
716
726
|
|
|
717
727
|
if (inputValue.length === 0) {
|
|
718
|
-
setValue(undefined);
|
|
719
728
|
setOptions(FOOD_OPTIONS);
|
|
720
729
|
setTooltipOpen(false);
|
|
721
730
|
} else {
|
|
@@ -750,7 +759,10 @@ export const ProductionExample = () => {
|
|
|
750
759
|
};
|
|
751
760
|
|
|
752
761
|
const handleClear: React.MouseEventHandler<HTMLButtonElement> = () => {
|
|
753
|
-
|
|
762
|
+
// На мобильном фокус открыл бы полноэкранный дропдаун сразу после очистки.
|
|
763
|
+
if (!adaptive) {
|
|
764
|
+
inputRef.current?.focus();
|
|
765
|
+
}
|
|
754
766
|
};
|
|
755
767
|
|
|
756
768
|
const handleLinkClick: React.MouseEventHandler<HTMLAnchorElement> = (event) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: Tabs
|
|
3
3
|
category: Tabs
|
|
4
|
-
related: [TabsExtended, TabsLine, ButtonDropdown]
|
|
4
|
+
related: [TabsExtended, TabsLine, ButtonDropdown, SegmentedControl]
|
|
5
5
|
tokens:
|
|
6
6
|
- Tabs.Type1_Tab_Background_Selected
|
|
7
7
|
- Tabs.Type2_Tab_Background_Selected
|
|
@@ -110,6 +110,7 @@ Tabs.Type2_Tab_Background_Selected
|
|
|
110
110
|
- `TabsLine` — альтернативный визуальный паттерн табов (подчёркивание вместо залитой кнопки).
|
|
111
111
|
- `ButtonDropdown` — кнопка выпадающего списка, которую `Tabs` рендерит сам для не поместившихся табов; тема подбирается по `type` (`DOTS_SECONDARY` / `DOTS_SECONDARY_LIGHT`).
|
|
112
112
|
- `Badge` — `Badge.Dot` как значок новых уведомлений на кнопке таба (рендерится внутри `TabsExtended.Content.TabButton`).
|
|
113
|
+
- `SegmentedControl` — альтернатива, когда переключатель не управляет панелями контента: компактный набор сегментов без семантики табов, зато с режимом множественного выбора.
|
|
113
114
|
|
|
114
115
|
---
|
|
115
116
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: TabsLine
|
|
3
3
|
category: Tabs
|
|
4
|
-
related: [Tabs, TabsExtended, Dropdown, Badge, Text]
|
|
4
|
+
related: [Tabs, TabsExtended, Dropdown, Badge, Text, SegmentedControl]
|
|
5
5
|
tokens:
|
|
6
6
|
- TabsLine.BorderColor_Active
|
|
7
7
|
- TabsLine.BorderColor_Hover
|
|
@@ -144,6 +144,7 @@ TabsLine.Shadow_Focus
|
|
|
144
144
|
- `Badge` — значок новых уведомлений на табе (`Badge.Dot`).
|
|
145
145
|
- `Text` — текст таба; размер выбирается по `size` через `tabsLineSizeToTextSizeMap`.
|
|
146
146
|
- `MobileView` — переключает десктопный и мобильный варианты по ширине окна (<768px). Собственной разметки не добавляет.
|
|
147
|
+
- `SegmentedControl` — альтернатива, когда переключатель не управляет панелями контента: компактный набор сегментов без семантики табов, зато с режимом множественного выбора.
|
|
147
148
|
|
|
148
149
|
---
|
|
149
150
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: TextField
|
|
3
3
|
category: TextFields
|
|
4
|
-
related: [MaskedField,
|
|
4
|
+
related: [MaskedField, SmallInput, TextareaField, MonthYearField, FormField, FormGroup]
|
|
5
5
|
tokens: []
|
|
6
6
|
stories: stories/TextField/TextField.stories.tsx
|
|
7
7
|
version: "1.0"
|
|
@@ -62,8 +62,8 @@ version: "1.0"
|
|
|
62
62
|
|
|
63
63
|
- `forwardRef` на `TextField` и `TextFieldBase` — не убирать. Внешний `ref` указывает на корневой `<div>` `FormField`; ref на сам input — только через `inputProps.ref`.
|
|
64
64
|
- Публичный API (`ITextFieldProps`: `inputProps` + `label`/`prefix`/`postfix`/`description`/`counter` + унаследованные `size`/`status`/`active`) — изменение имён/типов/значений enum — breaking change.
|
|
65
|
-
- `TextFieldBase` намеренно НЕ экспортируется — приватная база.
|
|
66
|
-
-
|
|
65
|
+
- Компонент `TextFieldBase` намеренно НЕ экспортируется — приватная база. Его тип `ITextFieldBaseProps` экспортируется из barrel только как тип (`export type`): `children` в нём обязателен (слот поля ввода), поэтому потребители типизируют props собственных обёрток через `Omit<ITextFieldBaseProps, "children">` — так же, как `TextField`, `MaskedField`, `AmountField` и `TextareaField`. Переименование или изменение полей — breaking change.
|
|
66
|
+
- `inputProps.ref` типизирован как `React.Ref<HTMLInputElement>` (расширен в TRIPLEX-935): принимает и объектный, и callback-ref, `FormFieldInput` отдаёт его прямо на `<input>`.
|
|
67
67
|
- Уникальный `id` для связки label↔input генерируется через `lodash uniqueId` в `FormFieldInput` — не заменять на `useId` (React 17 совместимость через release-0).
|
|
68
68
|
- `description || counter` рендерятся в общем блоке `FormFieldDescription` — не разносить по отдельным блокам (изменит DOM и скриншоты).
|
|
69
69
|
|
|
@@ -80,10 +80,12 @@ version: "1.0"
|
|
|
80
80
|
|
|
81
81
|
## Связанные компоненты
|
|
82
82
|
|
|
83
|
-
- `TextFieldBase` (внутренний, не в barrel) — базовая раскладка `FormGroup`/`FormField` + слоты `prefix`/`children`/`label`/`postfix` и блок `description`/`counter`. Описан здесь, отдельного AI.md не имеет.
|
|
83
|
+
- `TextFieldBase` (внутренний, не в barrel; публичен только тип `ITextFieldBaseProps`) — базовая раскладка `FormGroup`/`FormField` + слоты `prefix`/`children`/`label`/`postfix` и блок `description`/`counter`. Описан здесь, отдельного AI.md не имеет.
|
|
84
84
|
- `FormField` / `FormFieldInput` и слоты семейства — низкоуровневые строительные блоки; используются напрямую, когда нужна нестандартная композиция.
|
|
85
85
|
- `FormGroup` — контейнер вертикальной раскладки поля и описания.
|
|
86
86
|
- `FormFieldClear` — кнопка очистки, передаётся в `postfix`.
|
|
87
|
+
- `MaskedField` — то же поле, но с маской ввода: отличается только элементом ввода (`FormFieldMaskedInput` вместо `FormFieldInput`).
|
|
88
|
+
- `SmallInput` — альтернатива для инлайн-редактирования короткого значения: без лейбла, статусов, описания и счётчика.
|
|
87
89
|
- `TextareaField`, `AmountField`, `SuggestField`, `DateField`, `MonthYearField` — специализированные поля той же группы `TextFields`.
|
|
88
90
|
|
|
89
91
|
---
|
|
@@ -113,6 +115,8 @@ version: "1.0"
|
|
|
113
115
|
| Дата | Изменение |
|
|
114
116
|
|---|---|
|
|
115
117
|
| 2026-07-27 | Создан документ (TRI-5). AI-рефакторинг TextField/TextFieldBase, unit-тесты, миграция stories на modern pattern |
|
|
118
|
+
| 2026-09-29 | Тип `ITextFieldBaseProps` экспортирован из barrel (`export type`); компонент `TextFieldBase` остаётся приватным (TRI-158). Удалён устаревший инвариант про `RefObject` у `inputProps.ref` — тип уже `React.Ref` после TRIPLEX-935 |
|
|
119
|
+
| 2026-09-29 | Аудит симметрии `related` (TRI-156): добавлены обратные ссылки (`SmallInput`). Публичный API и поведение не затронуты. |
|
|
116
120
|
|
|
117
121
|
---
|
|
118
122
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: TextareaField
|
|
3
3
|
category: TextFields
|
|
4
|
-
related: [TextField,
|
|
4
|
+
related: [TextField, FormField, FormGroup]
|
|
5
5
|
tokens: []
|
|
6
6
|
stories: stories/TextareaField/TextareaField.stories.tsx
|
|
7
7
|
version: "1.0"
|
|
@@ -115,6 +115,7 @@ version: "1.0"
|
|
|
115
115
|
|---|---|
|
|
116
116
|
| 2026-09-17 | Создан документ (TRI-96). AI-рефакторинг `TextareaField`: проброс `ref` на корневой `<div>` через `forwardRef`, `displayName`, unit-тесты вместо mock-заглушек |
|
|
117
117
|
| 2026-09-17 | Ломающее изменение: `disabled` убран из `IFormFieldTextareaProps` (`Omit`), поэтому его больше нельзя передать в `textareaProps` — блокировка только через `status`. Правка по ревью PR #629, зафиксирована в release notes 1.47.0 |
|
|
118
|
+
| 2026-09-29 | Аудит симметрии `related` (TRI-156): из `related` убраны несимметричные имена (`MaskedField`, `DateField`, `MonthYearField`). Пояснения к убранным именам сохранены прозой в «Связанные компоненты»; публичный API и поведение не затронуты. |
|
|
118
119
|
|
|
119
120
|
---
|
|
120
121
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: ThemeProvider
|
|
3
3
|
category: ThemeProvider
|
|
4
|
-
related: [
|
|
4
|
+
related: []
|
|
5
5
|
tokens: []
|
|
6
6
|
stories: stories/ThemeProvider/ThemeProvider.stories.tsx
|
|
7
7
|
version: "1.0"
|
|
@@ -194,6 +194,7 @@ const App = () => {
|
|
|
194
194
|
| 2026-08-05 | Создан документ. AI-рефакторинг: JSDoc на props и экспортах, ключ тега стилей вынесен в хелпер, упрощён эффект `ThemeProviderView`, добавлены unit-тесты `ThemeProviderView` и `useToken`, stories переведены на modern pattern |
|
|
195
195
|
| 2026-08-05 | `related` сокращён до `useToken`. Удалён неактуальный комментарий про относительный импорт в `ThemeProviderView` |
|
|
196
196
|
| 2026-08-31 | Добавлен раздел «Как переопределять токены»: путь `{Группа}.{Токен}`, плоская форма prop `tokens`, `value` / `ref` |
|
|
197
|
+
| 2026-09-29 | Аудит симметрии `related` (TRI-156): из `related` убраны имена без строки в `docs/ai/ROADMAP.md` (`useToken`). Пояснения к убранным именам сохранены прозой в «Связанные компоненты»; публичный API и поведение не затронуты. |
|
|
197
198
|
|
|
198
199
|
---
|
|
199
200
|
|