@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.
Files changed (77) hide show
  1. package/bundle/components/Amount.md +2 -1
  2. package/bundle/components/Badge.md +2 -1
  3. package/bundle/components/BodyPage.md +2 -1
  4. package/bundle/components/Button.md +5 -1
  5. package/bundle/components/ButtonDropdownExtended.md +3 -1
  6. package/bundle/components/ButtonIcon.md +3 -1
  7. package/bundle/components/Calendar.md +3 -1
  8. package/bundle/components/CardAction.md +1 -0
  9. package/bundle/components/CheckboxXGroup.md +2 -1
  10. package/bundle/components/Chip.md +2 -1
  11. package/bundle/components/ChipMonthYearPicker.md +2 -1
  12. package/bundle/components/ChipMultiselect.md +4 -1
  13. package/bundle/components/ChipOptions.md +2 -1
  14. package/bundle/components/ChipSort.md +4 -1
  15. package/bundle/components/CollapsibleTree.md +2 -1
  16. package/bundle/components/ComposedKeyDownListener.md +4 -3
  17. package/bundle/components/DateField.md +7 -1
  18. package/bundle/components/Divider.md +85 -138
  19. package/bundle/components/DocumentNumberEdit.md +173 -214
  20. package/bundle/components/Dropdown.md +11 -1
  21. package/bundle/components/Ellipsis.md +131 -147
  22. package/bundle/components/FooterPage.md +3 -1
  23. package/bundle/components/FormField.md +2 -1
  24. package/bundle/components/Gap.md +1 -1
  25. package/bundle/components/Header.md +1 -0
  26. package/bundle/components/HeaderPage.md +3 -1
  27. package/bundle/components/HelpBox.md +2 -1
  28. package/bundle/components/IconWrapper.md +10 -6
  29. package/bundle/components/ImageGallery.md +2 -1
  30. package/bundle/components/ImageGalleryExtended.md +2 -1
  31. package/bundle/components/IslandAccordion.md +6 -1
  32. package/bundle/components/KeyDownListener.md +3 -2
  33. package/bundle/components/LightBox.md +7 -1
  34. package/bundle/components/List.md +2 -1
  35. package/bundle/components/ListActionItem.md +2 -1
  36. package/bundle/components/ListItem.md +3 -1
  37. package/bundle/components/ListItemControlsButton.md +2 -1
  38. package/bundle/components/ListItemSelectable.md +2 -1
  39. package/bundle/components/ListMasterFooter.md +2 -1
  40. package/bundle/components/ListMasterHeader.md +2 -1
  41. package/bundle/components/ListSortable.md +2 -1
  42. package/bundle/components/ListSortableItem.md +2 -1
  43. package/bundle/components/ListTableItem.md +4 -1
  44. package/bundle/components/LoaderMiddle.md +5 -1
  45. package/bundle/components/LoaderScreen.md +7 -1
  46. package/bundle/components/LoaderSmall.md +10 -4
  47. package/bundle/components/MaskedField.md +3 -1
  48. package/bundle/components/MediaWidth.md +2 -1
  49. package/bundle/components/MobileView.md +2 -1
  50. package/bundle/components/ModalWindow.md +4 -1
  51. package/bundle/components/MonthYearField.md +4 -1
  52. package/bundle/components/Notification.md +1 -0
  53. package/bundle/components/Overlay.md +7 -2
  54. package/bundle/components/Pagination.md +1 -7
  55. package/bundle/components/Radio.md +238 -115
  56. package/bundle/components/RadioXGroup.md +259 -0
  57. package/bundle/components/RadioYGroup.md +266 -0
  58. package/bundle/components/SegmentedControl.md +505 -158
  59. package/bundle/components/SelectExtendedField.md +2 -1
  60. package/bundle/components/SelectField.md +4 -1
  61. package/bundle/components/Skeleton.md +140 -69
  62. package/bundle/components/Spoiler.md +245 -63
  63. package/bundle/components/Step.md +257 -68
  64. package/bundle/components/SuggestField.md +30 -18
  65. package/bundle/components/Tabs.md +2 -1
  66. package/bundle/components/TabsLine.md +2 -1
  67. package/bundle/components/TextField.md +8 -4
  68. package/bundle/components/TextareaField.md +2 -1
  69. package/bundle/components/ThemeProvider.md +2 -1
  70. package/bundle/components/Tooltip.md +6 -3
  71. package/bundle/guides/context.md +4 -1
  72. package/bundle/manifest.json +32 -10
  73. package/bundle/release-notes/1.49.0.mdx +78 -0
  74. package/bundle/release-notes/1.50.0.mdx +35 -0
  75. package/bundle/release-notes/1.51.0.mdx +7 -0
  76. package/bundle/tokens.json +161 -11
  77. package/package.json +1 -1
