fix(ui): ограничить отображение каталога Communities
This commit is contained in:
1 parent
9d4d9f308f
commit
adfb2fc4c3
7 files changed
+108
-13
No files matched your search
@@ -1,12 +1,13 @@
|
||||
"use client"
|
||||
|
||||
import { dataGridFeatures, type DataGridFeatures } from "@/components/reui/data-grid"
|
||||
import { DataGridContainer, DataGridScrollArea, DataGridTable, dataGridFeatures, type DataGridFeatures } from "@/components/reui/data-grid"
|
||||
|
||||
import { useMemo } from "react"
|
||||
import { useEffect, useMemo } from "react"
|
||||
import {
|
||||
type ColumnDef,
|
||||
useTable,
|
||||
} from "@tanstack/react-table"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { DataGridShell } from "@/components/data-grids/shared/data-grid-shell"
|
||||
import {
|
||||
@@ -81,6 +82,7 @@ function CommunitiesDataGrid({
|
||||
() => [
|
||||
{
|
||||
id: "value",
|
||||
size: 192,
|
||||
accessorKey: "value",
|
||||
header: ({ column }) => (
|
||||
<DataGridSortHeader column={column} title="Community" className="ml-1" />
|
||||
@@ -93,7 +95,7 @@ function CommunitiesDataGrid({
|
||||
data-community-disabled={!c.enabled ? true : undefined}
|
||||
>
|
||||
<TagIcon className="size-3.5 text-muted-foreground shrink-0" />
|
||||
<span className="font-mono text-xs font-medium bg-muted px-1.5 py-0.5 rounded">
|
||||
<span className="font-mono text-xs font-medium break-all bg-muted px-1.5 py-0.5 rounded">
|
||||
{c.value}
|
||||
</span>
|
||||
<button
|
||||
@@ -102,7 +104,8 @@ function CommunitiesDataGrid({
|
||||
e.stopPropagation()
|
||||
onCopy(c.value)
|
||||
}}
|
||||
className="text-muted-foreground/40 hover:text-muted-foreground transition-colors"
|
||||
className="size-11 shrink-0 inline-flex items-center justify-center rounded text-muted-foreground hover:bg-muted"
|
||||
aria-label={`Скопировать community: ${c.value}`}
|
||||
>
|
||||
{copiedValue === c.value ? (
|
||||
<CheckIcon className="size-3" />
|
||||
@@ -121,6 +124,7 @@ function CommunitiesDataGrid({
|
||||
},
|
||||
{
|
||||
id: "name",
|
||||
size: 240,
|
||||
accessorKey: "name",
|
||||
header: ({ column }) => <DataGridSortHeader column={column} title="Имя / описание" />,
|
||||
cell: ({ row }) => (
|
||||
@@ -137,6 +141,7 @@ function CommunitiesDataGrid({
|
||||
},
|
||||
{
|
||||
id: "type",
|
||||
size: 128,
|
||||
accessorKey: "type",
|
||||
header: ({ column }) => <DataGridSortHeader column={column} title="Тип" />,
|
||||
cell: ({ row }) => (
|
||||
@@ -152,6 +157,7 @@ function CommunitiesDataGrid({
|
||||
},
|
||||
{
|
||||
id: "action",
|
||||
size: 128,
|
||||
accessorKey: "action",
|
||||
header: ({ column }) => <DataGridSortHeader column={column} title="Действие" />,
|
||||
cell: ({ row }) => {
|
||||
@@ -171,6 +177,7 @@ function CommunitiesDataGrid({
|
||||
},
|
||||
{
|
||||
id: "prefixCount",
|
||||
size: 112,
|
||||
accessorKey: "prefixCount",
|
||||
header: ({ column }) => (
|
||||
<DataGridSortHeader column={column} title="Маршрутов" className="ml-auto" />
|
||||
@@ -188,6 +195,7 @@ function CommunitiesDataGrid({
|
||||
},
|
||||
{
|
||||
id: "serverCount",
|
||||
size: 104,
|
||||
accessorKey: "serverCount",
|
||||
header: ({ column }) => (
|
||||
<DataGridSortHeader column={column} title="Серверов" className="ml-auto" />
|
||||
@@ -210,15 +218,19 @@ function CommunitiesDataGrid({
|
||||
id: "chevron",
|
||||
header: () => <span className="sr-only">Детали</span>,
|
||||
enableSorting: false,
|
||||
cell: () => <ChevronRightIcon className="size-4 text-muted-foreground/40" />,
|
||||
size: 40,
|
||||
cell: ({ row }) => (
|
||||
<Button type="button" variant="ghost" size="icon" className="size-11" aria-label={`Детали community: ${row.original.value}`} onClick={event => { event.stopPropagation(); onSelect(row.original) }}>
|
||||
<ChevronRightIcon aria-hidden="true" className="size-4 text-muted-foreground" />
|
||||
</Button>
|
||||
),
|
||||
size: 64,
|
||||
meta: {
|
||||
headerClassName: DATA_GRID_CELL_PAD_LAST,
|
||||
cellClassName: DATA_GRID_CELL_PAD_LAST,
|
||||
},
|
||||
},
|
||||
],
|
||||
[copiedValue, onCopy],
|
||||
[copiedValue, onCopy, onSelect],
|
||||
)
|
||||
|
||||
const rowSelection = useMemo(
|
||||
@@ -228,7 +240,9 @@ function CommunitiesDataGrid({
|
||||
|
||||
const table = useTable({
|
||||
features: dataGridFeatures,
|
||||
manualPagination: true,
|
||||
manualPagination: false,
|
||||
autoResetPageIndex: false,
|
||||
initialState: { pagination: { pageIndex: 0, pageSize: 50 }, columnPinning: { start: ["value"], end: [] } },
|
||||
data: communities,
|
||||
columns,
|
||||
getRowId: (row) => row.id,
|
||||
@@ -236,6 +250,12 @@ function CommunitiesDataGrid({
|
||||
state: { rowSelection },
|
||||
})
|
||||
|
||||
const { pageIndex, pageSize } = table.state.pagination
|
||||
useEffect(() => {
|
||||
const lastPage = Math.max(0, Math.ceil(communities.length / pageSize) - 1)
|
||||
if (pageIndex > lastPage) table.setPageIndex(lastPage)
|
||||
}, [communities.length, pageIndex, pageSize, table])
|
||||
|
||||
if (communities.length === 0) {
|
||||
return (
|
||||
<EmptyState
|
||||
@@ -250,6 +270,8 @@ function CommunitiesDataGrid({
|
||||
<DataGridShell
|
||||
table={table}
|
||||
recordCount={communities.length}
|
||||
pagination={communities.length > 50 || pageSize !== 50}
|
||||
tableLayout={{ dense: true, columnsPinnable: true }}
|
||||
onRowClick={(row) => onSelect(row)}
|
||||
tableClassNames={{
|
||||
headerRow: "border-b border-border",
|
||||
@@ -259,7 +281,11 @@ function CommunitiesDataGrid({
|
||||
"[&:has([data-community-disabled=true])]:opacity-50",
|
||||
),
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<DataGridContainer border={false} className="w-full rounded-none border-0">
|
||||
<DataGridScrollArea orientation="horizontal"><DataGridTable /></DataGridScrollArea>
|
||||
</DataGridContainer>
|
||||
</DataGridShell>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -15,11 +15,10 @@
|
||||
|
||||
## Оставшиеся специализированные grids
|
||||
|
||||
Аудит12.3.36 от2026-10-04: точный поиск `manualPagination: true` в components/data-grids —15 файлов. Это не15 дефектов: есть фиксированный источник, demo, неподключённый компонент и ограниченные срезы. Файлы ниже сопоставлены с действующими JSX-потребителями; ограничения установлены чтением кода, не нагрузочной приёмкой API.
|
||||
Аудит12.3.36 от2026-10-04: точный поиск `manualPagination: true` в components/data-grids —14 файлов после12.3.38. Это не15 дефектов: есть фиксированный источник, demo, неподключённый компонент и ограниченные срезы. Файлы ниже сопоставлены с действующими JSX-потребителями; ограничения установлены чтением кода, не нагрузочной приёмкой API.
|
||||
|
||||
| Файл | Источник и необходимая проверка |
|
||||
| --- | --- |
|
||||
| [communities-data-grid.tsx](../components/data-grids/communities-data-grid.tsx) | Evo catalog snapshot; полный доступный список/поиск/выбор. Первый live кандидат1000. |
|
||||
| [data-collection-scheduler-data-grid.tsx](../components/data-grids/data-collection-scheduler-data-grid.tsx) | 11 фиксированных SCHEDULER_JOB_KEYS (lib/scheduler-settings.ts); UI map этих ключей. Fixture1000 не соответствует источнику. |
|
||||
| [filters-data-grid.tsx](../components/data-grids/filters-data-grid.tsx) | currentRules/filteredRules + orderedRules: move up/down относятся ко всему исходному порядку. Проверить границу страниц и точный target. |
|
||||
| [firewall-rules-data-grid.tsx](../components/data-grids/firewall-rules-data-grid.tsx) | filteredRules; reorderable отключает сортировку, DnD dataIds включает исходный массив. Ограничение DOM требует отдельной проверки cross-page порядка. |
|
||||
@@ -35,7 +34,7 @@
|
||||
| [subusers-data-grid.tsx](../components/data-grids/subusers-data-grid.tsx) | Поиск импортов/JSX в app/components не нашёл потребителя: компонент экспортируется, но не подключён. Не доказывает дефект экрана /users. |
|
||||
| [traffic-flows-data-grid.tsx](../components/data-grids/traffic-flows-data-grid.tsx) | FlowAnalyticsPanel/conversationsList: backend traffic-flow-analytics.ts top=min(50,max(10,settings.topN)), slice(0,top). Это top slice, не полный каталог/серверная pagination. Проверить сообщение об ограничении50 и связывание с графиками. |
|
||||
|
||||
Очередь: Communities → recursive groups/details → Filters порядок → Firewall/DnD → OSPF таблица+topology → optimizer preview/комбинаторика. Scheduler11, traffic cap50 и commRecs24 проверять на реальной границе, не искусственно подставлять1000 в недоступный DTO. Demo и неиспользуемый Subusers отделены от live приёмки. Все новые профили должны сохранять search/sort/refresh/clamp, точный target и права,320px и URL scope.
|
||||
Очередь: clipboard Communities → recursive groups/details → Filters порядок → Firewall/DnD → OSPF таблица+topology → optimizer preview/комбинаторика. Scheduler11, traffic cap50 и commRecs24 проверять на реальной границе, не искусственно подставлять1000 в недоступный DTO. Demo и неиспользуемый Subusers отделены от live приёмки. Все новые профили должны сохранять search/sort/refresh/clamp, точный target и права,320px и URL scope.
|
||||
|
||||
Принятые после ранней карты: OSPF routes/BFD, WireGuard interfaces/peers/detail, VXLAN/GRE tunnels. Pagination сохраняет полный DTO в памяти и не доказывает серверную производительность. Текущий журнал: [ux-ui-progress.md](ux-ui-progress.md).
|
||||
|
||||
@@ -78,3 +77,5 @@ Statistics pivot в backend/src/services/statistics-aggregate.ts огранич
|
||||
Аудит источников12.4.5:RouteOptimizerCommRecsDataGrid получает flatRules.filter(sourceServerId===home.id).slice(0,24), максимум24 на один HomeCard; это не cap всей страницы с несколькими HomeCard. WireGuard /wireguard агрегирует полный interfaces/peers устройств без page/limit;1000-профиль применим к реальному пути, а WireGuardPeersDetail дополнительно рендерит полный peers массив раскрытого интерфейса. Эти границы установлены чтением кода, не внешней нагрузочной приёмкой.
|
||||
|
||||
12.3.28–12.3.35: WireGuard interfaces1000→50/2136 nodes/1573ms, peers1000→50/2596 nodes/1369ms, раскрытые peers1000→50/2388 nodes; VXLAN1000→50/2137 nodes/1249ms; GRE1000→50/2748 nodes/1609ms. Baseline до по этим экранам не замерялся; сравнение latency между разными dev прогонами не является ускорением production. Evidence и проверки по каждому блоку в ux-ui-stage-12.md.
|
||||
|
||||
12.3.38: Communities1000/190573 JSON bytes →50 DOM rows/2216 page nodes/1206ms dev ready.19 browser/type/lint/build прошли.14 manualPagination:true остаются; обновления/selection по ID подтверждены локально.
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
## Актуальная точка продолжения — 2026-10-04
|
||||
|
||||
Этап 11Б завершён в границах локальной приёмки (11Б.7); внешний выпуск не проверен. 12.1 принят локально: доступность оболочки/клавиатура/health в описанных сценариях. 12.2.1 принят локально: strict BGP catalog/partial/nullable counters. 12.2.2 принят локально: strict traffic DTO и агрегация без выдуманных точек. 12.2.3 принят локально: observations для server traffic от backend до dashboard. 12.2.4 принят локально: strict servers/probes и unknown server status. 12.2.5 принят локально: реальные ping samples/unknown/ось RTT/Recharts. 12.2.6 принят локально: strict GRE/WireGuard/VXLAN и device failures. 12.2.7 принят локально: strict Internet path snapshot/fallback. 12.2.8 принят локально: подтверждение полного backend сбора. 12.2.9 принят локально: метрики участков и наблюдаемый WAN без эвристической подмены. 12.3.1 принят локально:1000 серверов/штатная пагинация. 12.3.2 принят локально: пагинация CompactDataGrid/1000 строк статистики. 12.3.3 принят локально: порционный DOM очереди проблем. 12.3.4 принят локально: большой BGP каталог/страница при обновлении. Production build и123 профильных browser приняты локально на3978976. 12.2.10 принят локально: обязательные RouterOS поля. 12.3.5 принят локально: страница CompactDataGrid сохраняется при опросе,89 browser и повторный production build прошли. 12.3.6 сохранён: baseline1000 speed probes (ограничение DOM пока не выполнено). 12.3.7 принят локально:50 строк speed-проб/сохранение страницы/глобальная сортировка/1000→1; каталог1001 устройства остаётся в DOM. 12.3.8 принят локально:50 строк ресурсов, URL/поиск/сохранение страницы/1000→1; исправлена pagination оболочки с children, production build и8 больших browser прошли. 12.3.9 принят локально:порционное раскрытие1000 предупреждений ресурсов/клавиатура/axe; исходная ошибка ESLint speedDraft подтверждена. 12.3.10 принят локально:недоступные интерфейсы очищаются в обработчиках,54 browser/полный ESLint/production build прошли. 12.3.11 принят локально:ServerTileRail50 устройств/URL/поиск/клавиатура/две площадки/mobile Sheet/axe обе темы;47 browser и production build прошли. 12.3.12 принят локально:BGP ручная страница при выбранной URL сессии/refresh/reload,16 browser/build прошли. 12.4.1 записан:main1091 сценарий/1088 passed/3 устаревших fixtures; оба исправленных файла6 passed. 12.4.2 принят локально:context205/24 файла и unit28 прошли. 12.3.13 принят локально:wide pivot51×16/ScrollArea/axe/24+2 browser/builds. 12.3.14 принят локально:history200/50 DOM/54+1 browser/build. 12.3.15 принят локально:live расписание1000/page20 error cache/15 browser. 12.3.16 принят локально:certificates1000/50 DOM/refresh деталей/19+3 browser/build. 12.3.17 принят локально:pinning идентификатора/видимость значений/29 browser/build. 12.3.18 принят локально:users1000/поиск всего списка/детали и ошибка refresh/32 browser/build. 12.3.19 принят локально:backups1000/все мобильные действия/возврат focus/39+4 browser/build. 12.3.20 принят локально:независимые speed GET/cache/freshness/58+4 browser/build. 12.3.21 принят локально:strict history DTO/null rates от DB до UI/60+5 browser/unit4/builds. 12.4.3 принят локально:users/backups delta context36 на a558412. 12.3.22 принят локально:shared speed-probes DTO/context8/60+6+47 browser/unit5/builds. 12.3.23 принят локально:strict write DTO/atomic replacement/confirmed scheduler warning/65 browser/HTTP DI JWT/builds. 12.3.24 принят локально:OSPF routes1000/50 DOM/page20/cache/320px/22+1 browser/build. 12.3.25 принLine truncated
|
||||
Этап 11Б завершён в границах локальной приёмки (11Б.7); внешний выпуск не проверен. 12.1 принят локально: доступность оболочки/клавиатура/health в описанных сценариях. 12.2.1 принят локально: strict BGP catalog/partial/nullable counters. 12.2.2 принят локально: strict traffic DTO и агрегация без выдуманных точек. 12.2.3 принят локально: observations для server traffic от backend до dashboard. 12.2.4 принят локально: strict servers/probes и unknown server status. 12.2.5 принят локально: реальные ping samples/unknown/ось RTT/Recharts. 12.2.6 принят локально: strict GRE/WireGuard/VXLAN и device failures. 12.2.7 принят локально: strict Internet path snapshot/fallback. 12.2.8 принят локально: подтверждение полного backend сбора. 12.2.9 принят локально: метрики участков и наблюдаемый WAN без эвристической подмены. 12.3.1 принят локально:1000 серверов/штатная пагинация. 12.3.2 принят локально: пагинация CompactDataGrid/1000 строк статистики. 12.3.3 принят локально: порционный DOM очереди проблем. 12.3.4 принят локально: большой BGP каталог/страница при обновлении. Production build и123 профильных browser приняты локально на3978976. 12.2.10 принят локально: обязательные RouterOS поля. 12.3.5 принят локально: страница CompactDataGrid сохраняется при опросе,89 browser и повторный production build прошли. 12.3.6 сохранён: baseline1000 speed probes (ограничение DOM пока не выполнено). 12.3.7 принят локально:50 строк speed-проб/сохранение страницы/глобальная сортировка/1000→1; каталог1001 устройства остаётся в DOM. 12.3.8 принят локально:50 строк ресурсов, URL/поиск/сохранение страницы/1000→1; исправлена pagination оболочки с children, production build и8 больших browser прошли. 12.3.9 принят локально:порционное раскрытие1000 предупреждений ресурсов/клавиатура/axe; исходная ошибка ESLint speedDraft подтверждена. 12.3.10 принят локально:недоступные интерфейсы очищаются в обработчиках,54 browser/полный ESLint/production build прошли. 12.3.11 принят локально:ServerTileRail50 устройств/URL/поиск/клавиатура/две площадки/mobile Sheet/axe обе темы;47 browser и production build прошли. 12.3.12 принят локально:BGP ручная страница при выбранной URL сессии/refresh/reload,16 browser/build прошли. 12.4.1 записан:main1091 сценарий/1088 passed/3 устаревших fixtures; оба исправленных файла6 passed. 12.4.2 принят локально:context205/24 файла и unit28 прошли. 12.3.13 принят локально:wide pivot51×16/ScrollArea/axe/24+2 browser/builds. 12.3.14 принят локально:history200/50 DOM/54+1 browser/build. 12.3.15 принят локально:live расписание1000/page20 error cache/15 browser. 12.3.16 принят локально:certificates1000/50 DOM/refresh деталей/19+3 browser/build. 12.3.17 принят локально:pinning идентификатора/видимость значений/29 browser/build. 12.3.18 принят локально:users1000/поиск всего списка/детали и ошибка refresh/32 browser/build. 12.3.19 принят локально:backups1000/все мобильные действия/возврат focus/39+4 browser/build. 12.3.20 принят локально:независимые speed GET/cache/freshness/58+4 browser/build. 12.3.21 принят локально:strict history DTO/null rates от DB до UI/60+5 browser/unit4/builds. 12.4.3 принят локально:users/backups delta context36 на a558412. 12.3.22 принят локально:shared speed-probes DTO/context8/60+6+47 browser/unit5/builds. 12.3.23 принят локально:strict write DTO/atomic replacement/confirmed scheduler warning/65 browser/HTTP DI JWT/builds. 12.3.24 принят локально:OSPF routes1000/50 DOM/page20/cache/320px/22+1 browser/build. 12.3.25 принLine truncated
|
||||
|
||||
Реальные pg_dump/pg_restore, PG rollback и остановка UDP worker требуют внешней приёмки. Датированные продолжения ниже — история; точный следующий шаг также указан в начале [журнала](ux-ui-progress.md).
|
||||
|
||||
|
||||
@@ -31,6 +31,14 @@
|
||||
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Завершён в границах локальной приёмки 2026-10-03; ограничения и evidence в11Б.7 |
|
||||
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | В работе;12.1/12.2.1–12.2.10 и12.3.1–12.3.5 и12.3.7–12.3.12 приняты в описанных локальных границах;12.4.1 main регрессия и профильный повтор записаны;12.4.2 context205 и unit28 прошли;12.3.13–12.3.25 приняты локально в описанных границах;12.4.4 context213 и12.4.5 main1110 прошли, 12.3.26–12.3.33 приняты локально; 12.3.34 VXLAN и12.3.35 GRE tunnels приняты; остаются network grids/общая регрессия после delta/внешняя приёмка |
|
||||
|
||||
## 12.3.38 — большой каталог Communities (2026-10-04)
|
||||
|
||||
ReUI pagination50, global sort/search, сохранение страницы при refresh и clamp при уменьшении списка. Закреплено значение community; копирование и явная кнопка деталей имеют44px/доступные подписи. Выбор по ID не привязан к странице и сохраняется при сортировке/обновлении; удалённая запись закрывает детали. Кнопка деталей работает с клавиатуры. Права/источник/отсутствующие prefix counts сохранены.
|
||||
|
||||
19 browser (large Communities и Evolution consumers) прошли:1000/190573 JSON bytes →50 rows/2216 page nodes/1206ms dev ready; page20/refresh/search1000/global sort/cache/320px/pinning/axe, clipboard stub,1000→1→0/selection. TypeScript без incremental/ESLint прошли. Первый production build не получил Google Fonts, повторная обычная сборка успешно собрала34 страницы. [Снимок320px](ux-ui/screens/large-communities-320.png) просмотрен. Реальная интеграция Evolution и системный clipboard не проверены. Поиск/выбор Communities пока локальны, восстановления из URL нет.
|
||||
|
||||
Осталось14 manualPagination:true specialized grids. Обнаружено прежнее ложное подтверждение clipboard: handleCopy ставит copied до завершения writeText и игнорирует reject. Следующий блок12.3.39: исправить ack/error/pending копирования и поздние ответы при смене источника; затем12.3.40 recursive groups+endpoints. Общая main/context регрессия после delta и внешняя12.5 отдельно. Коммит локальный без push.
|
||||
|
||||
## 12.3.37 — границы выборки сессий трафика (2026-10-04)
|
||||
|
||||
FlowAnalyticsPanel сообщает фактическое число строк после фильтра и размер полученной выборки, объясняет top по объёму до50 и локальную область фильтра. Пустой выбранный фильтр имеет собственную причину, вместо общей parent emptyHint; сводка общего conversations не подменяется числом строк. Backend cap/read/network операции не изменены.
|
||||
|
||||
@@ -488,3 +488,12 @@ FlowAnalyticsPanel сообщает фактическое число строк
|
||||
|
||||
Следующий блок12.3.38: Communities1000 с полным поиском/sort/refresh/выбором/320px по актуальной performance карте.15 manualPagination:true остаются, traffic cap50 не требует фиктивной пагинации. Общая main/context после delta и внешняя12.5 отдельно; реальные RouterOS/PG не проверены. Коммит локальный без push.
|
||||
|
||||
|
||||
## 12.3.38 — большой каталог Communities (2026-10-04)
|
||||
|
||||
ReUI pagination50, global sort/search, сохранение страницы при refresh и clamp при уменьшении списка. Закреплено значение community; копирование и явная кнопка деталей имеют44px/доступные подписи. Выбор по ID не привязан к странице и сохраняется при сортировке/обновлении; удалённая запись закрывает детали. Кнопка деталей работает с клавиатуры. Права/источник/отсутствующие prefix counts сохранены.
|
||||
|
||||
19 browser (large Communities и Evolution consumers) прошли:1000/190573 JSON bytes →50 rows/2216 page nodes/1206ms dev ready; page20/refresh/search1000/global sort/cache/320px/pinning/axe, clipboard stub,1000→1→0/selection. TypeScript без incremental/ESLint прошли. Первый production build не получил Google Fonts, повторная обычная сборка успешно собрала34 страницы. [Снимок320px](ux-ui/screens/large-communities-320.png) просмотрен. Реальная интеграция Evolution и системный clipboard не проверены. Поиск/выбор Communities пока локальны, восстановления из URL нет.
|
||||
|
||||
Осталось14 manualPagination:true specialized grids. Обнаружено прежнее ложное подтверждение clipboard: handleCopy ставит copied до завершения writeText и игнорирует reject. Следующий блок12.3.39: исправить ack/error/pending копирования и поздние ответы при смене источника; затем12.3.40 recursive groups+endpoints. Общая main/context регрессия после delta и внешняя12.5 отдельно. Коммит локальный без push.
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
@@ -0,0 +1,51 @@
|
||||
import { test, expect } from "@playwright/test"
|
||||
import axe from "axe-core"
|
||||
import { evoCatalog, evoSettings } from "./fixtures/evobgp"
|
||||
import { parseEvoBgpCatalog } from "../../lib/evobgp-catalog"
|
||||
|
||||
test("Communities1000: full search/sort/page/selection/cache/320px", async ({ page }, info) => {
|
||||
test.setTimeout(90000)
|
||||
let communities = Array.from({ length: 1000 }, (_, i) => ({ id: "c" + (i + 1), value: "64512:" + (i + 1), name: "community-" + String(i + 1).padStart(4, "0"), description: "Описание", type: "custom", filterIds: [], serverCount: i + 1, prefixCount: i + 1, action: "permit", enabled: true }))
|
||||
let failed = false, calls = 0
|
||||
const writes: string[] = []
|
||||
await page.addInitScript(() => Object.defineProperty(navigator, "clipboard", { value: { writeText: async (value: string) => { (window as unknown as { copied: string }).copied = value } } }))
|
||||
await page.route("**/health", r => r.fulfill({ json: { ok: true } }))
|
||||
await page.route("**/api/**", r => {
|
||||
const p = new URL(r.request().url()).pathname
|
||||
if (r.request().method() !== "GET" && p !== "/api/evobgp/catalog") writes.push(p)
|
||||
if (p === "/api/evobgp/catalog") { calls++; return r.fulfill(failed ? { status: 503, json: { error: "Каталог недоступен тест" } } : { json: parseEvoBgpCatalog({ ...evoCatalog, communities }) }) }
|
||||
return r.fulfill({ json: p === "/api/auth/config" ? { required: false } : p === "/api/evobgp/settings" ? evoSettings : {} })
|
||||
})
|
||||
const started = Date.now(); await page.goto("/communities")
|
||||
const rows = page.locator("tr[data-row-id]"); await expect(rows).toHaveCount(50)
|
||||
const profile = { count: communities.length, jsonBytes: Buffer.byteLength(JSON.stringify(communities)), rows: await rows.count(), nodes: await page.locator("*").count(), readyMs: Date.now() - started }
|
||||
console.log("Communities profile", JSON.stringify(profile)); await info.attach("profile", { body: JSON.stringify(profile), contentType: "application/json" })
|
||||
const next = page.getByRole("button", { name: "Следующая страница", exact: true })
|
||||
for (let i = 0; i < 19; i++) await next.click()
|
||||
await expect(next).toBeDisabled(); await expect(rows.first()).toContainText("community-0951")
|
||||
const refresh = page.getByRole("button", { name: "Обновить каталог EvoBGP", exact: true })
|
||||
const before = calls; await refresh.click(); await expect.poll(() => calls).toBeGreaterThan(before); await expect(rows.first()).toContainText("community-0951")
|
||||
const search = page.getByPlaceholder("Поиск community…")
|
||||
await search.fill("community-1000"); await expect(rows).toHaveCount(1)
|
||||
const detailButton = rows.first().getByRole("button", { name: "Детали community: 64512:1000", exact: true })
|
||||
await detailButton.focus(); await page.keyboard.press("Enter")
|
||||
await expect(page.getByText("Скопировать значение", { exact: true })).toBeVisible()
|
||||
await expect(page.getByRole("button", { name: "Изменить community", exact: true })).toBeDisabled()
|
||||
await search.fill(""); await expect(rows).toHaveCount(50)
|
||||
await page.getByRole("table").getByRole("button", { name: "Серверов", exact: true }).click(); await expect(rows.first()).toContainText("community-1000")
|
||||
await next.click(); const previous = await rows.first().getAttribute("data-row-id")
|
||||
failed = true; await refresh.click(); await expect(page.getByText("Каталог не обновлён; показаны последние данные", { exact: true })).toBeVisible()
|
||||
await expect(rows.first()).toHaveAttribute("data-row-id", previous!)
|
||||
await page.setViewportSize({ width: 320, height: 1000 })
|
||||
const copy = rows.first().getByRole("button", { name: /Скопировать community:/ })
|
||||
await copy.scrollIntoViewIfNeeded(); const viewport = page.locator('[data-slot="data-grid"] [data-slot="scroll-area-viewport"]')
|
||||
await viewport.evaluate(el => { el.scrollLeft = el.scrollWidth }); await expect(copy).toBeInViewport()
|
||||
await copy.click(); expect(await page.evaluate(() => (window as unknown as { copied: string }).copied)).toBe("64512:950")
|
||||
await expect(page.getByText("Скопировать значение", { exact: true })).toHaveCount(1)
|
||||
await expect.poll(() => page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(320)
|
||||
await page.addScriptTag({ content: axe.source }); expect((await page.evaluate(async () => (window as unknown as { axe: typeof axe }).axe.run(document.querySelector('[data-slot="data-grid"]')!, { runOnly: { type: "tag", values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"] } }))).violations).toEqual([])
|
||||
await page.addStyleTag({ content: "nextjs-portal{display:none!important}" }); await page.screenshot({ path: info.outputPath("communities-320.png"), animations: "disabled" })
|
||||
failed = false; communities = [communities[999]]; await refresh.click(); await expect(rows).toHaveCount(1); await expect(rows.first()).toContainText("community-1000")
|
||||
communities = []; await refresh.click(); await expect(rows).toHaveCount(0); await expect(page.getByText("Выберите community", { exact: false })).toBeVisible()
|
||||
expect(writes).toEqual([])
|
||||
})
|
||||
Reference in new issue
Block a user