@sberbusiness/triplex-next-mcp-server 1.41.0-beta → 1.43.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/AlertProcess.md +460 -0
- package/bundle/components/Amount.md +145 -169
- package/bundle/components/Badge.md +155 -219
- package/bundle/components/Body.md +108 -116
- package/bundle/components/Button.md +1 -0
- package/bundle/components/CarouselExtended.md +355 -194
- package/bundle/components/Checkbox.md +7 -1
- package/bundle/components/CheckboxTreeExtended.md +149 -248
- package/bundle/components/CheckboxXGroup.md +2 -0
- package/bundle/components/CheckboxYGroup.md +7 -2
- package/bundle/components/ChipGroup.md +195 -126
- package/bundle/components/ComposedKeyDownListener.md +323 -0
- package/bundle/components/FooterPage.md +2 -1
- package/bundle/components/HeaderPage.md +2 -1
- package/bundle/components/Island.md +100 -12
- package/bundle/components/KeyDownListener.md +11 -72
- package/bundle/components/LightBox.md +6 -0
- package/bundle/components/LoaderSmall.md +230 -0
- package/bundle/components/MaskedField.md +481 -0
- package/bundle/components/MediaWidth.md +252 -108
- package/bundle/components/MobileView.md +192 -68
- package/bundle/components/ModalWindow.md +3 -1
- package/bundle/components/Notification.md +21 -3
- package/bundle/components/Page.md +3 -1
- package/bundle/components/Pagination.md +20 -0
- package/bundle/components/SelectExtendedField.md +691 -236
- package/bundle/components/SliderExtended.md +358 -222
- package/bundle/components/TabsExtended.md +380 -194
- package/bundle/components/UnorderedList.md +254 -0
- package/bundle/components/UnorderedListExtended.md +255 -0
- package/bundle/components/WindowResizeListener.md +249 -0
- package/bundle/guides/commits.md +66 -3
- package/bundle/guides/context.md +26 -0
- package/bundle/guides/stories.md +3 -1
- package/bundle/guides/template.md +1 -1
- package/bundle/manifest.json +64 -21
- package/bundle/release-notes/1.41.1.mdx +18 -0
- package/bundle/release-notes/1.42.0.mdx +81 -0
- package/bundle/release-notes/1.43.0.mdx +94 -0
- package/bundle/release-notes/1.44.0.mdx +7 -0
- package/package.json +1 -1
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: MobileView
|
|
3
|
-
category:
|
|
4
|
-
|
|
5
|
-
figma-file: ""
|
|
6
|
-
related: [MediaWidth]
|
|
3
|
+
category: MobileView
|
|
4
|
+
related: [MediaWidth, MediaMaxWidth, useMatchMedia, useMobileView]
|
|
7
5
|
tokens: []
|
|
8
|
-
stories:
|
|
6
|
+
stories: stories/MobileView/MobileView.stories.tsx
|
|
9
7
|
version: "1.0"
|
|
10
8
|
---
|
|
11
9
|
|
|
@@ -13,108 +11,234 @@ version: "1.0"
|
|
|
13
11
|
|
|
14
12
|
## Назначение
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
Сервисный headless-компонент адаптивности. Рендерит `children`, когда ширина окна
|
|
15
|
+
браузера соответствует мобильному устройству (`max-width: 767px`), и `fallback` —
|
|
16
|
+
во всех остальных случаях. Собственной разметки не добавляет: в DOM попадает
|
|
17
|
+
ровно тот элемент, который выбран по медиа-запросу.
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
`MediaWidth`/`MediaMinWidth`/`MediaMaxWidth`.
|
|
19
|
+
Реализован как сокращение над `MediaWidth` с `maxWidth={EScreenWidth.SM_MAX}` —
|
|
20
|
+
вся логика подписки на медиа-запрос живёт в `MediaMaxWidth` → `useMatchMedia`.
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
Используй когда: на мобильной ширине нужно отрендерить **другое дерево**
|
|
23
|
+
компонентов, а не просто другие стили — мобильный вариант дропдауна вместо
|
|
24
|
+
десктопного, тултип-модалка вместо всплывающей подсказки, список вместо таблицы.
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
26
|
+
Не используй когда:
|
|
27
|
+
- Разница между мобильным и десктопом чисто визуальная (отступы, шрифт,
|
|
28
|
+
раскладка) — это дешевле и надёжнее решается медиа-запросом в
|
|
29
|
+
`*.module.less`, без перемонтирования поддерева.
|
|
30
|
+
- Нужен не выбор между двумя готовыми элементами, а булево значение для props
|
|
31
|
+
или условной разметки вокруг — используй хук `useMobileView` (см. ниже).
|
|
32
|
+
- Нужна не мобильная, а произвольная граница ширины — используй `MediaWidth`
|
|
33
|
+
(`minWidth` / `maxWidth`) напрямую.
|
|
35
34
|
|
|
36
35
|
---
|
|
37
36
|
|
|
38
37
|
## Варианты и props
|
|
39
38
|
|
|
40
|
-
###
|
|
39
|
+
### Обязательные props
|
|
40
|
+
|
|
41
|
+
| Prop | Тип | Описание |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| `children` | `React.ReactElement \| null` | Элемент для мобильной ширины (`<768px`). `null` — на мобильной ширине не рендерится ничего |
|
|
44
|
+
| `fallback` | `React.ReactElement \| null` | Элемент для остальных ширин. Обязателен: prop без значения по умолчанию, для «ничего» передаётся явный `null` |
|
|
45
|
+
|
|
46
|
+
Опциональных props нет. `className`, `...rest` и `ref` компонент не принимает —
|
|
47
|
+
у него нет собственного host-элемента.
|
|
48
|
+
|
|
49
|
+
### Особенности поведения
|
|
50
|
+
|
|
51
|
+
- Граница — `EScreenWidth.SM_MAX` (`767px`), запрос `(max-width: 767px)`. При
|
|
52
|
+
ширине ровно `767px` рендерится `children`, при `768px` — `fallback`.
|
|
53
|
+
- **Ветки взаимоисключающие.** При пересечении границы одна ветка размонтируется,
|
|
54
|
+
вторая монтируется: локальный state, фокус, значения неконтролируемых полей и
|
|
55
|
+
текущие анимации внутри поддерева теряются. Состояние, которое должно
|
|
56
|
+
переживать смену ширины, храни выше `MobileView` (или передавай его как props
|
|
57
|
+
в обе ветки).
|
|
58
|
+
- Значение медиа-запроса читается синхронно на первом рендере (`useMatchMedia`
|
|
59
|
+
вызывает `window.matchMedia` в инициализаторе `useState`), поэтому «мигания»
|
|
60
|
+
десктопной ветки на мобильном экране нет.
|
|
61
|
+
- Пересчёт при ресайзе идёт по событию `change` у `MediaQueryList`, а не по
|
|
62
|
+
`resize` окна — слушатель снимается при размонтировании.
|
|
63
|
+
- **Компонент не SSR-safe:** `window.matchMedia` вызывается на первом рендере.
|
|
64
|
+
Это поведение унаследовано от `useMatchMedia` и общее для всего семейства
|
|
65
|
+
`MediaWidth`.
|
|
66
|
+
|
|
67
|
+
### `useMobileView`
|
|
68
|
+
|
|
69
|
+
Хук из того же barrel (`src/components/MobileView/useMobileView.ts`). Возвращает
|
|
70
|
+
`boolean` — результат того же медиа-запроса `(max-width: 767px)`. Нужен, когда
|
|
71
|
+
от ширины зависят props или разметка вокруг, а не выбор между двумя готовыми
|
|
72
|
+
элементами: размер кнопки, `block`-режим, наличие иконки. Так его используют
|
|
73
|
+
`DateFieldTarget`, `MonthYearFieldTarget`, `CheckboxTreeExtended`, `IslandWidget`,
|
|
74
|
+
`IslandAccordionItem`, `Tooltip`.
|
|
75
|
+
|
|
76
|
+
Компонент и хук строят строку запроса независимо (`MediaMaxWidth` — из своего
|
|
77
|
+
prop, хук — из константы `MOBILE_QUERY`), но обе строки собираются из
|
|
78
|
+
`EScreenWidth.SM_MAX` и всегда совпадают. Правя одно, проверь второе.
|
|
79
|
+
|
|
80
|
+
---
|
|
41
81
|
|
|
42
|
-
|
|
43
|
-
|---|---|---|---|
|
|
44
|
-
| `children` | `ReactElement \| null` | Да | Рендерится при ширине ≤ SM_MAX (767px). |
|
|
45
|
-
| `fallback` | `ReactElement \| null` | Да | Рендерится в остальных случаях (desktop). |
|
|
82
|
+
## Дизайн-токены
|
|
46
83
|
|
|
47
|
-
|
|
84
|
+
Собственных стилей и CSS-переменных нет — компонент не рендерит разметку.
|
|
48
85
|
|
|
49
86
|
---
|
|
50
87
|
|
|
51
|
-
##
|
|
88
|
+
## Инварианты
|
|
52
89
|
|
|
53
|
-
|
|
90
|
+
- **Без `forwardRef` — осознанное исключение** из общего правила библиотеки:
|
|
91
|
+
компонент не рендерит собственный host-элемент, форвардить `ref` некуда.
|
|
92
|
+
Не добавлять и не убирать без отдельного решения — это публичный контракт.
|
|
93
|
+
- **Имена и типы props `children`, `fallback`** и интерфейс `IMobileViewProps`
|
|
94
|
+
(экспортируется из barrel) — публичное API, не менять. `fallback` остаётся
|
|
95
|
+
обязательным: сделать его опциональным можно только осознанно, это меняет
|
|
96
|
+
контракт у потребителей.
|
|
97
|
+
- **Сигнатура `useMobileView(): boolean`** и его barrel-экспорт — публичное API.
|
|
98
|
+
Хук используется внутри библиотеки в шести компонентах.
|
|
99
|
+
- **Граница мобильной ширины — `EScreenWidth.SM_MAX`.** Не хардкодить `767px`
|
|
100
|
+
и не переводить на `MD_MIN` с `min-width`: значение согласовано с сеткой
|
|
101
|
+
дизайн-системы и с LESS-переменными брейкпоинтов.
|
|
102
|
+
- **Делегирование в `MediaWidth`** — не инлайнить `useMatchMedia` в
|
|
103
|
+
`MobileView.tsx`: семейство `MediaWidth` остаётся единственным местом с
|
|
104
|
+
логикой подписки на медиа-запросы.
|
|
105
|
+
- Реализация остаётся React 17-совместимой (без `useId`,
|
|
106
|
+
`useSyncExternalStore` и прочих React 18-only API): код синхронизируется
|
|
107
|
+
в `release-0`.
|
|
54
108
|
|
|
55
|
-
|
|
56
|
-
<MediaWidth maxWidth={EScreenWidth.SM_MAX} fallback={fallback}>
|
|
57
|
-
{children}
|
|
58
|
-
</MediaWidth>
|
|
59
|
-
```
|
|
109
|
+
---
|
|
60
110
|
|
|
61
|
-
|
|
62
|
-
- **Фиксированный брейкпоинт** — если нужен другой (например, планшеты), используй `MediaWidth` с явным `maxWidth`, а не оборачивай MobileView.
|
|
111
|
+
## Accessibility
|
|
63
112
|
|
|
64
|
-
|
|
113
|
+
- Собственной ARIA-семантики нет — компонент прозрачен для accessibility-дерева.
|
|
114
|
+
- Невыбранная ветка **не рендерится в DOM**, а не скрывается визуально: скринридер
|
|
115
|
+
и последовательность табуляции видят только актуальный вариант. Дублирования
|
|
116
|
+
контента для ассистивных технологий не возникает, но и «десктопная» разметка
|
|
117
|
+
на мобильном недоступна.
|
|
118
|
+
- Смена ветки при ресайзе удаляет из DOM сфокусированный элемент — фокус
|
|
119
|
+
переходит на `<body>`. Если это критично (открытый оверлей, форма), верни
|
|
120
|
+
фокус явно на стороне потребителя.
|
|
121
|
+
- Медиа-запрос завязан на ширину окна, а не на тип ввода: на десктопе с узким
|
|
122
|
+
окном тоже рендерится мобильная ветка. Не завязывай на `MobileView` наличие
|
|
123
|
+
touch-специфичных обработчиков как единственного способа взаимодействия.
|
|
65
124
|
|
|
66
|
-
|
|
125
|
+
---
|
|
67
126
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
127
|
+
## Связанные компоненты
|
|
128
|
+
|
|
129
|
+
- `MediaWidth` (`src/components/MediaWidth/`) — общий случай: `minWidth` и/или
|
|
130
|
+
`maxWidth`. `MobileView` — его частный случай для мобильной границы.
|
|
131
|
+
- `MediaMaxWidth`, `MediaMinWidth`, `MediaBetweenWidth` — реализации конкретных
|
|
132
|
+
диапазонов, к которым `MediaWidth` делегирует рендер.
|
|
133
|
+
- `useMatchMedia` (`src/components/MediaWidth/useMatchMedia.ts`) — хук подписки
|
|
134
|
+
на произвольный медиа-запрос, основа и компонента, и `useMobileView`.
|
|
75
135
|
|
|
76
|
-
###
|
|
136
|
+
### Потребители внутри библиотеки
|
|
77
137
|
|
|
78
|
-
|
|
138
|
+
Компонентом (`MobileView`): `Dropdown`, `Tooltip` (`TooltipBody`, `TooltipLink`),
|
|
139
|
+
`HelpBox`, `DateField`, `SuggestField`, `TabsLine`, `ImageGallery`,
|
|
140
|
+
`ImageGalleryExtendedMain`.
|
|
141
|
+
Хуком (`useMobileView`): `DateFieldTarget`, `MonthYearFieldTarget`,
|
|
142
|
+
`CheckboxTreeExtended`, `IslandWidget`, `IslandAccordionItem`, `Tooltip`.
|
|
143
|
+
|
|
144
|
+
В unit-тестах потребителей мобильная/десктопная ветка задаётся подменой
|
|
145
|
+
`window.matchMedia` (как в `Dropdown.test.tsx`) либо мокированием самого модуля
|
|
146
|
+
`MobileView` (как в `ImageGallery.test.tsx`, `LightBox.test.tsx`).
|
|
79
147
|
|
|
80
148
|
---
|
|
81
149
|
|
|
82
|
-
##
|
|
150
|
+
## Stories
|
|
83
151
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
- **Обе prop'ы обязательны** (`ReactElement | null`, не `ReactNode`, не optional).
|
|
87
|
-
- **FC, не forwardRef** — ref не пробрасывается.
|
|
152
|
+
Основные истории: `stories/MobileView/MobileView.stories.tsx`
|
|
153
|
+
Файлы примеров: `stories/MobileView/examples/`
|
|
88
154
|
|
|
89
|
-
|
|
155
|
+
Playground у компонента нет: `children` и `fallback` — React-элементы, для
|
|
156
|
+
Controls в них нечего настраивать. Скриншот-тесты снимают обе ветки: на
|
|
157
|
+
viewport `xs` (575px) рендерится мобильный вариант, на `xl` (1200px) —
|
|
158
|
+
`fallback`.
|
|
90
159
|
|
|
91
|
-
|
|
160
|
+
| Story | Example file | Что демонстрирует |
|
|
161
|
+
|---|---|---|
|
|
162
|
+
| `Default` | `Default.tsx` | Выбор между `children` и `fallback` по ширине окна |
|
|
163
|
+
| `Example: useMobileView` | `Example.tsx` | Хук вместо компонента: на мобильной ширине кнопки становятся блочными и меняют размер |
|
|
92
164
|
|
|
93
|
-
|
|
165
|
+
Пример использования `MobileView` есть и в stories `MediaWidth`
|
|
166
|
+
(`stories/MediaWidth/examples/MobileViewExample.tsx`) — он показывает связь
|
|
167
|
+
частного случая с общим и остаётся в документации `MediaWidth`.
|
|
94
168
|
|
|
95
169
|
---
|
|
96
170
|
|
|
97
|
-
##
|
|
171
|
+
## История изменений
|
|
98
172
|
|
|
99
|
-
|
|
173
|
+
| Дата | Изменение |
|
|
174
|
+
|---|---|
|
|
175
|
+
| 2026-08-10 | Создан документ. AI-рефакторинг (TRI-59): `useMobileView.tsx` → `useMobileView.ts` (в файле нет JSX), явный тип возврата у хука, `displayName` у `MobileView`, дополнен JSDoc, добавлены unit-тесты (15 кейсов) и stories по modern pattern. Публичный API не изменён (props, сигнатура хука, barrel-экспорты; `forwardRef` по-прежнему осознанно отсутствует). |
|
|
100
176
|
|
|
101
177
|
---
|
|
102
178
|
|
|
103
|
-
##
|
|
179
|
+
## Примеры использования
|
|
104
180
|
|
|
105
|
-
|
|
106
|
-
- `EScreenWidth.SM_MAX` — из `@sberbusiness/triplex-next/helpers/breakpoints`.
|
|
181
|
+
### Default
|
|
107
182
|
|
|
108
|
-
|
|
183
|
+
`stories/MobileView/examples/Default.tsx`
|
|
109
184
|
|
|
110
|
-
|
|
185
|
+
```tsx
|
|
186
|
+
import React from "react";
|
|
187
|
+
import { MobileView } from "@sberbusiness/triplex-next";
|
|
188
|
+
|
|
189
|
+
export const Default = () => (
|
|
190
|
+
<MobileView
|
|
191
|
+
fallback={
|
|
192
|
+
<div style={{ border: "1px dashed rgb(125, 131, 138)", borderRadius: "4px", padding: "16px" }}>
|
|
193
|
+
Десктопная версия: ширина окна браузера 768px и больше.
|
|
194
|
+
</div>
|
|
195
|
+
}
|
|
196
|
+
>
|
|
197
|
+
<div style={{ border: "1px dashed rgb(125, 131, 138)", borderRadius: "4px", padding: "16px" }}>
|
|
198
|
+
Мобильная версия: ширина окна браузера меньше 768px.
|
|
199
|
+
</div>
|
|
200
|
+
</MobileView>
|
|
201
|
+
);
|
|
202
|
+
```
|
|
111
203
|
|
|
112
|
-
|
|
204
|
+
### Example: useMobileView
|
|
113
205
|
|
|
114
|
-
|
|
206
|
+
`stories/MobileView/examples/Example.tsx`
|
|
115
207
|
|
|
116
|
-
|
|
208
|
+
```tsx
|
|
209
|
+
import React from "react";
|
|
210
|
+
import { Button, EButtonTheme, EComponentSize, useMobileView } from "@sberbusiness/triplex-next";
|
|
211
|
+
|
|
212
|
+
export const Example = () => {
|
|
213
|
+
const mobile = useMobileView();
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<div
|
|
217
|
+
style={{
|
|
218
|
+
display: "flex",
|
|
219
|
+
flexDirection: mobile ? "column" : "row",
|
|
220
|
+
gap: "8px",
|
|
221
|
+
maxWidth: "400px",
|
|
222
|
+
}}
|
|
223
|
+
>
|
|
224
|
+
<Button
|
|
225
|
+
theme={EButtonTheme.GENERAL}
|
|
226
|
+
size={mobile ? EComponentSize.SM : EComponentSize.MD}
|
|
227
|
+
block={mobile}
|
|
228
|
+
onClick={() => {}}
|
|
229
|
+
>
|
|
230
|
+
Сохранить
|
|
231
|
+
</Button>
|
|
232
|
+
<Button
|
|
233
|
+
theme={EButtonTheme.SECONDARY}
|
|
234
|
+
size={mobile ? EComponentSize.SM : EComponentSize.MD}
|
|
235
|
+
block={mobile}
|
|
236
|
+
onClick={() => {}}
|
|
237
|
+
>
|
|
238
|
+
Отменить
|
|
239
|
+
</Button>
|
|
240
|
+
</div>
|
|
241
|
+
);
|
|
242
|
+
};
|
|
243
|
+
```
|
|
117
244
|
|
|
118
|
-
| Дата | Изменение |
|
|
119
|
-
|---|---|
|
|
120
|
-
| 2026-04-18 | Создан документ |
|
|
@@ -101,7 +101,7 @@ version: "1.0"
|
|
|
101
101
|
|
|
102
102
|
- **`ModalWindowContent`** — контейнер контента. Оборачивает дочерние секции в `Page` (`triplex-next/Page`). Имеет `isLoading?: boolean` и `loaderScreenProps?: ILoaderScreenMiddleProps` — при `isLoading` поверх контента показывается `LoaderScreen` типа `middle`; кастомизация лоадера (текст, кнопки и др.) передаётся через `loaderScreenProps`. Свой `displayName`. Свои props через интерфейс `IModalWindowContentProps` — экспортируется.
|
|
103
103
|
- **`ModalWindowHeader`** — заголовок. Тривиальная обёртка над `HeaderPage` с фиксированным `type=FIRST` и доп. отступом справа под кнопку закрытия (через less). Экспонирует статическое поле `Title = HeaderPage.Title`. Тип `IModalWindowHeaderProps` (Omit `children`/`type` от `IHeaderPageTypeFirstProps`).
|
|
104
|
-
- **`ModalWindowBody`** — тело. Тривиальная обёртка над `Island` с фиксированными `type=TYPE_1` и `size=MD`. Тип `IModalWindowBodyProps extends IIslandProps`.
|
|
104
|
+
- **`ModalWindowBody`** — тело. Тривиальная обёртка над `Island` с фиксированными `type=TYPE_1` и `size=MD`. Тип `IModalWindowBodyProps extends Omit<IIslandProps, "loading" | "loaderScreenProps">`. Состояние загрузки у `Island` намеренно отрезано: тело скроллируется (`overflow: auto`), а `LoaderScreen` позиционируется абсолютно и уехал бы вместе с контентом. Загрузку модального окна задавай на `ModalWindowContent`.
|
|
105
105
|
- **`ModalWindowFooter`** — футер. Тривиальная обёртка над `FooterPage` с фиксированным `type=FIRST`. Экспонирует статическое поле `Description = FooterPage.Description`.
|
|
106
106
|
- **`ModalWindowClose`** — кнопка закрытия. Обёртка над `Button` (`theme=SECONDARY`, `size=MD`, иконка `CrossStrokeSrvIcon20`) внутри `TriggerClickOnKeyDownEvent` с `EVENT_KEY_CODES.ESCAPE`. **Текст `title` не имеет дефолта внутри компонента** — библиотека мультиязычная, локализованную подсказку (например, `title="Закрыть"`) передаёт потребитель через props. Тип `IModalWindowCloseProps` (Omit `size`/`theme`/`icon` от `IButtonSecondaryProps`).
|
|
107
107
|
- **`ModalWindowViewManager`** — невидимый компонент-сенсор. Создаёт DOM-ноду `<div id="modalWindowViewManagerNodeId">` (если её нет в `body`) и рендерит туда через `Portal` resize-сенсор + `<style>`-инжект CSS-переменных `--modalWindow-screen-*` в `:root`. Подключается автоматически из `ModalWindow`; экспортируется отдельно для случая, когда лайаут хочет позиционировать ноду вручную (например, рисовать модалку только в области рабочей зоны без шапки).
|
|
@@ -133,6 +133,8 @@ version: "1.0"
|
|
|
133
133
|
| 2026-05-06 | Возвращён `forwardRef` в `ModalWindowContent` и `ModalWindowViewManager` (обязательный инвариант проекта). Удалён захардкоженный `title="Закрыть"` из `ModalWindowClose` — для мультиязычной библиотеки текст подсказки должен приходить от потребителя. **Breaking:** теперь `title` не подставляется автоматически. |
|
|
134
134
|
| 2026-06-25 | Fix: устранено падение `focus-trap` `"must have at least one container with at least one tabbable node"` при открытии модалки без tabbable-узлов (асинхронный контент/лоадер). Добавлены `fallbackFocus` на dialog-узел и `tabIndex={-1}` на `role="dialog"`. Регрессия проявилась после синхронной активации ловушки в рефакторинге 1.28.0. Добавлен интеграционный тест на реальном `focus-trap`. |
|
|
135
135
|
| 2026-06-30 | Fix: модалка не отображалась под React 18 `StrictMode`. На искусственном unmount/mount-цикле cleanup в `useEffect` открепляет портальный `mountNode`, а инициализатор `useState` повторно не вызывается — узел оставался вне DOM. Теперь `useEffect` при (повторном) mount возвращает узел в wrapper, если тот откреплён. Wrapper вынесен в `getOrCreateWrapperNode`. Добавлен регрессионный тест на реальном `Portal` (`ModalWindow.strictMode.test.tsx`). |
|
|
136
|
+
| 2026-08-07 | `IModalWindowBodyProps` сузился до `Omit<IIslandProps, "isLoading" \| "loaderScreenProps">`: в `Island` появилось состояние загрузки, но в скроллируемом теле модалки абсолютный `LoaderScreen` уезжает вместе с контентом. Публичный API не изменён — props исключены до их первого релиза; путь для загрузки модального окна прежний, `ModalWindowContent`. |
|
|
137
|
+
| 2026-08-11 | Prop загрузки в `Island` переименован `isLoading` → `loading`, вслед за ним `Omit` в `IModalWindowBodyProps` — теперь `Omit<IIslandProps, "loading" \| "loaderScreenProps">`. Публичный API `ModalWindow` не изменён: у `ModalWindowContent` prop по-прежнему `isLoading`. |
|
|
136
138
|
|
|
137
139
|
---
|
|
138
140
|
|
|
@@ -202,7 +202,13 @@ export const Status = () => (
|
|
|
202
202
|
<Notification.Body.Content>
|
|
203
203
|
This message provides context or highlights important information to note.
|
|
204
204
|
</Notification.Body.Content>
|
|
205
|
-
<Notification.Body.List
|
|
205
|
+
<Notification.Body.List
|
|
206
|
+
items={[
|
|
207
|
+
{ key: "1", children: "List item text" },
|
|
208
|
+
{ key: "2", children: "List item text" },
|
|
209
|
+
{ key: "3", children: "List item text" },
|
|
210
|
+
]}
|
|
211
|
+
/>
|
|
206
212
|
</Notification.Body>
|
|
207
213
|
<Notification.Close onClick={() => {}} />
|
|
208
214
|
</Notification>
|
|
@@ -218,7 +224,13 @@ export const Status = () => (
|
|
|
218
224
|
<Notification.Body.Content>
|
|
219
225
|
This message provides context or highlights important information to note.
|
|
220
226
|
</Notification.Body.Content>
|
|
221
|
-
<Notification.Body.List
|
|
227
|
+
<Notification.Body.List
|
|
228
|
+
items={[
|
|
229
|
+
{ key: "1", children: "List item text" },
|
|
230
|
+
{ key: "2", children: "List item text" },
|
|
231
|
+
{ key: "3", children: "List item text" },
|
|
232
|
+
]}
|
|
233
|
+
/>
|
|
222
234
|
</Notification.Body>
|
|
223
235
|
<Notification.Close onClick={() => {}} />
|
|
224
236
|
</Notification>
|
|
@@ -234,7 +246,13 @@ export const Status = () => (
|
|
|
234
246
|
<Notification.Body.Content>
|
|
235
247
|
This message provides context or highlights important information to note.
|
|
236
248
|
</Notification.Body.Content>
|
|
237
|
-
<Notification.Body.List
|
|
249
|
+
<Notification.Body.List
|
|
250
|
+
items={[
|
|
251
|
+
{ key: "1", children: "List item text" },
|
|
252
|
+
{ key: "2", children: "List item text" },
|
|
253
|
+
{ key: "3", children: "List item text" },
|
|
254
|
+
]}
|
|
255
|
+
/>
|
|
238
256
|
</Notification.Body>
|
|
239
257
|
<Notification.Close onClick={() => {}} />
|
|
240
258
|
</Notification>
|
|
@@ -61,6 +61,7 @@ version: "1.0"
|
|
|
61
61
|
Собственных CSS-переменных дизайн-токенов у `Page` нет. Внешние отступы и брейкпоинты заданы
|
|
62
62
|
LESS-переменными в `styles/Page.module.less` (`@page-padding-*`, `@screen-sm-max`). Sticky-стили
|
|
63
63
|
для прилипающих Header/Footer (классы `.headerPageTypeFirst.sticky`, `.footerPageTypeFirst.sticky`)
|
|
64
|
+
применяются при ширине viewport от 992px (`@screen-lg`) и высоте от 801px и
|
|
64
65
|
используют токены соответствующих компонентов (`--triplex-next-HeaderPage-StickyShadow`,
|
|
65
66
|
`--triplex-next-FooterPage-StickyShadow`) — они описаны в AI.md этих компонентов.
|
|
66
67
|
|
|
@@ -73,7 +74,7 @@ LESS-переменными в `styles/Page.module.less` (`@page-padding-*`, `@s
|
|
|
73
74
|
- `Page` собран через `Object.assign(forwardRef(...), { Body, Header, Footer })` — статические
|
|
74
75
|
свойства `Header` / `Body` / `Footer` должны сохраняться.
|
|
75
76
|
- Корневые классы `page` и `global-page` на корневом `<div>` — не переименовывать (используются
|
|
76
|
-
в
|
|
77
|
+
в стилях; sticky-стили частей задаются медиазапросом `min-width: 992px` и `min-height: 801px`).
|
|
77
78
|
- Экспорты `Page`, `IPageProps` идут в barrel `src/components/Page/index.ts` — сохранять.
|
|
78
79
|
Там же реэкспортируются части и их enum'ы (`EHeaderPageType`, `EFooterPageType`, `EBodyPageType`,
|
|
79
80
|
`EBodyPageVerticalMargin`).
|
|
@@ -120,6 +121,7 @@ aria-атрибутов. Семантику задаёт потребитель
|
|
|
120
121
|
|
|
121
122
|
| Дата | Изменение |
|
|
122
123
|
|---|---|
|
|
124
|
+
| 2026-08-06 | Sticky-позиционирование Header/Footer доступно вне `LightBox` при ширине viewport от 992px и отключено при высоте viewport не больше 800px. |
|
|
123
125
|
| 2026-07-20 | Вертикальные padding изменены: 24px (было 32px) для desktop, 8px (было 16px) для mobile — через `@page-padding-desktop-y` / `@page-padding-mobile-y`. |
|
|
124
126
|
| 2026-06-24 | Создан документ. AI-рефакторинг (JSDoc на компоненте и `IPageProps`), unit-тесты (className-merge, статические части), миграция stories на modern pattern. |
|
|
125
127
|
|
|
@@ -13,6 +13,7 @@ related:
|
|
|
13
13
|
- SelectField
|
|
14
14
|
- ButtonIcon
|
|
15
15
|
- ButtonBase
|
|
16
|
+
- MasterTable
|
|
16
17
|
tokens:
|
|
17
18
|
- --triplex-next-Pagination-PageButton_Background_Hover
|
|
18
19
|
- --triplex-next-Pagination-PageButton_Background_Active
|
|
@@ -63,6 +64,21 @@ version: "1.0"
|
|
|
63
64
|
|
|
64
65
|
Расширяет `ISelectFieldProps` (без `size`, с обязательным `value`) и добавляет `paginationLabel: React.ReactNode` — текст лейбла перед селектом. Размер селекта зафиксирован как `EComponentSize.SM`. `value`, `options`, `onChange` — управляются потребителем.
|
|
65
66
|
|
|
67
|
+
### Состояние загрузки внутри `MasterTable`
|
|
68
|
+
|
|
69
|
+
Собственного prop загрузки у пагинации нет. Внутри `MasterTable` с `loading` элементы пагинации
|
|
70
|
+
блокируются автоматически: `PaginationNavigationButton` и `PaginationPageButton` получают
|
|
71
|
+
`disabled`, `PaginationSelect` — `status={EFormFieldStatus.DISABLED}`, из-за которого выпадающий
|
|
72
|
+
список не открывается. Читается это из `MasterTableContext` (`loading`), поэтому работает и через
|
|
73
|
+
`Pagination`, и через ручную компоновку на `PaginationExtended`.
|
|
74
|
+
|
|
75
|
+
Лоадер при этом рисует `TableBasic` — он лежит в собственном `position: relative`-контейнере и
|
|
76
|
+
перекрывает только таблицу. Пагинация в `MasterTable.PaginationPanel` остаётся видимой и читаемой.
|
|
77
|
+
|
|
78
|
+
Вне `MasterTable` контекст отдаёт `loading: false`, поэтому автономная пагинация ведёт себя
|
|
79
|
+
как раньше: блокировка приходит только из `disabled` на `buttonPrevProps`/`buttonNextProps` и из
|
|
80
|
+
крайних страниц.
|
|
81
|
+
|
|
66
82
|
### Subкомпоненты для кастомной компоновки
|
|
67
83
|
|
|
68
84
|
Для нестандартной раскладки используется `PaginationExtended` напрямую с вложенными `PaginationNavigation` и `PaginationSelect` (см. story `Extended`). Низкоуровневые части (`PaginationNavigationExtended`, `PaginationNavigationExtendedItem`, `PaginationPageButton`, `PaginationPageEllipsis`, `PaginationNavigationButton`) экспортируются для построения полностью кастомной навигации, но обычно используются неявно через `PaginationNavigation`.
|
|
@@ -92,6 +108,8 @@ version: "1.0"
|
|
|
92
108
|
- Навигация рендерится только при `totalPages > 1` — поведение публичное, на него опираются потребители.
|
|
93
109
|
- Логику `PaginationUtils.createPagesArray` / `generatePageRanges` / `generateRange` не менять без перегенерации `__tests__/paginationUtils.test.tsx` — там зафиксированы граничные случаи раскладки.
|
|
94
110
|
- `PAGINATION_ELLIPSIS_VALUE = -1` — sentinel в массиве страниц, на него завязан рендер многоточия.
|
|
111
|
+
- Блокировка по `MasterTableContext.loading` перекрывает `disabled`, пришедший из props: в состоянии загрузки кнопка заблокирована независимо от того, что передал потребитель. Обратное неверно — вне загрузки решает prop.
|
|
112
|
+
- Импорт `MasterTableContext` в частях пагинации — абсолютный (`@sberbusiness/triplex-next/components/Table/MasterTableContext`), как в самом `Table`. Относительный путь сюда не ставить.
|
|
95
113
|
|
|
96
114
|
---
|
|
97
115
|
|
|
@@ -114,6 +132,7 @@ version: "1.0"
|
|
|
114
132
|
- `PaginationPageButton` — кнопка-страница (поверх `ButtonBase`).
|
|
115
133
|
- `PaginationPageEllipsis` — многоточие, заменяющее группу скрытых страниц.
|
|
116
134
|
- `PaginationNavigationButton` — кнопка «Назад»/«Вперёд» (поверх `ButtonIcon`), направление задаётся `EPaginationNavigationIconDirection`.
|
|
135
|
+
- `MasterTable` — таблица, в паре с которой пагинация обычно и используется (`MasterTable.PaginationPanel`). Её `loading` через `MasterTableContext` блокирует элементы пагинации.
|
|
117
136
|
|
|
118
137
|
---
|
|
119
138
|
|
|
@@ -137,6 +156,7 @@ version: "1.0"
|
|
|
137
156
|
| Дата | Изменение |
|
|
138
157
|
|---|---|
|
|
139
158
|
| 2026-06-22 | Создан документ |
|
|
159
|
+
| 2026-08-12 | В состоянии загрузки `MasterTable` элементы пагинации становятся `disabled` — читается из `MasterTableContext.loading`. Публичный API не изменился. Заодно `PaginationSelect` перестал молча терять переданный `status`. |
|
|
140
160
|
|
|
141
161
|
---
|
|
142
162
|
|