@@ -1,9 +1,7 @@
1
1
  ---
2
2
  component: DocumentNumberEdit
3
- category: Form
4
- figma-node: ""
5
- figma-file: ""
6
- related: [SmallInput, Link, Typography]
3
+ category: TextFields
4
+ related: [SmallInput, Link, Text]
7
5
  tokens: []
8
6
  stories: stories/DocumentNumberEdit/DocumentNumberEdit.stories.tsx
9
7
  version: "1.0"
@@ -13,263 +11,194 @@ version: "1.0"
13
11
 
14
12
  ## Назначение
15
13
 
16
- Инлайн-виджет "номер документа" в режиме **просмотра** (подпись + текст)
17
- / **редактирования** (SmallInput c числовой маской). Используется
18
- типично в формах создания документов, где номер по умолчанию
19
- присваивается автоматически, но пользователь может его изменить.
14
+ Инлайн-редактирование номера документа: строка с подписью («Документ № 123456» или текст об
15
+ автоматическом присвоении) и ссылка-переключатель рядом. По клику по ссылке подпись сжимается до
16
+ `numberLabel`, а на её месте появляется `SmallInput` с автофокусом, который принимает только цифры.
17
+ Редактирование завершается по Enter или по потере фокуса. Значение компонент не хранит: он
18
+ контролируемый — `value` и `onChange` ведёт потребитель.
20
19
 
21
- Три состояния:
22
- 1. **Нет значения + не в режиме edit** — текст `emptyNumberLabel` +
23
- ссылка `emptyNumberButtonLabel` ("Задать номер").
24
- 2. **Есть значение + не в режиме edit** — текст `{numberLabel} {value}`
25
- + ссылка `buttonLabel` ("Изменить").
26
- 3. **В режиме edit** — текст `numberLabel` + SmallInput (цифры, ≤6
27
- символов).
20
+ Используй когда: в шапке документа или в плотной вёрстке нужно показать номер и дать его
21
+ поправить на месте, без открытия формы и без модального окна.
28
22
 
29
- Используй когда: форма документа с редактируемым номером и placeholder-логикой.
30
- Не используй для: произвольного числового поля — бери `SmallInput` или
31
- `NumberField`.
23
+ Не используй когда:
32
24
 
33
- ---
34
-
35
- ## Файловая структура
36
-
37
- ```text
38
- src/components/DocumentNumberEdit/
39
- ├── DocumentNumberEdit.tsx # FC, НЕ forwardRef
40
- ├── index.ts # Barrel
41
- └── styles/DocumentNumberEdit.module.less # .label (inline-block + 8px right), .inputEditWrapper (68px)
42
- ```
43
-
44
- Особенности:
45
- - **НЕ `forwardRef`** — FC. Известный AI-ready debt.
46
- - **Внутренний state `editingMode`** — компонент сам управляет
47
- переключением "просмотр ↔ редактирование". Внешнего управления нет.
48
- - **`value`** — controlled-only (без defaultValue).
49
- - **Нет `__tests__`** — покрытие через `SmallInput`-тесты косвенно.
50
- - **Нет `data-tx`** — не ставит атрибут версии.
25
+ - Поле живёт в форме и ему нужны лейбл, статус (error/warning), описание или счётчик — возьми
26
+ `TextField`.
27
+ - Нужен просто компактный `<input>` без подписи и переключения режимов — возьми `SmallInput`,
28
+ на котором `DocumentNumberEdit` и построен.
29
+ - Номер длиннее 6 символов или содержит не только цифры (серия, префикс, разделители):
30
+ `maxLength` ограничен литералами `0…6`, а нецифровые символы компонент вырезает молча.
31
+ - Нужно управлять режимом редактирования снаружи (открыть программно, запретить выход,
32
+ показать кнопки «Сохранить»/«Отмена»): режим — внутренний state без props и колбэков.
51
33
 
52
34
  ---
53
35
 
54
36
  ## Варианты и props
55
37
 
