6.1 KiB
Индикация загрузки при переключении страниц в таблицах брокера
Дата: 2026-06-18 Статус: реализовано
Контекст
Страница детального просмотра брокерского счёта (BrokerAccountDetailPage.tsx) содержит несколько таблиц с пагинацией:
- PositionGroupTable — Акции, Облигации, ETF, Фонды (4 независимые таблицы с курсорной пагинацией)
- BrokerOperationsTable — Операции (курсорная пагинация, управляемая из родительского компонента)
Текущее поведение при переключении страниц: isLoading === true → таблица скрывается, показывается TableSkeleton (shimmer-строки). Это создаёт визуальный flash: контент исчезает → скелетон → новые данные. При этом placeholderData: keepPreviousData уже настроен в хуках, но компоненты его не используют — они проверяют isLoading, а не data.
Цель
Добавить плавную индикацию загрузки при переключении страниц, чтобы пользователь видел, что данные обновляются, но не терял визуальный контекст.
Дизайн (выбран C3)
Визуальное поведение
- При нажатии «→» (вперед) или «←» (назад):
- Текущее содержимое таблицы остаётся видимым (предыдущая страница)
- Поверх таблицы появляется полупрозрачный overlay с центрированным спиннером
- Кнопка пагинации показывает спиннер и блокируется
- Когда новые данные загружены:
- Overlay исчезает с fade-out
- Таблица обновляется новыми данными
- При первой загрузке (initial load):
- Overlay не используется (нет старых данных для показа)
- Показывается
TableSkeleton(как сейчас)
Как это работает технически
TanStack Query v5 предоставляет два флага:
isLoading— true, когда данных нет и идёт первый запрос (initial load)isFetching— true при любом запросе (включая фоновые refetch'и при смене cursor)
Логика рендеринга для таблиц:
if isLoading → TableSkeleton (первая загрузка, данных нет)
if isFetching && data → TableLoadingOverlay + старые данные (переключение страниц)
иначе → рендер таблицы с данными
Компонент TableLoadingOverlay
interface TableLoadingOverlayProps {
pageNumber?: number;
}
function TableLoadingOverlay({ pageNumber }: TableLoadingOverlayProps) {
return (
<div style={{
position: 'absolute', inset: 0,
background: 'rgba(255,255,255,0.65)',
display: 'flex', alignItems: 'center', justifyContent: 'center',
flexDirection: 'column', gap: 12,
transition: 'opacity 0.2s ease',
}}>
<div className="loading-spinner" />
{pageNumber !== undefined && (
<span style={{ fontSize: 13, color: 'var(--color-text-secondary)' }}>
Загрузка страницы {pageNumber}…
</span>
)}
</div>
);
}
Пагинация: спиннер в кнопке
При isFetching кнопка «→» или «←» показывает спиннер вместо стрелки и становится disabled.
@keyframes loading-spin {
to { transform: rotate(360deg); }
}
.loading-spinner {
width: 20px; height: 20px;
border: 2px solid var(--color-border);
border-top-color: var(--color-accent);
border-radius: 50%;
animation: loading-spin 0.8s linear infinite;
}
Где применяется
| Компонент | Что меняется |
|---|---|
BrokerPositionsSection.tsx (PositionGroupTable) |
Overlay вместо TableSkeleton при isFetching. Спиннер в кнопках пагинации |
BrokerOperationsTable.tsx |
Overlay вместо TableSkeleton при isFetching. Спиннер в кнопках пагинации |
Файлы для изменения
| Файл | Изменение |
|---|---|
apps/frontend/src/styles.css |
Добавить @keyframes loading-spin, .loading-spinner, .table-loading-overlay |
apps/frontend/src/pages/broker/BrokerPositionsSection.tsx |
Overlay + спиннер в пагинации. Использовать isFetching из хука |
apps/frontend/src/pages/broker/BrokerOperationsTable.tsx |
Overlay + спиннер в пагинации. Использовать isFetching из хука |
apps/frontend/src/pages/broker/BrokerAccountDetailPage.tsx |
Прокинуть isFetching для операций (из useBrokerOperations) |
apps/frontend/src/pages/broker/BrokerPages.test.tsx |
Обновить тесты для overlay-логики |
Тестирование
npm run test:frontend— существующие тесты проходят с учётом изменений- Ручная проверка: переключение страниц в Акциях, Облигациях, Операциях — overlay появляется/исчезает
- Ручная проверка: при первой загрузке — skeleton (не overlay)
- Ручная проверка: при быстром переключении (быстрее, чем загрузка) — overlay остаётся, данные не мигают