@sberbusiness/triplex-next-mcp-server 1.50.0-beta → 1.53.1

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 (74) hide show
  1. package/README.md +1 -0
  2. package/bundle/components/AlertProcess.md +6 -3
  3. package/bundle/components/AlertProcessSpoiler.md +216 -0
  4. package/bundle/components/Badge.md +4 -3
  5. package/bundle/components/ButtonDropdown.md +5 -3
  6. package/bundle/components/Carousel.md +671 -0
  7. package/bundle/components/CarouselExtended.md +7 -5
  8. package/bundle/components/CarouselIndicators.md +666 -0
  9. package/bundle/components/CarouselItem.md +579 -0
  10. package/bundle/components/CarouselNextButton.md +633 -0
  11. package/bundle/components/CarouselPrevButton.md +633 -0
  12. package/bundle/components/CarouselViewport.md +631 -0
  13. package/bundle/components/CheckboxTreeExtended.md +21 -7
  14. package/bundle/components/CheckboxTreeExtendedCheckbox.md +591 -0
  15. package/bundle/components/CheckboxTreeExtendedNode.md +577 -0
  16. package/bundle/components/Chip.md +1 -0
  17. package/bundle/components/ChipDatePicker.md +332 -0
  18. package/bundle/components/ChipDatePickerTarget.md +316 -0
  19. package/bundle/components/ChipMonthYearPicker.md +4 -3
  20. package/bundle/components/ChipMonthYearPickerTarget.md +318 -0
  21. package/bundle/components/ChipMultiselect.md +63 -73
  22. package/bundle/components/ChipSelect.md +385 -0
  23. package/bundle/components/ChipSelectTarget.md +388 -0
  24. package/bundle/components/ChipSuggest.md +10 -16
  25. package/bundle/components/ChipSuggestTarget.md +506 -0
  26. package/bundle/components/CollapsibleTree.md +2 -1
  27. package/bundle/components/CollapsibleTreeExtended.md +2 -1
  28. package/bundle/components/DateField.md +4 -2
  29. package/bundle/components/DatePickerExtended.md +16 -14
  30. package/bundle/components/DateRange.md +17 -10
  31. package/bundle/components/Dropdown.md +36 -63
  32. package/bundle/components/EmptyView.md +9 -4
  33. package/bundle/components/ExpandAnimation.md +9 -5
  34. package/bundle/components/FormField.md +1 -1
  35. package/bundle/components/ImageGallery.md +19 -52
  36. package/bundle/components/ImageGalleryExtended.md +123 -139
  37. package/bundle/components/ListItemControlsButtonDropdown.md +3 -2
  38. package/bundle/components/LoaderSmall.md +5 -4
  39. package/bundle/components/MonthYearRange.md +334 -0
  40. package/bundle/components/MultiselectField.md +81 -143
  41. package/bundle/components/Notification.md +2 -1
  42. package/bundle/components/NotificationGrouped.md +174 -0
  43. package/bundle/components/NumberField.md +259 -116
  44. package/bundle/components/OrderedList.md +149 -112
  45. package/bundle/components/Overlay.md +3 -2
  46. package/bundle/components/PageIndicators.md +306 -0
  47. package/bundle/components/Pagination.md +81 -3
  48. package/bundle/components/PaginationExtended.md +160 -0
  49. package/bundle/components/Portal.md +5 -2
  50. package/bundle/components/Puller.md +375 -0
  51. package/bundle/components/SMSField.md +277 -102
  52. package/bundle/components/SelectExtendedField.md +11 -7
  53. package/bundle/components/SelectField.md +7 -4
  54. package/bundle/components/SmartSpot.md +259 -0
  55. package/bundle/components/Step.md +2 -1
  56. package/bundle/components/Stepper.md +7 -7
  57. package/bundle/components/Suggest.md +7 -6
  58. package/bundle/components/SuggestField.md +8 -6
  59. package/bundle/components/TableBasic.md +12 -14
  60. package/bundle/components/Tooltip.md +9 -8
  61. package/bundle/guides/commits.md +4 -1
  62. package/bundle/guides/context.md +5 -5
  63. package/bundle/guides/stories.md +29 -21
  64. package/bundle/manifest.json +145 -13
  65. package/bundle/release-notes/1.50.0.mdx +4 -0
  66. package/bundle/release-notes/1.51.0.mdx +57 -0
  67. package/bundle/release-notes/1.52.0.mdx +55 -0
  68. package/bundle/release-notes/1.53.0.mdx +60 -0
  69. package/bundle/release-notes/1.54.0.mdx +45 -0
  70. package/bundle/release-notes/1.55.0.mdx +7 -0
  71. package/bundle/tokens.json +377 -222
  72. package/dist/index.js +39 -1
  73. package/dist/index.js.map +1 -1
  74. package/package.json +1 -1
@@ -1,16 +1,16 @@
1
1
  ---
2
2
  component: SMSField
3
- category: Form Controls
4
- figma-node: ""
5
- figma-file: ""
6
- related: [TextField, Tooltip, ButtonIcon, FormField]
3
+ category: TextFields
4
+ related: [FormField, FormGroup, ButtonIcon, Tooltip]
7
5
  tokens:
8
- - --triplex-next-SMSField-Submit_Background_Default
9
- - --triplex-next-SMSField-Submit_Background_Hover
10
- - --triplex-next-SMSField-Submit_Background_Active
11
- - --triplex-next-SMSField-Refresh_Fill_Empty
12
- - --triplex-next-SMSField-Refresh_Fill_Full
13
- - --triplex-next-SMSField-Refresh_Disabled
6
+ - FormField.Input_Color_Disabled
7
+ - SMSField.Refresh_Disabled
8
+ - SMSField.Refresh_Fill_Empty
9
+ - SMSField.Refresh_Fill_Full
10
+ - SMSField.Submit_Background_Active
11
+ - SMSField.Submit_Background_Default
12
+ - SMSField.Submit_Background_Hover
13
+ - Typography.Error_Color
14
14
  stories: stories/SMSField/SMSField.stories.tsx