56
- ### `IDocumentNumberEditProps extends ISmallInputProps`
57
-
58
- **DocumentNumberEdit-specific:**
59
-
60
- | Prop | Тип | Default | Описание |
61
- |---|---|---|---|
62
- | `value` | `React.ReactText` (`string \| number`) | — | Текущий номер. Falsy → empty-state. |
63
- | `numberLabel` | `string` | — (обязателен) | Префикс текста в view-mode ("Документ №"). |
64
- | `emptyNumberLabel` | `string` | — (обязателен) | Полный текст в empty-state ("Номер документа будет присвоен автоматически"). |
65
- | `buttonLabel` | `string` | — (обязателен) | Текст ссылки в view-mode ("Изменить"). |
66
- | `emptyNumberButtonLabel` | `string` | — (обязателен) | Текст ссылки в empty-state ("Задать номер"). |
67
- | `maxLength` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6` | `6` | Максимум цифр. Влияет на `placeholder` (`"0"*maxLength`) и `SmallInput.maxLength`. |
68
-
69
- **Остальные:** все пропсы `ISmallInputProps` (включая `onChange`,
70
- `onBlur`, `onKeyDown`, `className`, `...`).
71
-
72
- ### `labelText` — вычисляемый
73
-
74
- ```ts
75
- const labelText = useMemo(() => {
76
- if (editingMode) return numberLabel; // только "Документ №"
77
- if (value) return `${numberLabel} ${value}`; // "Документ № 12345"
78
- return emptyNumberLabel; // "Номер ... будет присвоен автоматически"
79
- }, [value, numberLabel, emptyNumberLabel, editingMode]);
80
- ```
81
-
82
- ---
83
-
84
- ## Ключевые особенности реализации
85
-
86
- ### State `editingMode` — внутренний uncontrolled switch
87
-
88
- ```ts
89
- const [editingMode, setEditingMode] = useState(false);
90
- ```
91
-
92
- **Включается:** клик по ссылке (`handleButtonClick`, `preventDefault()`).
93
- **Выключается:** Enter в input (`handleInputKeyDown`) или blur input
94
- (`handleInputBlur`).
95
-
96
- **Нет `controlled` варианта** — потребитель не может извне открыть
97
- редактирование. Если нужно — оборачивать в свою логику.
98
-
99
- ### Фильтрация цифр + сохранение позиции курсора
100
-
101
- ```ts
102
- const handleInputChange = (event) => {
103
- const caret = getCaretPosition(event.target);
104
- const value = getFilteredValue(event.target.value);
105
- const caretShift = value.length - event.target.value.length;
106
-
107
- event.target.value = value;
108
- setCaretPosition(event.target, caret + caretShift);
109
-
110
- onChange?.(event);
111
- };
112
-
113
- const getFilteredValue = (value: string) => {
114
- const buffer: string[] = [];
115
- for (let i = 0; i < value.length; i++) {
116
- if (StringUtils.isDigit(value[i])) buffer.push(value[i]);
117
- }
118
- return buffer.join("");
119
- };
120
- ```
121
-
122
- **Зачем `caretShift`:** если пользователь вставил "12a3" → фильтр
123
- вернёт "123", длина уменьшилась на 1. Курсор должен сдвинуться на `-1`
124
- относительно момента вставки, чтобы остаться на правильном месте.
125
- Без этого курсор "прыгнет" в конец.
126
-
127
- **`StringUtils.isDigit`** — проверка по регулярке (0-9). Не
128
- поддерживает юникод-цифры, только ASCII.
129
-
130
- **Мутация `event.target.value` напрямую** — это анти-паттерн, но
131
- React принимает это, так как мы работаем с uncontrolled SmallInput
132
- под капотом (controlled через onChange). Вариант: принять внешний
133
- onChange(filteredValue), но тогда `value` не обновится моментально
134
- до ре-рендера.
38
+ `IDocumentNumberEditProps` наследует `ISmallInputProps` (то есть
39
+ `React.InputHTMLAttributes<HTMLInputElement>`), поэтому принимает все атрибуты `<input>`.
135
40
 
136
- ### `autoFocus={true}` на SmallInput
41
+ ### Обязательные props
137
42
 
138
- При переключении в edit-mode input появляется **с фокусом**. Важно
139
- для UX — пользователь кликнул "Изменить" и сразу может вводить.
43
+ | Prop | Тип | Описание |
44
+ |---|---|---|
45
+ | `numberLabel` | `string` | Текст перед номером («Документ №»). В режиме просмотра склеивается с `value` через пробел, в режиме редактирования остаётся один |
46
+ | `buttonLabel` | `string` | Текст ссылки-переключателя, когда номер задан («Изменить») |
47
+ | `emptyNumberButtonLabel` | `string` | Текст ссылки-переключателя, когда номера нет («Задать номер») |
48
+ | `emptyNumberLabel` | `string` | Текст вместо подписи с номером, когда номера нет («Номер документа будет присвоен автоматически») |
140
49
 
141
- ### Ссылка — `Link` с `href="#"` + preventDefault
50
+ Все четыре текста обязательны и **дефолтов не имеют** — библиотека мультиязычная, строки в неё
51
+ не зашиты. Значения в скобках — примеры из stories, а не значения по умолчанию.
142
52
 
143
- ```tsx
144
- <Link href="#" onClick={handleButtonClick}>
145
- {value ? buttonLabel : emptyNumberButtonLabel}
146
- </Link>
147
- ```
148
-
149
- **`href="#"`** — чтобы ссылка была "настоящей" (правильный фокус,
150
- подчёркивание). `preventDefault` в обработчике клика предотвращает
151
- прыжок страницы наверх.
152
-
153
- **Альтернатива — `<button>`**, но дизайн требует ссылочного вида.
154
- Компромисс: семантика button + визуал Link.
53
+ ### Опциональные props
155
54
 
156
- ### Выход из edit-mode: Enter или blur
157
-
158
- ```ts
159
- if (isKey(event.code, "ENTER")) setEditingMode(false);
160
- // + onBlur → setEditingMode(false)
161
- ```
55
+ | Prop | Тип | По умолчанию | Описание |
56
+ |---|---|---|---|
57
+ | `value` | `React.ReactText` (`string \| number`) | — | Номер документа. Тип сужен относительно `<input>`: `readonly string[]` не принимается |
58
+ | `maxLength` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6` | `6` | Максимальная длина ввода. Из неё же строится плейсхолдер — маска из нулей той же длины (`6` → `000000`) |
59
+ | `className` | `string` | — | Мерджится через `clsx` на корневой `<div>` |
60
+ | `onChange` | `React.ChangeEventHandler<HTMLInputElement>` | — | Вызывается уже после фильтрации: в `event.target.value` лежат только цифры |
61
+ | `onKeyDown` | `React.KeyboardEventHandler<HTMLInputElement>` | — | Вызывается после внутренней обработки Enter |
62
+ | `onBlur` | `React.FocusEventHandler<HTMLInputElement>` | — | Вызывается после выхода из режима редактирования |
63
+ | `...InputHTMLAttributes` | — | — | Уходят на `SmallInput` через `...rest` — **только в режиме редактирования** |
162
64
 