15
15
  version: "1.0"
16
16
  ---
@@ -19,137 +19,129 @@ version: "1.0"
19
19
 
20
20
  ## Назначение
21
21
 
22
- Композитный компонент для ввода SMS-кода: поле ввода + кнопка отправки +
23
- кнопка повторного запроса кода с обратным отсчётом + tooltip над Refresh.
24
- Работает через `SMSFieldContext` — подкомпоненты обязаны быть детьми `SMSField`.
22
+ Управляемое поле ввода СМС-кода с кнопками повторного запроса и отправки. Корневой компонент хранит контекст для составных частей `SMSField.Input`, `SMSField.Refresh`, `SMSField.Submit` и `SMSField.Tooltip`; значение кода и обратный отсчёт хранит потребитель.
25
23
 
26
- Используй когда: нужно подтверждение действия SMS-кодом (логин, оплата,
27
- verification). Для произвольных цифровых полей используй `TextField` с
28
- `type="tel"`.
24
+ Используй когда: нужен ввод цифрового кода подтверждения с повторным запросом и отправкой кнопкой или клавишей Enter.
29
25
 
30
- ---
31
-
32
- ## Файловая структура
33
-
34
- ```text
35
- src/components/SMSField/
36
- ├── SMSField.tsx # FC + статичные Composition
37
- ├── SMSFieldContext.ts # Shared state (code, disabled, size...)
38
- ├── types.ts # ISMSFieldProps
39
- ├── components/
40
- │ ├── SMSFieldInput.tsx # <input> на базе FormField/FormFieldInput
41
- │ ├── SMSFieldSubmit.tsx # ButtonIcon (CIRCLE) с SubmitIcon
42
- │ ├── SMSFieldRefresh.tsx # ButtonIcon (SQUIRCLE) + круговой прогресс
43
- │ ├── SMSFieldTooltip.tsx # Оборачивает Refresh в Tooltip
44
- │ ├── RefreshIcon.tsx
45
- │ └── SubmitIcon.tsx
46
- ├── styles/
47
- └── index.ts
48
- ```
26
+ Не используй когда: нужен произвольный текст (`TextField`), форматирование по маске (`MaskedField`) или выбор значения из подсказок (`SuggestField`). SMSField не выполняет запрос к серверу и не проверяет правильность кода.
49
27
 
50
28
  ---
51
29
 
52
- ## Slot-компоненты
30
+ ## Варианты и props
31
+
32
+ ### Обязательные props
53
33
 
54
- | Slot | Props | Назначение |
34
+ | Prop | Тип | Описание |
55
35
  |---|---|---|
56
- | `SMSField.Input` | `ISMSFieldInputProps` (`InputHTMLAttributes` + `counter?`, `description?`) | Поле ввода кода; фильтрует ввод (только цифры), `maxLength = 8` по умолчанию, `Enter` → `onSubmitCode`. |
57
- | `SMSField.Submit` | `ButtonHTMLAttributes` | Кнопка отправки (ButtonIcon CIRCLE); автоматически disabled, когда `code === ""`. |
58
- | `SMSField.Refresh` | `countdownTime`, `countdownTimeLeft`, `onRefresh` | Кнопка запроса кода с круговым прогрессом; disabled пока `countdownTimeLeft > 0`. |
59
- | `SMSField.Tooltip` | `Partial<ITooltipProps>` + `message`, `children` (ReactElement), `targetRef` | Оборачивает `Refresh` (через `targetRef`) в `Tooltip` с hover-триггером. |
36
+ | `code` | `string` | Текущее значение кода. Для пустого поля передавай `""`; обновляй значение в ответ на `onChangeCode`. |
37
+ | `onChangeCode` | callback | Получает новое значение при допустимом вводе в `SMSField.Input`: строку из цифр `0–9` или пустую строку. |
38
+ | `onSubmitCode` | callback | Получает текущий `code` при нажатии доступной `SMSField.Submit` или Enter в `SMSField.Input`. |
39
+ | `size` | `EComponentSize` | Общий размер поля и кнопок: SM / MD / LG. Дефолта у корневого компонента нет. |
60
40
 
61
- ---
41
+ ### Опциональные props
62
42
 
63
- ## Варианты и props
43
+ | Prop | Тип | По умолчанию | Описание |
44
+ |---|---|---|---|
45
+ | `status` | `EFormFieldStatus` без `WARNING` | `DEFAULT` | `DEFAULT`, `ERROR` или `DISABLED`. Ошибка сохраняет возможность ввода, disabled блокирует поле и обе кнопки. |
46
+ | `children` | `ReactNode` | — | Составные части поля; корень не добавляет Input, Refresh, Submit или Tooltip автоматически. |
47
+ | `className` | `string` | — | Добавляется на корневой `div`. |
64
48
 
65
- ### `ISMSFieldProps`
49
+ Остальные HTML-атрибуты `div`, обработчики и `data-test-id` передаются корневому контейнеру. Атрибуты поля ввода (`id`, `aria-label`, `placeholder`, `maxLength` и другие) задаются на `SMSField.Input`.
66
50
 
67
- | Prop | Тип | Default | Описание |
68
- |---|---|---|---|
69
- | `code` | `string` | — | Текущее значение кода. |
70
- | `onChangeCode` | `(code: string) => void` | — | Вызывается при валидном вводе (только цифры). |
71
- | `onSubmitCode` | `(code: string) => void` | — | Вызывается из `SMSField.Submit` и по Enter в `SMSField.Input`. |
72
- | `size` | `EComponentSize` | — | Прокидывается в дочерние FormField/ButtonIcon. |
73
- | `disabled?` | `boolean` | `false` | Блокирует все вложенные контролы. |
74
- | `error?` | `boolean` | `false` | Устанавливает `EFormFieldStatus.ERROR` у `SMSField.Input`. |
75
- | `...HTMLAttributes<HTMLDivElement>` | — | — | Прокидываются на корневой div. |
76
- | `TestProps` | — | — | `data-test-*` атрибуты. |
77
-
78
- ### `ISMSFieldInputProps`
79
-
80
- | Prop | Тип | Default | Описание |
81
- |---|---|---|---|
82
- | `counter?` | `ReactNode` | — | Отображается в `FormFieldCounter` внутри description. |
83
- | `description?` | `ReactNode` | — | Текст под полем. |
84
- | `maxLength?` | `number` | `8` | Максимальная длина кода. |
85
- | `...InputHTMLAttributes` | — | — | Стандартные атрибуты input. |
51
+ ### Составные части
86
52
 
87
- ### `ISMSFieldRefreshProps`
53
+ - `SMSField.Input` (`SMSFieldInput`) — ввод на базе `FormFieldInput`. Значение задавай через корневой `code`. По умолчанию `maxLength={8}`, `autoComplete="off"`; при попытке ввода нецифровой строки не вызывает ни `onChangeCode`, ни собственный `onChange`. `description` и `counter` выводятся в общем блоке под полем, счётчик вычисляет потребитель.
54
+ - `errorText` у Input заменяет placeholder в статусе `ERROR`, пока поле не в фокусе. В фокусе возвращается обычный placeholder. Текущее значение кода сохраняется; текст ошибки виден как placeholder только у пустого поля. Без `errorText` остаются рамка ошибки и `aria-invalid`.
55
+ - `SMSField.Submit` (`SMSFieldSubmit`) — кнопка отправки. Недоступна при пустом `code`, корневом `DISABLED` или собственном `disabled`; при доступном клике вызывает `onSubmitCode(code)`, затем свой `onClick`. Наличие кода включает активный вид кнопки. Ref указывает на `button`. Полноту и правильность кода компонент не проверяет: непустое значение можно отправить до достижения `maxLength`.
56
+ - `SMSField.Refresh` (`SMSFieldRefresh`) — кнопка повторного запроса. Требует `countdownTime`, `countdownTimeLeft` и `onRefresh`; недоступна при корневом `DISABLED`, собственном `disabled` или `countdownTimeLeft > 0`. При доступном клике вызывает `onRefresh()`, затем свой `onClick`. Таймер, очистку кода и сетевой запрос реализует потребитель. Радиальное заполнение иконки отображает долю прошедшего времени.
57
+ - `SMSField.Tooltip` (`SMSFieldTooltip`) — обёртка над одним React-элементом с обязательными `message` и `targetRef`. Для обычной композиции используй тот же ref на `SMSField.Refresh` и в `targetRef`. Размер SM, выравнивание START, hover-переключение и отключение адаптивного режима заданы как настройки по умолчанию и могут переопределяться props Tooltip.
88
58
 
89
- | Prop | Тип | Описание |
90
- |---|---|---|
91
- | `countdownTime` | `number` | Полное время отсчёта (сек). Для расчёта прогресса. |
92
- | `countdownTimeLeft` | `number` | Оставшееся время (сек). Если `> 0` — кнопка disabled. |
93
- | `onRefresh` | `() => void` | Вызывается при клике. |
59
+ ### Синхронизация и ограничения
60
+
61
+ - Корень передаёт `code`, callbacks, `size` и `status` составным частям через `SMSFieldContext`. Фильтр цифр работает только при вводе в Input: переданный снаружи `code` не нормализуется.
62
+ - `disabledSubmit` хранится в корне и изначально равен `true`. `SMSField.Submit` синхронизирует его эффектом со своей доступностью; Input использует это значение для отправки по Enter. Для согласованного поведения Enter размещай Submit внутри того же SMSField.
63
+ - `tooltipId` хранится в корневом контексте: Tooltip записывает туда свой `id` (без него — значение `uniqueId`, сгенерированное при монтировании), Refresh берёт его для `aria-describedby`. Для воспроизводимых визуальных примеров задавай стабильный `id`.
64
+ - У составных частей нет собственных `size` и `status` — они задаются только через корень.
94
65
 
95
66
  ---
96
67
 
97
- ## Контекст (`SMSFieldContext`)
68
+ ## Дизайн-токены
69
+
70
+ Прямые ссылки общего `styles/SMSField.module.less`:
98
71
 
99
- Поля: `code`, `disabled`, `error`, `onChangeCode`, `onSubmitCode`, `size`,
100
- `sizeClassName`, `disabledSubmit` / `setDisabledSubmit`, `tooltipId` / `setTooltipId`.
72
+ ```text
73
+ FormField.Input_Color_Disabled
74
+ SMSField.Refresh_Disabled
75
+ SMSField.Refresh_Fill_Empty
76
+ SMSField.Refresh_Fill_Full
77
+ SMSField.Submit_Background_Active
78
+ SMSField.Submit_Background_Default
79
+ SMSField.Submit_Background_Hover
80
+ Typography.Error_Color
81
+ ```
82
+
83
+ Токены `SMSField.Refresh_*` управляют заполнением и недоступным видом иконки повторного запроса. `SMSField.Submit_Background_*` управляют заливкой иконки отправки: DEFAULT для неактивного состояния, ACTIVE при наличии кода, HOVER при наведении на доступную активную кнопку. `FormField.Input_Color_Disabled` используется для placeholder недоступного Input, `Typography.Error_Color` — для placeholder с текстом ошибки.
101
84
 
102
- - `disabledSubmit` — вычисляется в `SMSField.Submit` (через `useEffect`) и
103
- читается в `SMSField.Input` для обработки Enter.
104
- - `tooltipId` — генерируется в `SMSField.Tooltip` и читается в `SMSField.Refresh`
105
- для `aria-describedby`.
85
+ Значения по умолчанию находятся в `src/components/DesignTokens/components/SMSField.ts`, `FormField.ts` и `Typography.ts`. Переопределяй их через `ThemeProvider` (prop `tokens`) — см. [ThemeProvider-ai.md](../ThemeProvider/ThemeProvider-ai.md), раздел «Как переопределять токены».
86
+
87
+ Внешний вид базовых поля, кнопок и подсказки также наследует токены семейств FormField, Button/IconWrapper и Tooltip.
106
88
 