163
- **Esc не выходит** — это UX-решение. Если нужно — потребитель может
164
- сам обработать в `onKeyDown` и програмно вызвать blur.
65
+ ### Что считается «номер задан»
165
66
 
166
- ### Placeholder — `"0".repeat(maxLength)`
67
+ Проверка — на правдивость `value`, а не на `undefined`: при `value={0}`, `value=""` и
68
+ `value={undefined}` компонент одинаково показывает `emptyNumberLabel` и
69
+ `emptyNumberButtonLabel`, а в поле ввода подставляет пустую строку. Числовой ноль как номер
70
+ документа не поддерживается.
167
71
 
168
- `inputPlaceholder = "0".repeat(maxLength)` → "000000" для maxLength=6.
169
- Визуально показывает сколько символов ожидается. Меняется с maxLength.
72
+ ### Фильтрация и курсор
170
73
 
171
- ### Ширина input — 68px (hardcoded)
74
+ `onChange` перехватывается: из значения вырезаются все нецифровые символы
75
+ (`StringUtils.isDigit`), отфильтрованное значение записывается прямо в `event.target.value`, а
76
+ курсор сдвигается на разницу длин — чтобы вставка `12ab34` в середину не уводила курсор в конец.
77
+ Потребитель получает уже очищенное значение, и любая своя валидация работает поверх цифр.
172
78
 
173
- ```less
174
- .inputEditWrapper { width: 68px; }
175
- ```
79
+ ### Контролируемость
176
80
 
177
- **Hardcoded** и рассчитана под 6 цифр в SmallInput. Если изменить
178
- `maxLength`, input может не помещать значение — 68px могут быть
179
- слишком широки или узки. **Потребители с кастомным maxLength ≠ 6
180
- должны переопределять ширину через className/style на wrapper**
181
- (но wrapper — внутренний, override невозможен без хака).
81
+ В поле ввода всегда уходит `value || ""`, то есть `<input>` контролируемый. Если потребитель не
82
+ обновляет `value` в `onChange`, ввод визуально не применяется. Остальные props для этого сценария
83
+ (`defaultValue`, `readOnly`) не запрещены, но противоречат схеме контролируемого поля.
182
84
 