107
89
  ---
108
90
 
109
91
  ## Инварианты
110
92
 
111
- - **Регэксп ввода `/^[0-9]*$/`** — только цифры; нецифровой ввод отбрасывается в `SMSFieldInput.handleChange`.
112
- - **`maxLength` default `8`** — переопределяемо, но не нужно без причины.
113
- - **Enter в `Input`** вызывает `onSubmitCode(code)` только когда `!disabledSubmit`.
114
- - **`Submit` disabled, если `code === ""`** — «пустой» код не отправляется.
115
- - **`Refresh` disabled, если `countdownTimeLeft > 0`** — пользователь не может запросить код повторно до окончания отсчёта.
116
- - **`Refresh` остаётся enabled при `allDisabled && error`** (`(allDisabled && !error) || disabled || isSmsCountdownTicking`) — пользователь должен иметь возможность перезапросить код при ошибке.
117
- - **`SMSField.Tooltip` требует `targetRef`** — ref на Refresh-кнопку.
118
- - **`ISMSFieldTooltipProps.children: React.ReactElement`** — единственный ребёнок.
119
- - **`SMSField` — FC** (не forwardRef). `Input`, `Refresh` — forwardRef на соответствующие HTML-элементы.
120
- - **`FormField`/`FormFieldInput`** используется внутри Input — это фиксирует error/disabled-стилизацию.
93
+ - `forwardRef<HTMLDivElement>` на SMSField сохраняется; внешний ref указывает на прежний корневой `div`, а не на input. Ref на поле ввода передаётся в `SMSField.Input`, ref на кнопки — в `SMSField.Refresh` и `SMSField.Submit`.
94
+ - Статические части `SMSField.Input`, `.Refresh`, `.Submit`, `.Tooltip` и отдельные экспорты `SMSFieldInput`, `SMSFieldRefresh`, `SMSFieldSubmit`, `SMSFieldTooltip` из barrel сохраняются. Имена и типы props, значения `EComponentSize` и допустимые значения `EFormFieldStatus` — публичный API.
95
+ - Корневой `div` остаётся контейнером с `position: relative` и полной шириной; кнопки позиционируются внутри него. `className`, HTML-атрибуты и `data-tx` относятся к корню.
96
+ - Связь через единый `SMSFieldContext`, синхронизация `disabledSubmit` кнопкой Submit и передача `tooltipId` от Tooltip к Refresh определяют поведение композиции; не меняй их независимо друг от друга.
97
+ - Сохраняй фильтрацию ввода, порядок callbacks и текущую передачу `code` при отправке. Enter сейчас определяется по `keyCode` через `EVENT_KEY_CODES.ENTER`; замена клавиатурного контракта требует проверки поведения.
98
+ - Имена дизайн-токенов, LESS-классы и существующие story IDs `components-smsfield--*` нельзя переименовывать без согласования и синхронизации тестов. Цвета берутся из токенов.
99
+ - Генерацию tooltip ID через `uniqueId` не заменяй на React 18-only API: библиотека поддерживает React 17 через ветку `release-0`.
121
100
 
122
101
  ---
123
102
 
124
103
  ## Accessibility
125
104
 
126
- - `SMSField.Input` наследует accessibility от `FormFieldInput`.
127
- - `SMSField.Refresh` получает `aria-describedby={tooltipId}` → screen reader озвучивает tooltip как описание кнопки.
128
- - `autoComplete="off"` захардкожен на input — предотвращает автозаполнение.
129
- - Tooltip-триггер — `hover`; для клавиатуры — фокус на кнопке активирует tooltip в Tooltip-компоненте (см. Tooltip-AI).
105
+ - Корневой `div` не имеет роли и label и не управляет фокусом; для фокуса используй ref `SMSField.Input`.
106
+ - Доступное имя Input и обеих кнопок-иконок задаёт потребитель: например, `aria-label` или связь через `aria-labelledby`. Placeholder и `description` сами по себе не заменяют доступное имя. Компонент не хардкодит язык подписей.
107
+ - Input в `ERROR` выставляет `aria-invalid="true"`, в других статусах атрибут отсутствует. Текст `errorText` отображается как placeholder; автоматического `aria-live` или связи input с текстом ошибки нет. При необходимости связь через `aria-describedby` задаёт потребитель на Input.
108
+ - Корневой `DISABLED` выставляет нативный `disabled` на Input, Submit и Refresh: они исключаются из обычного таб-обхода. Локальный `disabled` кнопки действует дополнительно.
109
+ - Enter в Input отправляет текущий код только при `disabledSubmit === false`; пользовательский `onKeyDown` Input вызывается на самом `input` после этой обработки. SMSField не вызывает `preventDefault` для Enter. Tab, фокус/blur и активация кнопок сохраняют нативное поведение.
110
+ - Refresh получает `aria-describedby={tooltipId}` из контекста. Tooltip связывает подсказку с target через `targetRef` и ID; сам базовый Tooltip не добавляет `role="tooltip"`.
130
111
 
131
112
  ---
132
113
 
133
114
  ## Связанные компоненты
134
115
 
135
- - `FormField`, `FormFieldInput`, `FormFieldCounter`, `FormFieldDescription` — основа поля ввода.
136
- - `ButtonIcon` — основа для Submit/Refresh.
137
- - `Tooltip` — обёртка Refresh.
138
- - `TextField` — общий аналог для не-SMS кейсов.
116
+ - `SMSFieldInput`, `SMSFieldRefresh`, `SMSFieldSubmit`, `SMSFieldTooltip` — составные части, доступны также как `SMSField.*`. Зависимости ниже рендерят они, а не корень.
117
+ - `FormField` — через Input передаёт статус и размер базовому полю; `FormFieldInput`, `FormFieldDescription` и `FormFieldCounter` формируют ввод и нижний блок.
118
+ - `FormGroup` — через Input объединяет поле с описанием и счётчиком.
119
+ - `ButtonIcon` — база Submit и Refresh; даёт нативный `button`, недоступное состояние и обработчики.
120
+ - `Tooltip` — база SMSField.Tooltip; её `targetRef` и дополнительные props определяют размещение и открытие подсказки.
121
+ - `RefreshIcon` и `SubmitIcon` — внутренние SVG-иконки Refresh и Submit, из barrel не экспортируются.
122
+ - `TextField`, `MaskedField`, `SuggestField` — поля для других задач ввода, описанных в «Не используй когда».
139
123
 
140
124
  ---
141
125
 
142
126
  ## Stories
143
127
 
144
128
  Основные истории: `stories/SMSField/SMSField.stories.tsx`
145
- Примеры: `stories/SMSField/examples/`
129
+ Файлы примеров: `stories/SMSField/examples/`
146
130
 
147
- | Story | Что демонстрирует |
148
- |---|---|
149
- | `Playground` | Интерактивный контроль |
150
- | `Error` | Состояние ошибки + Refresh доступен |
151
- | `Disabled` | Общий disabled |
152
- | `Sizes` | Все размеры (MD/LG) |
131
+ | Story | Example file | Что демонстрирует |
132
+ |---|---|---|
133
+ | `Playground` | `Playground.tsx` | Управляемый код, size/status, настройки Input, счётчик и внешний обратный отсчёт; Actions для callbacks. |
134
+ | `Default` | `Default.tsx` | Минимальная контролируемая композиция с доступными именами поля и кнопок. |
135
+ | `Sizes` | `Sizes.tsx` | Пустое поле DEFAULT в размерах SM / MD / LG с подписями. |
136
+ | `Statuses` | `Statuses.tsx` | Пустое поле в DEFAULT, ERROR (errorText вместо placeholder) и DISABLED; WARNING не поддерживается. |
137
+ | `Example` | `Example.tsx` | Внешний таймер повторного запроса, Tooltip, описание, maxLength и счётчик. |
138
+ | `VisualTests` | `VisualTests.tsx` | Заполненный код в DEFAULT / ERROR / DISABLED, начало и середина обратного отсчёта, описание и счётчик. |
139
+ | `VisualTestsFocused` | `VisualTestsFocused.tsx` | Фокус пустого поля DEFAULT через play. |
140
+ | `VisualTestsErrorFocused` | `VisualTestsErrorFocused.tsx` | Фокус пустого ERROR: обычный placeholder вместо errorText. |
141
+ | `VisualTestsSubmitHovered` | `VisualTestsSubmitHovered.tsx` | Hover доступной активной кнопки отправки при заполненном коде. |
142
+ | `VisualTestsRefreshHovered` | `VisualTestsRefreshHovered.tsx` | Hover кнопки повторного запроса с открытой подсказкой в портале и стабильным ID. |
143
+
144
+ Код документационных примеров показывается через `?raw`. Из скриншот-тестов исключены Playground, Default (дублирует LG в Sizes) и Example (живой таймер). Stories с одним размером используют LG — основной размер на формах: статус меняет только цвета, а геометрию размеров проверяет Sizes. В hover-stories курсор наводит test-runner по `parameters.testRunner.hoverSelector` (CSS `:hover`), а `play` открывает подсказку.
153
145
 
154
146
  ---
155
147
 
@@ -157,4 +149,187 @@ src/components/SMSField/
157
149
 
158
150
  | Дата | Изменение |
159
151
  |---|---|