183
- **Это известное ограничение**: компонент заточен под 6-значный номер.
85
+ ### Режим редактирования
184
86
 
185
- ### `inline-block` разметка
87
+ Режим — внутренний `useState`, наружу не выведен: нет ни prop'а, ни колбэка на вход/выход. Вход —
88
+ только клик по ссылке, выход — Enter (по `event.code`) или потеря фокуса. Escape не обрабатывается,
89
+ отката значения нет: всё, что потребитель успел записать в свой state, остаётся. `...rest`
90
+ доходит только до поля ввода, поэтому `disabled` ссылку-переключатель не выключает: вход в режим
91
+ редактирования остаётся доступен, а выйти из него уже нельзя — disabled-поле не получает фокус
92
+ (`autoFocus` становится no-op), поэтому ни `blur`, ни Enter не срабатывают, ссылка в этом режиме
93
+ не рендерится, и номер остаётся скрытым. Передавать `disabled` этому компоненту бессмысленно:
94
+ запрета редактирования он не даёт, а даёт тупиковое состояние.
186
95
 
187
- ```less
188
- .label { display: inline-block; margin-right: 8px; }
189
- .inputEditWrapper { width: 68px; display: inline-block; }
190
- ```
96
+ ---
191
97
 
192
- Label и кнопка/input стоят **в одну строку** (inline-block). Без
193
- grid/flex. Это требование дизайна — "в одну строку".
98
+ ## Дизайн-токены
194
99
 
195
- ### Рендер: всегда label + один из (renderButton / renderInput)
100
+ Собственных токенов у компонента нет: `styles/DocumentNumberEdit.module.less` задаёт только
101
+ раскладку — `display: inline-block` и `margin-right: 8px` на классе `.label` (он навешан и на
102
+ подпись, и на обёртку ссылки, поэтому правка отступа затронет оба элемента),
103
+ `display: inline-block` и `width: 68px` на обёртке поля (под шесть цифр). Самому корневому `<div>` стили ничего не
104
+ выставляют. Цвета и типографика приходят из вложенных компонентов и переопределяются через их
105
+ группы токенов (`ThemeProvider`, prop `tokens` — см. `ThemeProvider-ai.md`):
196
106
 