160
- | 2026-04-18 | Создан документ |
152
+ | 2026-10-05 | Создан документ (TRI-72). Описаны контролируемый код, составные части, контекст и accessibility; добавлен ref на корневой div и на SMSField.Submit, stories переведены на modern pattern. |
153
+
154
+ ---
155
+
156
+ ## Примеры использования
157
+
158
+ ### Default
159
+
160
+ `stories/SMSField/examples/Default.tsx`
161
+
162
+ ```tsx
163
+ import React, { useState } from "react";
164
+ import { SMSField, EComponentSize } from "@sberbusiness/triplex-next";
165
+
166
+ export const Default = () => {
167
+ const [code, setCode] = useState("");
168
+
169
+ return (
170
+ <div style={{ width: 300 }}>
171
+ <SMSField code={code} onChangeCode={setCode} onSubmitCode={() => {}} size={EComponentSize.LG}>
172
+ <SMSField.Refresh
173
+ aria-label="Запросить новый код"
174
+ countdownTime={10}
175
+ countdownTimeLeft={0}
176
+ onRefresh={() => {}}
177
+ />
178
+ <SMSField.Input aria-label="СМС-код" placeholder="Введите код" />
179
+ <SMSField.Submit aria-label="Отправить код" />
180
+ </SMSField>
181
+ </div>
182
+ );
183
+ };
184
+ ```
185
+
186
+ ### Sizes
187
+
188
+ `stories/SMSField/examples/Sizes.tsx`
189
+
190
+ ```tsx
191
+ import React, { useState } from "react";
192
+ import { SMSField, EComponentSize } from "@sberbusiness/triplex-next";
193
+
194
+ interface ISizeItemProps {
195
+ size: EComponentSize;
196
+ }
197
+
198
+ const SizeItem = ({ size }: ISizeItemProps) => {
199
+ const [code, setCode] = useState("");
200
+
201
+ return (
202
+ <div>
203
+ <div style={{ marginBottom: 8, fontSize: 16, fontWeight: 700 }}>{size.toUpperCase()}</div>
204
+ <SMSField code={code} onChangeCode={setCode} onSubmitCode={() => {}} size={size}>
205
+ <SMSField.Refresh
206
+ aria-label="Запросить новый код"
207
+ countdownTime={10}
208
+ countdownTimeLeft={0}
209
+ onRefresh={() => {}}
210
+ />
211
+ <SMSField.Input aria-label={`СМС-код ${size}`} placeholder="Введите код" />
212
+ <SMSField.Submit aria-label="Отправить код" />
213
+ </SMSField>
214
+ </div>
215
+ );
216
+ };
217
+
218
+ const SIZES = Object.values(EComponentSize);
219
+
220
+ export const Sizes = () => (
221
+ <div style={{ display: "flex", flexDirection: "column", gap: 16, width: 300 }}>
222
+ {SIZES.map((size) => (
223
+ <SizeItem key={size} size={size} />
224
+ ))}
225
+ </div>
226
+ );
227
+ ```
228
+
229
+ ### Statuses
230
+
231
+ `stories/SMSField/examples/Statuses.tsx`
232
+
233
+ ```tsx
234
+ import React, { useState } from "react";
235
+ import { SMSField, EComponentSize, EFormFieldStatus, ISMSFieldProps } from "@sberbusiness/triplex-next";
236
+
237
+ interface IStatusItemProps {
238
+ status: NonNullable<ISMSFieldProps["status"]>;
239
+ }
240
+
241
+ const StatusItem = ({ status }: IStatusItemProps) => {
242
+ const [code, setCode] = useState("");
243
+
244
+ return (
245
+ <div>
246
+ <div style={{ marginBottom: 8, fontSize: 16, fontWeight: 700 }}>{status.toUpperCase()}</div>
247
+ <SMSField
248
+ code={code}
249
+ onChangeCode={setCode}
250
+ onSubmitCode={() => {}}
251
+ size={EComponentSize.LG}
252
+ status={status}
253
+ >
254
+ <SMSField.Refresh
255
+ aria-label="Запросить новый код"
256
+ countdownTime={10}
257
+ countdownTimeLeft={0}
258
+ onRefresh={() => {}}
259
+ />
260
+ <SMSField.Input aria-label={`СМС-код ${status}`} errorText="Неверный код" placeholder="Введите код" />
261
+ <SMSField.Submit aria-label="Отправить код" />
262
+ </SMSField>
263
+ </div>
264
+ );
265
+ };
266
+
267
+ // WARNING в SMSField не поддерживается.
268
+ const STATUSES = [EFormFieldStatus.DEFAULT, EFormFieldStatus.ERROR, EFormFieldStatus.DISABLED] as const;
269
+
270
+ export const Statuses = () => (
271
+ <div style={{ display: "flex", flexDirection: "column", gap: 16, width: 300 }}>
272
+ {STATUSES.map((status) => (
273
+ <StatusItem key={status} status={status} />
274
+ ))}
275
+ </div>
276
+ );
277
+ ```
278
+
279
+ ### Example
280
+
281
+ `stories/SMSField/examples/Example.tsx`
282
+
283
+ ```tsx
284
+ import React, { useEffect, useRef, useState } from "react";
285
+ import { SMSField, EComponentSize } from "@sberbusiness/triplex-next";
286
+
287
+ const COUNTDOWN_TIME = 10;
288
+ const MAX_LENGTH = 8;
289
+
290
+ export const Example = () => {
291
+ const [code, setCode] = useState("");
292
+ const [countdownTimeLeft, setCountdownTimeLeft] = useState(0);
293
+ const targetRef = useRef<HTMLButtonElement>(null);
294
+
295
+ useEffect(() => {
296
+ if (countdownTimeLeft === 0) {
297
+ return;
298
+ }
299
+
300
+ const timeoutId = window.setTimeout(() => setCountdownTimeLeft((timeLeft) => timeLeft - 1), 1000);
301
+
302
+ return () => window.clearTimeout(timeoutId);
303
+ }, [countdownTimeLeft]);
304
+
305
+ const handleRefresh = () => {
306
+ setCode("");
307
+ setCountdownTimeLeft(COUNTDOWN_TIME);
308
+ };
309
+
310
+ return (
311
+ <div style={{ width: 300 }}>
312
+ <SMSField code={code} onChangeCode={setCode} onSubmitCode={() => {}} size={EComponentSize.LG}>
313
+ <SMSField.Tooltip targetRef={targetRef} message="Запросить СМС-код ещё раз">
314
+ <SMSField.Refresh
315
+ aria-label="Запросить новый код"
316
+ countdownTime={COUNTDOWN_TIME}
317
+ countdownTimeLeft={countdownTimeLeft}
318
+ onRefresh={handleRefresh}
319
+ ref={targetRef}
320
+ />
321
+ </SMSField.Tooltip>
322
+ <SMSField.Input
323
+ aria-label="СМС-код"
324
+ counter={`${code.length}/${MAX_LENGTH}`}
325
+ description="Введите код из СМС"
326
+ maxLength={MAX_LENGTH}
327
+ placeholder="Введите код"
328
+ />
329
+ <SMSField.Submit aria-label="Отправить код" />
330
+ </SMSField>
331
+ </div>
332
+ );
333
+ };
334
+ ```
335
+
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  component: SelectExtendedField
3
3
  category: SelectExtendedField
4
- related: [SelectField, MultiselectField, SuggestField, Dropdown, FormField, KeyDownListener, IconWrapper, LoaderSmall]
4
+ related: [SelectField, MultiselectField, SuggestField, Dropdown, Puller, FormField, KeyDownListener, IconWrapper, LoaderSmall]
5
5
  tokens: []
6
6
  stories: stories/SelectExtendedField/SelectExtendedField.stories.tsx
7
7
  version: "1.0"
@@ -75,8 +75,8 @@ version: "1.0"
75
75
  | `label` | `React.ReactNode` | — | Выбранное значение |
76
76
  | `placeholder` | `React.ReactNode` | — | Отображается, пока нет `label` |
77
77
  | `loading` | `boolean` | `false` | Вместо каретки — лоадер; клик и клавиатура не открывают блок |
78
- | `onClear` | `() => void` | — | Если передан, в постфиксе появляется кнопка очистки |
79
- | `prefix` / `postfix` | `React.ReactNode` | — | Слоты `FormFieldPrefix` / `FormFieldPostfix`. Постфикс идёт **после** каретки |
78
+ | `onClear` | `React.MouseEventHandler<HTMLButtonElement>` | — | Если передан, в постфиксе появляется кнопка очистки |
79
+ | `prefix` / `postfix` | `React.ReactNode` | — | Слоты `FormFieldPrefix` / `FormFieldPostfix`. Постфикс идёт **после** каретки. Обработка событий — см. «Ограничения» |
80
80
  | `size` | `EComponentSize` | `EComponentSize.MD` | Размер поля. Определяет размер каретки и лоадера |
81
81
  | `status` | `EFormFieldStatus` | `EFormFieldStatus.DEFAULT` | Визуальное состояние. `DISABLED` блокирует открытие |
82
82
 
@@ -86,7 +86,7 @@ version: "1.0"
86
86
  а через prop `forwardedRef` — рендер-функция `children` отдаёт готовый `dropdownRef`,
87
87
  и передать его через `ref` в JSX было бы невозможно без промежуточной обёртки.
88
88
  Остальные props — как у `Dropdown` (`size`, `width`, `direction`, `alignment`,
89
- `mobileViewProps`, ...). Статическое свойство `List` — это `DropdownList`.
89
+ `pullerProps`, ...). Статическое свойство `List` — это `DropdownList`.
90
90
 
91
91
  ### Ограничения
92
92
 
@@ -94,7 +94,8 @@ version: "1.0"
94
94
  - `SelectExtendedFieldDropdown` — не `forwardRef`-компонент. Ссылка передаётся через `forwardedRef`.
95
95
  - Выпадающий блок компонент не рендерит сам и не знает, открыт ли он визуально: `opened` нужно передать в `SelectExtendedFieldDropdown` (или в свою разметку) самостоятельно.
96
96
  - Компонент не хранит выбранное значение и не знает про опции. Значение — целиком забота потребителя.
97
- - Клик по кнопке очистки (`onClear`) не раскрывает выпадающий блок: `SelectExtendedFieldTarget` останавливает всплытие в обработчике `FormFieldClear`. Без этого событие дошло бы до `onClick` на `FormField` и переключило `opened` — так же гасят всплытие `Chip.Select` и `Chip.DatePicker`. Не убирать: затрагивает `SelectField` и `MultiselectField.Target`, которые рендерят тот же `Target`.
97
+ - Клик по фокусируемым элементам внутри поля (`tabIndex >= 0`: кнопка очистки, `HelpBox`, кнопки тегов в значении) и по содержимому порталов (Tooltip) не переключает выпадающий блок; Enter / Space на них не открывают блок и не отменяют их нативный клик. Клик по остальному содержимому поля, включая элементы с `tabIndex < 0` (`<button tabIndex={-1}>`, `div` с `onClick` без `tabIndex`), блок переключает. Затрагивает `SelectField` и `MultiselectField.Target`.
98
+ - Клик по кнопке очистки (`onClear`) не всплывает: `onClick` поля и обработчики внешних элементов его не получают.
98
99
 
99
100
  ---
100
101
 
@@ -135,7 +136,7 @@ version: "1.0"
135
136
 
136
137
  - `SelectExtendedFieldTarget` выставляет на корневом элементе `aria-haspopup="listbox"` и `aria-expanded`, отражающий `opened`.
137
138
  - Роли `listbox` / `option` и клавиатурную навигацию по опциям (`ArrowUp` / `ArrowDown`, выбор по `Enter` / `Space`) даёт `DropdownList` — то есть `SelectExtendedField.Dropdown.List`. Если выпадающий блок не список, эти роли не появляются и навигацию организует потребитель.
138
- - Клавиши открытия на поле выбора: `Space`, `Enter`, `ArrowDown`, `ArrowUp`. Обработчик вызывает `preventDefault` (чтобы страница не прокручивалась) и `stopPropagation` — иначе то же нажатие поймает документный обработчик `DropdownListItem` и список сразу выберет опцию.
139
+ - Клавиши открытия на поле выбора: `Space`, `Enter`, `ArrowDown`, `ArrowUp` (только когда фокус на самом поле). Обработчик вызывает `preventDefault` (чтобы страница не прокручивалась) и `stopPropagation` — иначе то же нажатие поймает документный обработчик `DropdownListItem` и список сразу выберет опцию.
139
140
  - В состояниях `loading` и `status={EFormFieldStatus.DISABLED}` поле не реагирует ни на клик, ни на клавиатуру; `onClick` / `onKeyDown` потребителя в этих состояниях тоже не вызываются.
140
141
  - `Escape` закрывает блок из любого места страницы — слушатель `KeyDownListener` висит на `window`.
141
142
  - `Tab` закрывает блок только при `closeOnTab`. Фокус компонент не перехватывает и не возвращает: focus trap, если нужен, добавляет потребитель.
@@ -171,7 +172,8 @@ version: "1.0"
171
172
 
172
173
  **Инфраструктура:**
173
174
 
174
- - `Dropdown` — выпадающее меню через `Portal`, позиционирование, мобильная версия.
175
+ - `Dropdown` — выпадающее меню через `Portal`, позиционирование, переключение на мобильную версию.
176
+ - `Puller` — мобильная версия выпадающего блока в `SelectExtendedFieldDropdownDefault` (`PullerHeader`, `PullerClose`, `PullerBody`, `PullerList`, `PullerListItem`).
175
177
  - `FormField` — визуальный контейнер поля, контекст размера и статуса.
176
178
  - `KeyDownListener` — закрытие по `Escape`.
177
179
  - `IconWrapper`, `LoaderSmall` — каретка и лоадер в постфиксе поля.
@@ -204,6 +206,8 @@ version: "1.0"
204
206
  | 2026-08-07 | Создан документ AI-ready для `SelectExtendedField` (TRI-74). |
205
207
  | 2026-08-07 | AI-рефакторинг (TRI-74): `onOpen` / `onClose` сравнивают предыдущее значение `opened` вместо флага «смонтирован» — колбэки больше не срабатывают ложно при двойном маунте в React 18 StrictMode. Удалены ссылки на несуществующие CSS-классы в `SelectExtendedFieldTarget` (в `class` попадал литерал `undefined`). Клик по кнопке очистки больше не раскрывает выпадающий блок — всплытие останавливается, как в `Chip.Select`. Слушатель `mousedown` навешивается только пока блок открыт, callback-ref стабилизирован через `useCallback`, убран мёртвый внутренний `targetRef` в `SelectExtendedFieldTarget`, дедуплицирован список опций десктопной и мобильной версий в `SelectExtendedFieldDropdownDefault`. JSDoc на всех props, unit-тесты разбиты по субкомпонентам и расширены с 42 до 69 кейсов. Stories переведены на modern pattern. Публичный API не изменён. |