197
- ```tsx
198
- <div className={styles.documentNumberEdit}>
199
- <Text className={styles.label}>{labelText}</Text>
200
- {editingMode ? renderInput() : renderButton()}
201
- </div>
107
+ ```text
108
+ SmallInput.Background
109
+ SmallInput.Color
110
+ SmallInput.PlaceholderColor
111
+ SmallInput.Shadow
112
+ Link.Text_Color_Default
113
+ Link.Text_Color_Hover
114
+ Link.Text_Color_Active
115
+ Link.BorderColor_Focus
202
116
  ```
203
117
 
204
- **Без анимации перехода** — моментальная замена.
118
+ Подпись и обёртку ссылки рендерит `Text` (`ETextSize.B3`, `EFontType.SECONDARY`): размер и цвет
119
+ подписи — из токенов типографики. Цвет самого текста ссылки задаёт `Link` своими токенами
120
+ (`Link.Text_Color_*` стоят на `.link` и перебивают унаследованный от `Text` цвет), поэтому
121
+ перекрашивать переключатель нужно через группу `Link`, а не через типографику.
205
122
 
206
123
  ---
207
124
 
208
125
  ## Инварианты
209
126
 
210
- - **`editingMode` — внутренний state**. Нет controlled-варианта.
211
- - **`value` — controlled only** (через проп). Нет defaultValue.
212
- - **`maxLength: 0..6`** — жёсткое ограничение в типе. Расширять только
213
- с обновлением `.inputEditWrapper.width` в LESS.
214
- - **Фильтрация только ASCII-цифр** через `StringUtils.isDigit`.
215
- - **`caretShift` в `handleInputChange`** — не удалять. Сохраняет
216
- позицию курсора при фильтрации.
217
- - **Ссылка `Link href="#" + preventDefault`** — семантичная кнопка
218
- визуально, без button-стилей. Не заменять на button без
219
- дизайн-согласования.
220
- - **Enter и blur выходят из edit-mode**, Esc — нет. Не менять.
221
- - **Placeholder = "0".repeat(maxLength)** — генерируется из maxLength.
222
- - **`autoFocus=true`** на SmallInput — обязателен для UX.
223
- - **`.inputEditWrapper` 68px** — hardcoded под 6 цифр.
224
- - **НЕТ forwardRef** — известный debt.
225
- - **НЕТ `data-tx`** — наследованный пропуск.
127
+ - **`forwardRef<HTMLDivElement>`** — не убирать. `ref` указывает на корневой
128
+ `<div class="documentNumberEdit">`, **не** на поле ввода: поле существует только в режиме
129
+ редактирования. Доступ к `<input>` по ref не предусмотрен.
130
+ - **Корневой элемент — `<div>`** с двумя детьми: подпись (`Text`) и либо обёртка поля
131
+ (`div.inputEditWrapper` → `SmallInput`), либо ссылка (`Text` → `Link`). Сам корень — обычный
132
+ блок и в строку текста не встраивается; в строку выстроены его дети (оба `inline-block`).
133
+ Смена тега сломает эту вёрстку и ref-контракт (`ref` типизирован как `HTMLDivElement`).
134
+ - **`displayName`** — `"DocumentNumberEdit"`. Не менять.
135
+ - **Имена `DocumentNumberEdit`, `IDocumentNumberEditProps`** и все четыре текстовых props —
136
+ публичный API, экспортируются из barrel `src/components/DocumentNumberEdit/index.ts`.
137
+ - **Обязательность `buttonLabel`, `emptyNumberButtonLabel`, `emptyNumberLabel`, `numberLabel`** —
138
+ часть публичного контракта. Сделать их опциональными с дефолтными русскими строками нельзя:
139
+ это и изменение API, и хардкод языка в мультиязычной библиотеке.
140
+ - **Тип `maxLength` — литеральный union `0…6`**, сужение относительно `number` у `<input>`.
141
+ Расширение или сужение диапазона — ломающее изменение.
142
+ - **Фильтрация до цифр и перезапись `event.target.value` до вызова `onChange`** — наблюдаемое
143
+ поведение, на него опираются потребители. Порядок «отфильтровать → выставить курсор →
144
+ вызвать `onChange`» менять нельзя.
145
+ - **Плейсхолдер выводится из `maxLength`** (`"0".repeat(maxLength)`), отдельного prop'а нет.
146
+ - **`autoFocus` на поле ввода** — обязателен: без него переход в режим редактирования требовал бы
147
+ второго клика.
148
+ - **Ширина `inputEditWrapper` 68px** рассчитана на шесть цифр шрифтом B3. При меньшем `maxLength`
149
+ поле не сжимается.
226
150
 
227
151
  ---
228
152
 
229
153
  ## Accessibility
230
154
 
231
- - **Link с href="#"** — семантически ссылка, обнаруживается screen
232
- reader'ом, фокусируется Tab'ом. Минус — href="#" может быть
233
- нежелательным для SEO (не актуально для form-виджетов).
234
- - **Input** — SmallInput со своими ARIA (`aria-label` если передан
235
- через `...rest`).
236
- - **Transition между view/edit** — визуальный, не объявляется screen
237
- reader'у отдельно. Пользователь видит / слышит новую разметку.
238
- - **Нет live-region** — если input появился асинхронно (а не по
239
- клику), screen reader может пропустить. В текущем сценарии это
240
- реакция на клик, поэтому не критично.
241
- - **Placeholder "000000"** — может сбить с толку, если пользователь
242
- читает screen reader'ом. Лучше полагаться на `aria-label` через
243
- rest.
244
-
245
- ---
246
-
247
- ## Дизайн-токены
248
-
249
- **Нет собственных.** Все стили от `SmallInput`, `Link`, `Typography.Text`.
155
+ - Переключатель режима — `Link` (`<a href="#">` с `preventDefault`), то есть скринридер
156
+ объявляет его ссылкой, а не кнопкой. Заменить роль снаружи нельзя: props до `Link` не доходят.
157
+ С клавиатуры ссылка доступна (Tab + Enter), но это остаётся ссылкой-заглушкой.
158
+ - **Доступное имя поля ввода обязан задать потребитель** — `aria-label` или `aria-labelledby`
159
+ уходят на `SmallInput` через `...rest`. Видимая подпись «Документ №» с полем программно не
160
+ связана: `<label htmlFor>` компонент не рендерит и `id` не генерирует.
161
+ - Фокус при входе в режим редактирования уходит в поле (`autoFocus`). При выходе по Enter поле
162
+ размонтируется, и фокус **не возвращается** на ссылку — он оказывается на `<body>`. Если
163
+ компонент стоит в клавиатурном сценарии, верни фокус сам или ориентируй пользователя на выход
164
+ по Tab.
165
+ - Escape не обрабатывается: отменить правку клавиатурой нельзя, выход по Escape не предусмотрен.
166
+ - Смена режима ничего не анонсирует: `aria-live` области нет, изменение подписи скринридер сам
167
+ не озвучит.
168
+ - Компонент не хардкодит текстовые строки: все подписи и любые `aria-*` приходят из props.
250
169
 
251
170
  ---
252
171
 
253
172
  ## Связанные компоненты
254
173
 
255
- - `SmallInput` (`src/components/SmallInput`) — input в edit-mode.
256
- - `Link` (`src/components/Link`) — кнопка-ссылка.
257
- - `Typography.Text` — label перед номером.
258
- - `NumberField` — альтернатива для более "обычных" числовых полей
259
- (без specific "edit / view" UX).
174
+ - `SmallInput` (`src/components/SmallInput/SmallInput.tsx`) — поле ввода, которое рендерится в
175
+ режиме редактирования, и источник типа props: `IDocumentNumberEditProps extends ISmallInputProps`.
176
+ `DocumentNumberEdit` фиксирует у него `value`, `placeholder`, `maxLength`, `autoFocus` и все три
177
+ обработчика — переопределить их через `...rest` нельзя, они стоят после спреда.
178
+ - `Link` (`src/components/Link/Link.tsx`) — ссылка-переключатель режима. `DocumentNumberEdit`
179
+ передаёт ей только `href="#"` и свой `onClick`; настроить её из props потребителя нельзя.
180
+ - `Text` (`src/components/Typography/Text.tsx`) — рендерит и подпись, и обёртку ссылки
181
+ (`tag="div"`, `ETextSize.B3`, `EFontType.SECONDARY`). Типографику компонента менять можно только
182
+ здесь.
183
+ - `TextField` — альтернатива для полноформатного поля в форме (лейбл, статусы, описание,
184
+ счётчик), когда инлайн-редактирования недостаточно.
260
185
 
261
186
  ---
262
187
 
263
188
  ## Stories
264
189
 
265
- `stories/DocumentNumberEdit/DocumentNumberEdit.stories.tsx`
266
- Примеры: `stories/DocumentNumberEdit/examples/`
190
+ Основные истории: `stories/DocumentNumberEdit/DocumentNumberEdit.stories.tsx`
191
+ Файлы примеров: `stories/DocumentNumberEdit/examples/`
192
+
193
+ | Story | Example file | Что демонстрирует |
194
+ |---|---|---|
195
+ | `Playground` | `PlaygroundExample.tsx` | Интерактивный контроль всех четырёх текстов и `maxLength` на контролируемом значении |
196
+ | `Default` | `DefaultExample.tsx` | Минимальное использование: контролируемое значение в state потребителя |
197
+ | `VisualTests` | `VisualTestsExample.tsx` | Скриншот-регрессия: пустой номер и заполненный номер, у первого `play` открывает режим редактирования |
267
198
 
268
- Типовые stories:
269
- - `Default` — с числом
270
- - `Empty` — без числа (empty-state UI)
271
- - `MaxLengths` — разные длины
272
- - `Playground` — интерактив
199
+ Story ID участвуют в именах baseline-скриншотов (`documentnumberedit--default--xs.png`,
200
+ `documentnumberedit--visual-tests--xl.png`), поэтому переименование story требует перегенерации
201
+ baseline и удаления осиротевших файлов.
273
202
 
274
203
  ---
275
204
 
@@ -277,4 +206,34 @@ grid/flex. Это требование дизайна — "в одну стро
277
206
 
278
207
  | Дата | Изменение |
279
208
  |---|---|
280
- | 2026-04-18 | Создан документ |
209
+ | 2026-09-29 | Создан документ (TRI-34). AI-рефакторинг: компонент переведён с `React.FC` на `forwardRef` (`ref` идёт на корневой `<div>`), фильтрация цифр вынесена в модульную функцию, `renderInput`/`renderButton` заменены на JSX в `return`, снят лишний `useMemo` с подписи, JSDoc на props перестал обещать несуществующие дефолты. Добавлены unit-тесты (`__tests__/DocumentNumberEdit.test.tsx`). Публичный API не изменён |
210
+
211
+ ---
212
+
213
+ ## Примеры использования
214
+
215
+ ### Default
216
+
217
+ `stories/DocumentNumberEdit/examples/DefaultExample.tsx`
218
+
219
+ ```tsx
220
+ import React, { useState } from "react";
221
+ import { DocumentNumberEdit } from "@sberbusiness/triplex-next";
222
+
223
+ export const DefaultExample = () => {
224
+ const [value, setValue] = useState("");
225
+ const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => setValue(event.target.value);
226
+
227
+ return (
228
+ <DocumentNumberEdit
229
+ value={value}
230
+ buttonLabel="Изменить"
231
+ emptyNumberButtonLabel="Задать номер"
232
+ emptyNumberLabel="Номер документа будет присвоен автоматически"
233
+ numberLabel="Документ №"
234
+ onChange={handleChange}
235
+ />
236
+ );
237
+ };
238
+ ```
239
+
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  component: Dropdown
3
3
  category: Dropdown
4
- related: [ButtonDropdown, ButtonDropdownExtended, Portal, MobileView, OverlayBase, SelectField, SelectExtendedField, SuggestField, MultiselectField, TabsLine]
4
+ related: [ButtonDropdown, ButtonDropdownExtended, Portal, MobileView, LoaderSmall]
5
5
  tokens:
6
6
  - Dropdown.Background
7
7
  - Dropdown.Shadow
@@ -181,6 +181,14 @@ DropdownMobileList.Selected_Background
181
181
  - `Portal`, `MobileView`, `OverlayBase` — инфраструктура рендера.
182
182
  - `ButtonDropdown`, `ButtonDropdownExtended` — готовые кнопки с выпадающим блоком поверх `Dropdown`.
183
183
 
184
+ **Потребители внутри библиотеки** (в `related` не входят — по `docs/ai/CONTEXT.md` ссылка
185
+ стоит с их стороны, и она там уже есть):
186
+
187
+ - `SelectExtendedField` — база полей выбора: владеет состоянием открытости и рендерит `Dropdown`.
188
+ - `SelectField`, `MultiselectField` — поля выбора одного и нескольких значений, построены на `SelectExtendedField`.
189
+ - `SuggestField` — поле с поиском по вводу; собрано на `Dropdown` напрямую, а не на `SelectExtendedField`.
190
+ - `TabsLine` — прячет не поместившиеся вкладки в `Dropdown`.
191
+
184
192
  ---
185
193
 
186
194
  ## Stories
@@ -210,6 +218,8 @@ DropdownMobileList.Selected_Background
210
218
  | 2026-07-31 | Создан документ AI-ready для `Dropdown` (TRI-35). |
211
219
  | 2026-07-31 | AI-рефакторинг: `DropdownList` больше не создаёт ref через `React.createRef()` в рендере (из-за этого автопрокрутка к активному элементу не работала), сброс активного элемента при открытии переведён на корректировку state в рендере вместо `setState` в эффекте, прокрутка вынесена в `desktop/utils.ts`, в `DropdownListItem` починен устаревший обработчик клавиатуры, в `Dropdown` актуальные `onOpen` / `onClose` хранятся в ref. Публичный API не изменён. Добавлены unit-тесты (109 кейсов) и stories. |
212
220
  | 2026-07-31 | Правки по ревью PR #491: эффект `onOpen` / `onClose` в `Dropdown` сравнивает предыдущее значение `opened` вместо флага «смонтирован» — колбэки больше не срабатывают ложно при двойном маунте в React 18 StrictMode (регрессионный тест `__tests__/Dropdown.strictMode.test.tsx`). Документационные stories стартуют закрытыми и раскрываются через `play`. Сброс активного элемента `DropdownList` сделан симметричным: при закрытии подсветка снимается вместе с `activeDescendant`. |
221
+ | 2026-09-28 | Токен `Dropdown.Shadow` ссылается на `Shadow.07` (светлая) и `DarkShadow.07` (тёмная) вместо литералов; в тёмной теме прозрачность 0.25 вместо 0.35. |
222
+ | 2026-09-29 | Аудит симметрии `related` (TRI-156): из `related` убраны имена без строки в `docs/ai/ROADMAP.md` (`OverlayBase`) и потребители `SelectField` / `SelectExtendedField` / `SuggestField` / `MultiselectField` / `TabsLine` — по `docs/ai/CONTEXT.md` ссылка стоит с их стороны; добавлен `LoaderSmall` — контракт по рендеру в верную сторону. Пояснения к убранным именам сохранены прозой в «Связанные компоненты»; публичный API и поведение не затронуты. |
213
223
 
214
224
  ---
215
225