206
208
  | 2026-09-29 | Аудит симметрии `related` (TRI-156): из `related` убраны несимметричные имена (`Chip`). Пояснения к убранным именам сохранены прозой в «Связанные компоненты»; публичный API и поведение не затронуты. |
209
+ | 2026-10-21 | **Breaking** (TRIPLEX-961): prop `mobileViewProps`, унаследованный `SelectExtendedField.Dropdown` от `Dropdown`, переименован в `pullerProps`. |
210
+ | 2026-10-29 | TRIPLEX-1028: `SelectExtendedField.Target` переключает выпадающий блок только по событиям самого поля — см. «Ограничения». `onClear` получает событие клика. |
207
211
 
208
212
  ---
209
213
 
@@ -59,8 +59,8 @@ version: "1.0"
59
59
  | `loading` | `boolean` | `false` | Вместо каретки — лоадер; список не раскрывается ни мышью, ни с клавиатуры |
60
60
  | `targetProps` | `Omit<ISelectExtendedFieldTargetProps, "opened" \| "setOpened" \| "size">` | — | Уходит целиком в `SelectExtendedField.Target`. Здесь задаётся **обязательный** заголовок поля `fieldLabel`, а также `prefix`, `postfix`, `onClear` |
61
61
  | `dropdownProps` | `ISelectExtendedFieldDropdownDefaultProps["dropdownProps"]` | — | Настройки `Dropdown` (`direction`, `alignment`, `className`, ...). `width`, `size`, `opened` и `targetRef` компонент задаёт сам |
62
- | `dropdownListItemClassName` | `string` | — | Класс каждого элемента списка — и десктопного `DropdownListItem`, и мобильного `DropdownMobileListItem` |
63
- | `mobileTitle` | `React.ReactNode` | — | Заголовок выпадающего блока в мобильном режиме. Отдельный prop: на мобильном список раскрывается на весь экран и `fieldLabel` в нём не виден |
62
+ | `dropdownListItemClassName` | `string` | — | Класс каждого элемента списка — и десктопного `DropdownListItem`, и мобильного `PullerListItem` |
63
+ | `mobileTitle` | `React.ReactNode` | — | Заголовок выпадающего блока в мобильном режиме. Отдельный prop: на мобильном список открывается в нижней шторке `Puller`, и `fieldLabel` в ней не виден |
64
64
  | `closeOnTab` | `boolean` | `true` | Компонент передаёт `closeOnTab={true}`, но `{...rest}` идёт после, поэтому переданное потребителем значение выигрывает |
65
65
  | `children` | `never` | — | Не принимаются: выпадающий блок компонент рендерит сам |
66
66
 
@@ -147,8 +147,9 @@ version: "1.0"
147
147
  `SelectField` рендерит вокруг выпадающего блока. Без этого провайдера связка не работает.
148
148
  - `aria-expanded` и `aria-haspopup="listbox"` выставляет `SelectExtendedFieldTarget`;
149
149
  роли `listbox` / `option` и навигацию `ArrowUp` / `ArrowDown` — `DropdownList`.
150
- - Клавиши открытия списка: `Space`, `Enter`, `ArrowDown`, `ArrowUp`. Закрытие — `Escape`
151
- (из любого места страницы), `Tab` (`closeOnTab`) и нажатие мышью вне поля и списка.
150
+ - Клавиши открытия списка: `Space`, `Enter`, `ArrowDown`, `ArrowUp` (только когда фокус на самом
151
+ поле). Закрытие — `Escape` (из любого места страницы), `Tab` (`closeOnTab`) и нажатие мышью
152
+ вне поля и списка.
152
153
  - В состояниях `loading` и `status={EFormFieldStatus.DISABLED}` поле не реагирует ни на клик,
153
154
  ни на клавиатуру.
154
155
  - **Доступное имя задаёт потребитель.** Компонент не хардкодит текст (библиотека мультиязычная):
@@ -206,6 +207,8 @@ version: "1.0"
206
207
  |---|---|
207
208
  | 2026-09-03 | Создан документ (TRI-75). AI-рефакторинг: удалён мёртвый внутренний `targetRef`, внешний `ref` пробрасывается напрямую и больше не пересоздаётся на каждый рендер; идентификатор списка переведён с `useRef(uniqueId())` на `useState(() => uniqueId())`, чтобы счётчик не крутился на каждом рендере; параметры рендер-функций перестали затенять `props` компонента; импорты приведены к алиасу пакета; JSDoc на всех props и на компоненте. Unit-тесты расширены с 15 до 24 кейсов (`onChange` с аргументом, опции и выбранное значение, `aria-activedescendant` через `DropdownListContext`, стабильность `listId`, ref-target). Stories переведены на modern pattern, `States` разделена на `Statuses` и `Loading`. Публичный API не изменён |
208
209
  | 2026-09-29 | Аудит симметрии `related` (TRI-156): из `related` убраны несимметричные имена (`Chip`); добавлены обратные ссылки (`FormField`). Пояснения к убранным именам сохранены прозой в «Связанные компоненты»; публичный API и поведение не затронуты. |
210
+ | 2026-10-21 | Переход на `Puller` (TRIPLEX-961): мобильный элемент списка — `PullerListItem` (бывший `DropdownMobileListItem`). |
211
+ | 2026-10-29 | TRIPLEX-1028: Enter / Space на кнопке очистки и других фокусируемых элементах внутри поля больше не открывают список, клик по таким элементам, кроме очистки, — не переключает. Клавиши открытия — см. «Accessibility». `targetProps.onClear` получает событие клика. |
209
212
 
210
213
  ---
211
214