@oneuptime/common 12.0.9 → 12.0.11
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/Models/DatabaseModels/NetworkDevice.ts +44 -0
- package/Models/DatabaseModels/NetworkDeviceLink.ts +116 -0
- package/Models/DatabaseModels/RunnerJob.ts +17 -2
- package/Server/API/LlmProviderAPI.ts +8 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.ts +52 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/NetworkDeviceLabelRuleEngineService.ts +240 -0
- package/Server/Services/NetworkDeviceLinkService.ts +338 -2
- package/Server/Services/NetworkDeviceService.ts +265 -12
- package/Server/Services/RunnerJobService.ts +24 -1
- package/Server/Types/Billing/MeteredPlan/AllMeteredPlans.ts +19 -5
- package/Server/Types/Database/JSONColumnQuery.ts +42 -4
- package/Server/Utils/LLM/LLMService.ts +108 -19
- package/Server/Utils/Monitor/MonitorAlert.ts +22 -115
- package/Server/Utils/Monitor/MonitorIncident.ts +7 -337
- package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +27 -2
- package/Server/Utils/Monitor/SeriesResourceLabels.ts +32 -0
- package/Server/Utils/Monitor/SeriesResourceLinker.ts +469 -0
- package/Server/Utils/SSRFProtection.ts +5 -4
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +186 -38
- package/Tests/App/AdminDashboard/AdminUsersBulkAddToProject.test.tsx +955 -0
- package/Tests/App/AdminDashboard/BulkAddUsersToProject.test.ts +778 -0
- package/Tests/App/AdminDashboard/BulkAddUsersToProjectModal.test.tsx +1051 -0
- package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +420 -112
- package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +81 -6
- package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +543 -0
- package/Tests/App/Dashboard/PayAsYouGoFreePlan.test.ts +125 -0
- package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +612 -0
- package/Tests/App/Dashboard/SloWidgetChartRendering.test.tsx +536 -0
- package/Tests/App/Dashboard/UserElementEmail.test.tsx +447 -0
- package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +284 -0
- package/Tests/Models/AnalyticsModels/CommonModel.test.ts +312 -0
- package/Tests/Server/Infrastructure/Postgres/DeviceRoleAndDeclaredLinkParentMigration.test.ts +250 -0
- package/Tests/Server/Infrastructure/Postgres/EpisodeMemberNotifyIndexesMigration.test.ts +46 -18
- package/Tests/Server/Services/NetworkDeviceLabelRuleRun.test.ts +618 -0
- package/Tests/Server/Services/NetworkDeviceLinkParentValidation.test.ts +1043 -0
- package/Tests/Server/Services/NetworkSiteAssignmentRuleRun.test.ts +706 -0
- package/Tests/Server/Services/RunnerJobEnqueue.test.ts +412 -0
- package/Tests/Server/Types/Billing/PayAsYouGoMeteredPricing.test.ts +125 -0
- package/Tests/Server/Types/Database/JSONColumnQuery.test.ts +508 -0
- package/Tests/Server/Utils/AI/LLMServiceRequestPolicy.test.ts +2 -2
- package/Tests/Server/Utils/AI/LLMServiceRetryPolicy.test.ts +450 -0
- package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +556 -0
- package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +547 -0
- package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +30 -0
- package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +699 -0
- package/Tests/Server/Utils/SeriesResourceLabels.test.ts +55 -0
- package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +668 -0
- package/Tests/Types/API/URLMalformedTolerance.test.ts +301 -0
- package/Tests/Types/API/URLTelScheme.test.ts +261 -0
- package/Tests/Types/API/URLUnsupportedScheme.test.ts +148 -0
- package/Tests/Types/Billing/PayAsYouGoPricing.test.ts +64 -0
- package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +107 -0
- package/Tests/Types/Monitor/MonitorType.test.ts +62 -0
- package/Tests/Types/NetworkAutomation/RuleRunResult.test.ts +184 -0
- package/Tests/Types/Runbook/RunbookStepType.test.ts +124 -0
- package/Tests/Types/Workflow/BaseModelComponents.test.ts +17 -0
- package/Tests/UI/Components/Button.test.tsx +279 -0
- package/Tests/UI/Components/Charts/ChartXAxisDataKey.test.tsx +256 -0
- package/Tests/UI/Components/Charts/DataPointUtil.test.ts +323 -0
- package/Tests/UI/Components/DictionaryValuePlaceholderAndSuggestions.test.tsx +595 -0
- package/Tests/UI/Components/Icon.test.tsx +57 -0
- package/Tests/UI/Components/List.test.tsx +20 -3
- package/Tests/UI/Components/LogsSavedViewsDropdown.test.tsx +304 -0
- package/Tests/UI/Components/LogsViewerClearSavedView.test.tsx +179 -0
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +349 -1
- package/Tests/UI/Components/Pagination.test.tsx +721 -146
- package/Tests/UI/Components/PaginationIntegration.test.tsx +386 -0
- package/Tests/UI/Components/PaginationUtil.test.ts +417 -0
- package/Tests/UI/Components/TelemetrySavedViewsDropdown.test.tsx +343 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnControl.test.ts +289 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnOperators.test.ts +271 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnRow.test.ts +119 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.test.ts +968 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditor.render.test.tsx +766 -0
- package/Tests/UI/Utils/RunNetworkRule.test.ts +320 -0
- package/Tests/Utils/APIRetryPolicy.test.ts +823 -0
- package/Tests/Utils/Monitor/NetworkDeviceRoleUtil.test.ts +502 -0
- package/Tests/Utils/Monitor/NetworkTopologyDeclaredHierarchy.test.ts +759 -0
- package/Tests/Utils/NetworkAutomation/RuleRunSummary.test.ts +286 -0
- package/Tests/Utils/Schema/BaseSchema.test.ts +651 -0
- package/Types/API/Protocol.ts +9 -0
- package/Types/API/URL.ts +286 -20
- package/Types/Billing/PayAsYouGoPricing.ts +47 -0
- package/Types/CustomField/CustomFieldType.ts +28 -0
- package/Types/Monitor/MonitorType.ts +11 -0
- package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +13 -0
- package/Types/NetworkAutomation/RuleRunResult.ts +121 -0
- package/Types/Runbook/RunbookStepType.ts +23 -0
- package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +7 -0
- package/UI/Components/Button/Button.tsx +14 -22
- package/UI/Components/Charts/Area/AreaChart.tsx +4 -2
- package/UI/Components/Charts/Bar/BarChart.tsx +4 -2
- package/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.ts +13 -0
- package/UI/Components/Charts/Line/LineChart.tsx +4 -2
- package/UI/Components/Charts/Types/XAxis/XAxis.ts +6 -0
- package/UI/Components/Charts/Utils/DataPoint.ts +15 -12
- package/UI/Components/Dictionary/Dictionary.tsx +4 -39
- package/UI/Components/Icon/Icon.tsx +1 -1
- package/UI/Components/LogsViewer/LogsViewer.tsx +5 -1
- package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +8 -0
- package/UI/Components/LogsViewer/components/LogsPagination.tsx +29 -91
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +2 -0
- package/UI/Components/LogsViewer/components/SavedViewsDropdown.tsx +66 -1
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +55 -0
- package/UI/Components/Pagination/Pagination.tsx +373 -328
- package/UI/Components/Pagination/PaginationUtil.ts +263 -0
- package/UI/Components/Pagination/Screenshots/README.md +15 -0
- package/UI/Components/Pagination/Screenshots/pagination-before-after.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-go-to-page-modal.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-long-list.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-mobile.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-telemetry.png +0 -0
- package/UI/Components/TelemetryViewer/components/SavedViewsDropdown.tsx +66 -1
- package/UI/Components/TelemetryViewer/components/TelemetryPagination.tsx +38 -92
- package/UI/Components/TextArea/TextArea.tsx +3 -0
- package/UI/Components/Workflow/ArgumentsForm.tsx +18 -1
- package/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.tsx +177 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.tsx +243 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnControl.ts +255 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.tsx +178 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnOperators.ts +206 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnRow.ts +157 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.ts +517 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnValueInput.tsx +482 -0
- package/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.tsx +131 -0
- package/UI/Components/Workflow/ColumnEditor/ModelRecordForm.tsx +163 -0
- package/UI/Components/Workflow/ModelColumnEditor.tsx +284 -100
- package/UI/Utils/NetworkAutomation/RunNetworkRule.ts +122 -0
- package/Utils/API.ts +243 -36
- package/Utils/Monitor/NetworkDeviceRoleUtil.ts +45 -0
- package/Utils/Monitor/NetworkTopologyUtil.ts +42 -2
- package/Utils/NetworkAutomation/RuleRunSummary.ts +147 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +45 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js +115 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RunnerJob.js +17 -2
- package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
- package/build/dist/Server/API/LlmProviderAPI.js +9 -1
- package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.js +33 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js +202 -0
- package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLinkService.js +253 -1
- package/build/dist/Server/Services/NetworkDeviceLinkService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +223 -12
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/RunnerJobService.js +20 -2
- package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
- package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js +12 -5
- package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js.map +1 -1
- package/build/dist/Server/Types/Database/JSONColumnQuery.js +40 -4
- package/build/dist/Server/Types/Database/JSONColumnQuery.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +78 -22
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +22 -98
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +7 -254
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js +14 -2
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +27 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +285 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -0
- package/build/dist/Server/Utils/SSRFProtection.js +5 -4
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +128 -35
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
- package/build/dist/Types/API/Protocol.js +9 -0
- package/build/dist/Types/API/Protocol.js.map +1 -1
- package/build/dist/Types/API/URL.js +241 -18
- package/build/dist/Types/API/URL.js.map +1 -1
- package/build/dist/Types/Billing/PayAsYouGoPricing.js +41 -0
- package/build/dist/Types/Billing/PayAsYouGoPricing.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldType.js +26 -0
- package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorType.js +10 -0
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js +39 -0
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookStepType.js +21 -0
- package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
- package/build/dist/UI/Components/Button/Button.js +10 -16
- package/build/dist/UI/Components/Button/Button.js.map +1 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +2 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +2 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.js +12 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.js.map +1 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js +2 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js +17 -10
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
- package/build/dist/UI/Components/Dictionary/Dictionary.js +4 -18
- package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +1 -1
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +3 -3
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +5 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js +14 -44
- package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js +20 -2
- package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +42 -0
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/Pagination/Pagination.js +174 -199
- package/build/dist/UI/Components/Pagination/Pagination.js.map +1 -1
- package/build/dist/UI/Components/Pagination/PaginationUtil.js +160 -0
- package/build/dist/UI/Components/Pagination/PaginationUtil.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js +20 -2
- package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryPagination.js +23 -47
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryPagination.js.map +1 -1
- package/build/dist/UI/Components/TextArea/TextArea.js +1 -1
- package/build/dist/UI/Components/TextArea/TextArea.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js +14 -2
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.js +86 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.js +119 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnControl.js +206 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnControl.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.js +57 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnOperators.js +150 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnOperators.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRow.js +67 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRow.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.js +309 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnValueInput.js +204 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnValueInput.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js +66 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelRecordForm.js +83 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelRecordForm.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +187 -68
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -1
- package/build/dist/UI/Utils/NetworkAutomation/RunNetworkRule.js +75 -0
- package/build/dist/UI/Utils/NetworkAutomation/RunNetworkRule.js.map +1 -0
- package/build/dist/Utils/API.js +175 -34
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +37 -0
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +25 -3
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/NetworkAutomation/RuleRunSummary.js +78 -0
- package/build/dist/Utils/NetworkAutomation/RuleRunSummary.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,238 +1,747 @@
|
|
|
1
1
|
import Pagination, {
|
|
2
2
|
ComponentProps,
|
|
3
3
|
} from "../../../UI/Components/Pagination/Pagination";
|
|
4
|
-
import { describe, expect, jest } from "@jest/globals";
|
|
4
|
+
import { describe, expect, it, jest } from "@jest/globals";
|
|
5
5
|
import "@testing-library/jest-dom";
|
|
6
|
-
import { fireEvent, render, screen
|
|
6
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
7
7
|
import React from "react";
|
|
8
8
|
import getJestMockFunction, { MockFunction } from "../../../Tests/MockType";
|
|
9
9
|
|
|
10
|
+
type RenderPaginationFunction = (
|
|
11
|
+
overrides?: Partial<ComponentProps>,
|
|
12
|
+
) => MockFunction;
|
|
13
|
+
|
|
14
|
+
const baseProps: ComponentProps = {
|
|
15
|
+
currentPageNumber: 1,
|
|
16
|
+
totalItemsCount: 240,
|
|
17
|
+
itemsOnPage: 10,
|
|
18
|
+
onNavigateToPage: jest.fn(),
|
|
19
|
+
isLoading: false,
|
|
20
|
+
isError: false,
|
|
21
|
+
singularLabel: "Monitor",
|
|
22
|
+
pluralLabel: "Monitors",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/*
|
|
26
|
+
* Renders the control and hands back the navigate spy, which is what almost
|
|
27
|
+
* every assertion below is really about.
|
|
28
|
+
*/
|
|
29
|
+
const renderPagination: RenderPaginationFunction = (
|
|
30
|
+
overrides?: Partial<ComponentProps>,
|
|
31
|
+
): MockFunction => {
|
|
32
|
+
const onNavigateToPage: MockFunction = getJestMockFunction();
|
|
33
|
+
|
|
34
|
+
render(
|
|
35
|
+
<Pagination
|
|
36
|
+
{...baseProps}
|
|
37
|
+
onNavigateToPage={onNavigateToPage}
|
|
38
|
+
{...overrides}
|
|
39
|
+
/>,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
return onNavigateToPage;
|
|
43
|
+
};
|
|
44
|
+
|
|
10
45
|
describe("Pagination", () => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
totalItemsCount: 20,
|
|
15
|
-
itemsOnPage: 10,
|
|
16
|
-
onNavigateToPage: jest.fn(),
|
|
17
|
-
isLoading: false,
|
|
18
|
-
isError: false,
|
|
19
|
-
singularLabel: "Item",
|
|
20
|
-
pluralLabel: "Items",
|
|
21
|
-
};
|
|
46
|
+
describe("summary", () => {
|
|
47
|
+
it("prints the range and the total", () => {
|
|
48
|
+
renderPagination();
|
|
22
49
|
|
|
23
|
-
|
|
50
|
+
expect(screen.getByTestId("pagination-summary")).toHaveTextContent(
|
|
51
|
+
"Showing 1-10 of 240 monitors",
|
|
52
|
+
);
|
|
53
|
+
});
|
|
24
54
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
).toBeInTheDocument();
|
|
28
|
-
});
|
|
55
|
+
it("offsets the range by the pages already passed", () => {
|
|
56
|
+
renderPagination({ currentPageNumber: 4, itemsOnPage: 25 });
|
|
29
57
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
58
|
+
expect(screen.getByTestId("pagination-summary")).toHaveTextContent(
|
|
59
|
+
"Showing 76-100 of 240 monitors",
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
/*
|
|
64
|
+
* The label used to multiply the page number by the page size, so the
|
|
65
|
+
* last page of a 19-row list claimed to be showing rows 11 to 20.
|
|
66
|
+
*/
|
|
67
|
+
it("stops the last page at the last row that exists", () => {
|
|
68
|
+
renderPagination({ currentPageNumber: 2, totalItemsCount: 19 });
|
|
69
|
+
|
|
70
|
+
expect(screen.getByTestId("pagination-summary")).toHaveTextContent(
|
|
71
|
+
"Showing 11-19 of 19 monitors",
|
|
72
|
+
);
|
|
73
|
+
});
|
|
41
74
|
|
|
42
|
-
|
|
75
|
+
it("uses the singular label for a list of one", () => {
|
|
76
|
+
renderPagination({ totalItemsCount: 1 });
|
|
43
77
|
|
|
44
|
-
|
|
78
|
+
expect(screen.getByTestId("pagination-summary")).toHaveTextContent(
|
|
79
|
+
"Showing 1 of 1 monitor",
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("says so when there is nothing to show", () => {
|
|
84
|
+
renderPagination({ totalItemsCount: 0 });
|
|
85
|
+
|
|
86
|
+
expect(screen.getByTestId("pagination-summary")).toHaveTextContent(
|
|
87
|
+
"No monitors",
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("groups the digits of a large total", () => {
|
|
92
|
+
renderPagination({ totalItemsCount: 1234567 });
|
|
93
|
+
|
|
94
|
+
expect(screen.getByTestId("pagination-summary")).toHaveTextContent(
|
|
95
|
+
`Showing 1-10 of ${(1234567).toLocaleString()} monitors`,
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("clamps the range to the rows the page rendered", () => {
|
|
100
|
+
renderPagination({
|
|
101
|
+
currentPageNumber: 24,
|
|
102
|
+
totalItemsCount: 236,
|
|
103
|
+
itemsOnCurrentPage: 6,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect(screen.getByTestId("pagination-summary")).toHaveTextContent(
|
|
107
|
+
"Showing 231-236 of 236 monitors",
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("says it is loading rather than printing a stale range", () => {
|
|
112
|
+
renderPagination({ isLoading: true });
|
|
113
|
+
|
|
114
|
+
expect(screen.getByTestId("pagination-summary")).toHaveTextContent(
|
|
115
|
+
"Loading",
|
|
116
|
+
);
|
|
117
|
+
});
|
|
45
118
|
});
|
|
46
119
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
120
|
+
describe("page numbers", () => {
|
|
121
|
+
it("renders a button for every page of a short list", () => {
|
|
122
|
+
renderPagination({ totalItemsCount: 30 });
|
|
123
|
+
|
|
124
|
+
expect(screen.getByTestId("pagination-page-1")).toBeInTheDocument();
|
|
125
|
+
expect(screen.getByTestId("pagination-page-2")).toBeInTheDocument();
|
|
126
|
+
expect(screen.getByTestId("pagination-page-3")).toBeInTheDocument();
|
|
127
|
+
expect(screen.queryByTestId("pagination-page-4")).toBeNull();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("navigates to the page that was clicked", () => {
|
|
131
|
+
const onNavigateToPage: MockFunction = renderPagination({
|
|
132
|
+
totalItemsCount: 30,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
fireEvent.click(screen.getByTestId("pagination-page-3"));
|
|
136
|
+
|
|
137
|
+
expect(onNavigateToPage).toHaveBeenCalledWith(3, 10);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("keeps the page size when jumping to a page", () => {
|
|
141
|
+
const onNavigateToPage: MockFunction = renderPagination({
|
|
142
|
+
totalItemsCount: 250,
|
|
143
|
+
itemsOnPage: 50,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
fireEvent.click(screen.getByTestId("pagination-page-4"));
|
|
147
|
+
|
|
148
|
+
expect(onNavigateToPage).toHaveBeenCalledWith(4, 50);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("marks the current page for assistive technology", () => {
|
|
152
|
+
renderPagination({ currentPageNumber: 3, totalItemsCount: 30 });
|
|
153
|
+
|
|
154
|
+
expect(screen.getByTestId("pagination-page-3")).toHaveAttribute(
|
|
155
|
+
"aria-current",
|
|
156
|
+
"page",
|
|
157
|
+
);
|
|
158
|
+
expect(screen.getByTestId("pagination-page-1")).not.toHaveAttribute(
|
|
159
|
+
"aria-current",
|
|
160
|
+
);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("does not re-fetch the page the user is already on", () => {
|
|
164
|
+
const onNavigateToPage: MockFunction = renderPagination({
|
|
165
|
+
currentPageNumber: 2,
|
|
166
|
+
totalItemsCount: 30,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
fireEvent.click(screen.getByTestId("pagination-page-2"));
|
|
170
|
+
|
|
171
|
+
expect(onNavigateToPage).not.toHaveBeenCalled();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
/*
|
|
175
|
+
* Five slots, not a row of a dozen buttons: where you are, both ends,
|
|
176
|
+
* and a gap on either side for everything else.
|
|
177
|
+
*/
|
|
178
|
+
it("collapses the middle of a long list but keeps both ends", () => {
|
|
179
|
+
renderPagination({ currentPageNumber: 12, totalItemsCount: 240 });
|
|
180
|
+
|
|
181
|
+
expect(screen.getByTestId("pagination-page-1")).toBeInTheDocument();
|
|
182
|
+
expect(screen.getByTestId("pagination-page-12")).toBeInTheDocument();
|
|
183
|
+
expect(screen.getByTestId("pagination-page-24")).toBeInTheDocument();
|
|
184
|
+
expect(screen.queryByTestId("pagination-page-11")).toBeNull();
|
|
185
|
+
expect(screen.queryByTestId("pagination-page-13")).toBeNull();
|
|
186
|
+
expect(
|
|
187
|
+
screen.getByTestId("pagination-ellipsis-start"),
|
|
188
|
+
).toBeInTheDocument();
|
|
189
|
+
expect(screen.getByTestId("pagination-ellipsis-end")).toBeInTheDocument();
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it("never draws more than five page slots", () => {
|
|
193
|
+
renderPagination({ currentPageNumber: 12, totalItemsCount: 10000 });
|
|
194
|
+
|
|
195
|
+
expect(
|
|
196
|
+
screen
|
|
197
|
+
.getByRole("navigation")
|
|
198
|
+
.querySelectorAll(
|
|
199
|
+
'[data-testid^="pagination-page-"], [data-testid^="pagination-ellipsis-"]',
|
|
200
|
+
),
|
|
201
|
+
).toHaveLength(5);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("jumps to the last page in one click", () => {
|
|
205
|
+
const onNavigateToPage: MockFunction = renderPagination({
|
|
206
|
+
currentPageNumber: 12,
|
|
207
|
+
totalItemsCount: 240,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
fireEvent.click(screen.getByTestId("pagination-page-24"));
|
|
211
|
+
|
|
212
|
+
expect(onNavigateToPage).toHaveBeenCalledWith(24, 10);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
/*
|
|
216
|
+
* An even split used to produce one page too many, so a 20-row list with
|
|
217
|
+
* 10 rows to a page offered a page 3 that could only render empty.
|
|
218
|
+
*/
|
|
219
|
+
it("does not offer a page past the end of an even split", () => {
|
|
220
|
+
renderPagination({ totalItemsCount: 20 });
|
|
59
221
|
|
|
60
|
-
|
|
222
|
+
expect(screen.getByTestId("pagination-page-2")).toBeInTheDocument();
|
|
223
|
+
expect(screen.queryByTestId("pagination-page-3")).toBeNull();
|
|
224
|
+
});
|
|
61
225
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
fireEvent.click(nextButtons[0]!);
|
|
226
|
+
it("renders a single page for an empty list", () => {
|
|
227
|
+
renderPagination({ totalItemsCount: 0 });
|
|
65
228
|
|
|
66
|
-
|
|
67
|
-
expect(
|
|
229
|
+
expect(screen.getByTestId("pagination-page-1")).toBeInTheDocument();
|
|
230
|
+
expect(screen.queryByTestId("pagination-page-2")).toBeNull();
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it("does not navigate from a page number while loading", () => {
|
|
234
|
+
const onNavigateToPage: MockFunction = renderPagination({
|
|
235
|
+
totalItemsCount: 30,
|
|
236
|
+
isLoading: true,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
fireEvent.click(screen.getByTestId("pagination-page-2"));
|
|
240
|
+
|
|
241
|
+
expect(onNavigateToPage).not.toHaveBeenCalled();
|
|
68
242
|
});
|
|
69
243
|
});
|
|
70
244
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
onNavigateToPage
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
245
|
+
describe("previous and next", () => {
|
|
246
|
+
it("moves forward a page", () => {
|
|
247
|
+
const onNavigateToPage: MockFunction = renderPagination();
|
|
248
|
+
|
|
249
|
+
fireEvent.click(screen.getByTestId("pagination-next-button"));
|
|
250
|
+
|
|
251
|
+
expect(onNavigateToPage).toHaveBeenCalledWith(2, 10);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it("moves back a page", () => {
|
|
255
|
+
const onNavigateToPage: MockFunction = renderPagination({
|
|
256
|
+
currentPageNumber: 5,
|
|
257
|
+
});
|
|
83
258
|
|
|
84
|
-
|
|
259
|
+
fireEvent.click(screen.getByTestId("pagination-previous-button"));
|
|
85
260
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
261
|
+
expect(onNavigateToPage).toHaveBeenCalledWith(4, 10);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it("disables previous on the first page", () => {
|
|
265
|
+
renderPagination();
|
|
266
|
+
|
|
267
|
+
expect(screen.getByTestId("pagination-previous-button")).toBeDisabled();
|
|
268
|
+
expect(screen.getByTestId("pagination-next-button")).toBeEnabled();
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it("disables next on the last page", () => {
|
|
272
|
+
renderPagination({ currentPageNumber: 24 });
|
|
273
|
+
|
|
274
|
+
expect(screen.getByTestId("pagination-next-button")).toBeDisabled();
|
|
275
|
+
expect(screen.getByTestId("pagination-previous-button")).toBeEnabled();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("disables next when the last page is only partly full", () => {
|
|
279
|
+
renderPagination({ currentPageNumber: 2, totalItemsCount: 19 });
|
|
89
280
|
|
|
90
|
-
|
|
91
|
-
|
|
281
|
+
expect(screen.getByTestId("pagination-next-button")).toBeDisabled();
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it("disables both directions for an empty list", () => {
|
|
285
|
+
renderPagination({ totalItemsCount: 0 });
|
|
286
|
+
|
|
287
|
+
expect(screen.getByTestId("pagination-previous-button")).toBeDisabled();
|
|
288
|
+
expect(screen.getByTestId("pagination-next-button")).toBeDisabled();
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it("disables both directions while loading", () => {
|
|
292
|
+
renderPagination({ currentPageNumber: 5, isLoading: true });
|
|
293
|
+
|
|
294
|
+
expect(screen.getByTestId("pagination-previous-button")).toBeDisabled();
|
|
295
|
+
expect(screen.getByTestId("pagination-next-button")).toBeDisabled();
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it("disables both directions after an error", () => {
|
|
299
|
+
renderPagination({ currentPageNumber: 5, isError: true });
|
|
300
|
+
|
|
301
|
+
expect(screen.getByTestId("pagination-previous-button")).toBeDisabled();
|
|
302
|
+
expect(screen.getByTestId("pagination-next-button")).toBeDisabled();
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it("disables both directions when the caller asks", () => {
|
|
306
|
+
renderPagination({ currentPageNumber: 5, isDisabled: true });
|
|
307
|
+
|
|
308
|
+
expect(screen.getByTestId("pagination-previous-button")).toBeDisabled();
|
|
309
|
+
expect(screen.getByTestId("pagination-next-button")).toBeDisabled();
|
|
92
310
|
});
|
|
93
311
|
});
|
|
94
312
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
313
|
+
describe("rows per page", () => {
|
|
314
|
+
it("is visible on the page rather than behind a menu", () => {
|
|
315
|
+
renderPagination();
|
|
316
|
+
|
|
317
|
+
expect(screen.getByLabelText("Rows per page")).toBeInTheDocument();
|
|
318
|
+
expect(screen.getByTestId("pagination-items-on-page-select")).toHaveValue(
|
|
319
|
+
"10",
|
|
320
|
+
);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it("offers the standard page sizes", () => {
|
|
324
|
+
renderPagination();
|
|
325
|
+
|
|
326
|
+
const options: Array<HTMLOptionElement> = Array.from(
|
|
327
|
+
screen
|
|
328
|
+
.getByTestId("pagination-items-on-page-select")
|
|
329
|
+
.querySelectorAll("option"),
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
expect(
|
|
333
|
+
options.map((option: HTMLOptionElement) => {
|
|
334
|
+
return option.value;
|
|
335
|
+
}),
|
|
336
|
+
).toEqual(["10", "20", "25", "50", "100"]);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it("changes the page size", () => {
|
|
340
|
+
const onNavigateToPage: MockFunction = renderPagination();
|
|
106
341
|
|
|
107
|
-
|
|
342
|
+
fireEvent.change(screen.getByTestId("pagination-items-on-page-select"), {
|
|
343
|
+
target: { value: "50" },
|
|
344
|
+
});
|
|
108
345
|
|
|
109
|
-
|
|
346
|
+
expect(onNavigateToPage).toHaveBeenCalledWith(1, 50);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
/*
|
|
350
|
+
* Row 400 of the old page size is not row 400 of the new one, so a
|
|
351
|
+
* resize that kept the page number could land on a page that no longer
|
|
352
|
+
* exists.
|
|
353
|
+
*/
|
|
354
|
+
it("returns to the first page when the size changes", () => {
|
|
355
|
+
const onNavigateToPage: MockFunction = renderPagination({
|
|
356
|
+
currentPageNumber: 20,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
fireEvent.change(screen.getByTestId("pagination-items-on-page-select"), {
|
|
360
|
+
target: { value: "100" },
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
expect(onNavigateToPage).toHaveBeenCalledWith(1, 100);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it("does nothing when the size is unchanged", () => {
|
|
367
|
+
const onNavigateToPage: MockFunction = renderPagination();
|
|
368
|
+
|
|
369
|
+
fireEvent.change(screen.getByTestId("pagination-items-on-page-select"), {
|
|
370
|
+
target: { value: "10" },
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
expect(onNavigateToPage).not.toHaveBeenCalled();
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
/*
|
|
377
|
+
* A shared link can carry any page size. A select whose value is missing
|
|
378
|
+
* from its options renders as the first option instead, so the table
|
|
379
|
+
* would claim 10 rows a page while showing 37.
|
|
380
|
+
*/
|
|
381
|
+
it("shows a page size that came from a link", () => {
|
|
382
|
+
renderPagination({ itemsOnPage: 37 });
|
|
110
383
|
|
|
111
|
-
|
|
112
|
-
|
|
384
|
+
expect(screen.getByTestId("pagination-items-on-page-select")).toHaveValue(
|
|
385
|
+
"37",
|
|
386
|
+
);
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it("honours a caller's own page sizes", () => {
|
|
390
|
+
renderPagination({ itemsOnPage: 50, itemsOnPageOptions: [50, 250] });
|
|
391
|
+
|
|
392
|
+
const options: Array<HTMLOptionElement> = Array.from(
|
|
393
|
+
screen
|
|
394
|
+
.getByTestId("pagination-items-on-page-select")
|
|
395
|
+
.querySelectorAll("option"),
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
expect(
|
|
399
|
+
options.map((option: HTMLOptionElement) => {
|
|
400
|
+
return option.value;
|
|
401
|
+
}),
|
|
402
|
+
).toEqual(["50", "250"]);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it("is frozen while loading", () => {
|
|
406
|
+
renderPagination({ isLoading: true });
|
|
407
|
+
|
|
408
|
+
expect(
|
|
409
|
+
screen.getByTestId("pagination-items-on-page-select"),
|
|
410
|
+
).toBeDisabled();
|
|
113
411
|
});
|
|
114
412
|
});
|
|
115
413
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
414
|
+
/*
|
|
415
|
+
* The jump box costs nothing on the bar until it is asked for: the
|
|
416
|
+
* collapsed gap is the button that opens it, which is also where the
|
|
417
|
+
* pages it can reach were hidden.
|
|
418
|
+
*/
|
|
419
|
+
describe("go to page", () => {
|
|
420
|
+
type OpenGoToPageModalFunction = (
|
|
421
|
+
overrides?: Partial<ComponentProps>,
|
|
422
|
+
) => MockFunction;
|
|
423
|
+
|
|
424
|
+
const openGoToPageModal: OpenGoToPageModalFunction = (
|
|
425
|
+
overrides?: Partial<ComponentProps>,
|
|
426
|
+
): MockFunction => {
|
|
427
|
+
const onNavigateToPage: MockFunction = renderPagination(overrides);
|
|
428
|
+
fireEvent.click(screen.getByTestId("pagination-ellipsis-end"));
|
|
429
|
+
return onNavigateToPage;
|
|
126
430
|
};
|
|
127
431
|
|
|
128
|
-
|
|
432
|
+
it("takes up no room on the bar until it is asked for", () => {
|
|
433
|
+
renderPagination();
|
|
434
|
+
|
|
435
|
+
expect(screen.queryByTestId("pagination-go-to-page-input")).toBeNull();
|
|
436
|
+
expect(screen.queryByTestId("modal")).toBeNull();
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it("opens from a collapsed gap", () => {
|
|
440
|
+
openGoToPageModal();
|
|
441
|
+
|
|
442
|
+
expect(
|
|
443
|
+
screen.getByTestId("pagination-go-to-page-input"),
|
|
444
|
+
).toBeInTheDocument();
|
|
445
|
+
expect(screen.getByTestId("modal-title")).toHaveTextContent("Go to page");
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
it("says how many pages there are to choose from", () => {
|
|
449
|
+
openGoToPageModal();
|
|
450
|
+
|
|
451
|
+
expect(screen.getByTestId("modal-description")).toHaveTextContent(
|
|
452
|
+
"This list has 24 pages",
|
|
453
|
+
);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it("has no gap to open it from when every page is one click away", () => {
|
|
457
|
+
renderPagination({ totalItemsCount: 30 });
|
|
129
458
|
|
|
130
|
-
|
|
459
|
+
expect(screen.queryByTestId("pagination-ellipsis-end")).toBeNull();
|
|
460
|
+
expect(screen.queryByTestId("pagination-ellipsis-start")).toBeNull();
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it("opens from the gap before the current page too", () => {
|
|
464
|
+
renderPagination({ currentPageNumber: 12 });
|
|
131
465
|
|
|
132
|
-
|
|
133
|
-
|
|
466
|
+
fireEvent.click(screen.getByTestId("pagination-ellipsis-start"));
|
|
467
|
+
|
|
468
|
+
expect(
|
|
469
|
+
screen.getByTestId("pagination-go-to-page-input"),
|
|
470
|
+
).toBeInTheDocument();
|
|
134
471
|
});
|
|
135
472
|
|
|
136
|
-
|
|
473
|
+
it("jumps to the page that was typed", () => {
|
|
474
|
+
const onNavigateToPage: MockFunction = openGoToPageModal();
|
|
475
|
+
|
|
476
|
+
fireEvent.change(screen.getByTestId("pagination-go-to-page-input"), {
|
|
477
|
+
target: { value: "17" },
|
|
478
|
+
});
|
|
479
|
+
fireEvent.click(screen.getByTestId("modal-footer-submit-button"));
|
|
137
480
|
|
|
138
|
-
|
|
139
|
-
|
|
481
|
+
expect(onNavigateToPage).toHaveBeenCalledWith(17, 10);
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
it("jumps on Enter as well as on the button", () => {
|
|
485
|
+
const onNavigateToPage: MockFunction = openGoToPageModal();
|
|
486
|
+
|
|
487
|
+
fireEvent.change(screen.getByTestId("pagination-go-to-page-input"), {
|
|
488
|
+
target: { value: "9" },
|
|
489
|
+
});
|
|
490
|
+
fireEvent.keyDown(screen.getByTestId("pagination-go-to-page-input"), {
|
|
491
|
+
key: "Enter",
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
expect(onNavigateToPage).toHaveBeenCalledWith(9, 10);
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it("closes itself once it has navigated", () => {
|
|
498
|
+
openGoToPageModal();
|
|
499
|
+
|
|
500
|
+
fireEvent.change(screen.getByTestId("pagination-go-to-page-input"), {
|
|
501
|
+
target: { value: "17" },
|
|
502
|
+
});
|
|
503
|
+
fireEvent.click(screen.getByTestId("modal-footer-submit-button"));
|
|
504
|
+
|
|
505
|
+
expect(screen.queryByTestId("pagination-go-to-page-input")).toBeNull();
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
it("pulls a page past the end back to the last page", () => {
|
|
509
|
+
const onNavigateToPage: MockFunction = openGoToPageModal();
|
|
510
|
+
|
|
511
|
+
fireEvent.change(screen.getByTestId("pagination-go-to-page-input"), {
|
|
512
|
+
target: { value: "9999" },
|
|
513
|
+
});
|
|
514
|
+
fireEvent.click(screen.getByTestId("modal-footer-submit-button"));
|
|
515
|
+
|
|
516
|
+
expect(onNavigateToPage).toHaveBeenCalledWith(24, 10);
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it("pulls a page before the start back to the first page", () => {
|
|
520
|
+
const onNavigateToPage: MockFunction = openGoToPageModal({
|
|
521
|
+
currentPageNumber: 5,
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
fireEvent.change(screen.getByTestId("pagination-go-to-page-input"), {
|
|
525
|
+
target: { value: "-3" },
|
|
526
|
+
});
|
|
527
|
+
fireEvent.click(screen.getByTestId("modal-footer-submit-button"));
|
|
528
|
+
|
|
529
|
+
expect(onNavigateToPage).toHaveBeenCalledWith(1, 10);
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
it("cannot be submitted empty", () => {
|
|
533
|
+
const onNavigateToPage: MockFunction = openGoToPageModal();
|
|
534
|
+
|
|
535
|
+
expect(screen.getByTestId("modal-footer-submit-button")).toBeDisabled();
|
|
536
|
+
expect(onNavigateToPage).not.toHaveBeenCalled();
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
it("ignores text that is not a page number", () => {
|
|
540
|
+
const onNavigateToPage: MockFunction = openGoToPageModal();
|
|
541
|
+
|
|
542
|
+
fireEvent.change(screen.getByTestId("pagination-go-to-page-input"), {
|
|
543
|
+
target: { value: "abc" },
|
|
544
|
+
});
|
|
545
|
+
fireEvent.keyDown(screen.getByTestId("pagination-go-to-page-input"), {
|
|
546
|
+
key: "Enter",
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
expect(onNavigateToPage).not.toHaveBeenCalled();
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
it("can be dismissed without navigating", () => {
|
|
553
|
+
const onNavigateToPage: MockFunction = openGoToPageModal();
|
|
554
|
+
|
|
555
|
+
fireEvent.change(screen.getByTestId("pagination-go-to-page-input"), {
|
|
556
|
+
target: { value: "17" },
|
|
557
|
+
});
|
|
558
|
+
fireEvent.click(screen.getByTestId("modal-footer-close-button"));
|
|
559
|
+
|
|
560
|
+
expect(onNavigateToPage).not.toHaveBeenCalled();
|
|
561
|
+
expect(screen.queryByTestId("pagination-go-to-page-input")).toBeNull();
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
it("does not remember the last page typed", () => {
|
|
565
|
+
openGoToPageModal();
|
|
566
|
+
|
|
567
|
+
fireEvent.change(screen.getByTestId("pagination-go-to-page-input"), {
|
|
568
|
+
target: { value: "17" },
|
|
569
|
+
});
|
|
570
|
+
fireEvent.click(screen.getByTestId("modal-footer-close-button"));
|
|
571
|
+
fireEvent.click(screen.getByTestId("pagination-ellipsis-end"));
|
|
572
|
+
|
|
573
|
+
expect(screen.getByTestId("pagination-go-to-page-input")).toHaveValue(
|
|
574
|
+
null,
|
|
575
|
+
);
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
it("closes when the page changes underneath it", () => {
|
|
579
|
+
const { rerender } = render(
|
|
580
|
+
<Pagination {...baseProps} onNavigateToPage={jest.fn()} />,
|
|
581
|
+
);
|
|
582
|
+
|
|
583
|
+
fireEvent.click(screen.getByTestId("pagination-ellipsis-end"));
|
|
584
|
+
|
|
585
|
+
expect(
|
|
586
|
+
screen.getByTestId("pagination-go-to-page-input"),
|
|
587
|
+
).toBeInTheDocument();
|
|
588
|
+
|
|
589
|
+
rerender(
|
|
590
|
+
<Pagination
|
|
591
|
+
{...baseProps}
|
|
592
|
+
currentPageNumber={17}
|
|
593
|
+
onNavigateToPage={jest.fn()}
|
|
594
|
+
/>,
|
|
595
|
+
);
|
|
596
|
+
|
|
597
|
+
expect(screen.queryByTestId("pagination-go-to-page-input")).toBeNull();
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
it("does not open while loading", () => {
|
|
601
|
+
renderPagination({ isLoading: true });
|
|
602
|
+
|
|
603
|
+
fireEvent.click(screen.getByTestId("pagination-ellipsis-end"));
|
|
604
|
+
|
|
605
|
+
expect(screen.queryByTestId("pagination-go-to-page-input")).toBeNull();
|
|
140
606
|
});
|
|
141
607
|
});
|
|
142
608
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
};
|
|
609
|
+
describe("small screens", () => {
|
|
610
|
+
/*
|
|
611
|
+
* The numbered list is hidden by a media query on narrow screens, so the
|
|
612
|
+
* control still has to say where the reader is.
|
|
613
|
+
*/
|
|
614
|
+
it("carries a page-of-pages indicator", () => {
|
|
615
|
+
renderPagination({ currentPageNumber: 12 });
|
|
616
|
+
|
|
617
|
+
expect(
|
|
618
|
+
screen.getByTestId("pagination-current-page-indicator"),
|
|
619
|
+
).toHaveTextContent("Page 12 of 24");
|
|
620
|
+
});
|
|
155
621
|
|
|
156
|
-
|
|
622
|
+
it("groups the digits of a large page count", () => {
|
|
623
|
+
renderPagination({ totalItemsCount: 1000000 });
|
|
624
|
+
|
|
625
|
+
expect(
|
|
626
|
+
screen.getByTestId("pagination-current-page-indicator"),
|
|
627
|
+
).toHaveTextContent(`Page 1 of ${(100000).toLocaleString()}`);
|
|
628
|
+
});
|
|
629
|
+
});
|
|
157
630
|
|
|
158
|
-
|
|
631
|
+
describe("accessibility", () => {
|
|
632
|
+
it("names the region for screen readers", () => {
|
|
633
|
+
renderPagination();
|
|
159
634
|
|
|
160
|
-
|
|
161
|
-
|
|
635
|
+
expect(
|
|
636
|
+
screen.getByRole("navigation", { name: "Pagination for Monitors" }),
|
|
637
|
+
).toBeInTheDocument();
|
|
162
638
|
});
|
|
163
639
|
|
|
164
|
-
|
|
640
|
+
it("labels the arrows", () => {
|
|
641
|
+
renderPagination();
|
|
165
642
|
|
|
166
|
-
|
|
167
|
-
|
|
643
|
+
expect(
|
|
644
|
+
screen.getByRole("button", { name: "Go to previous page" }),
|
|
645
|
+
).toBeInTheDocument();
|
|
646
|
+
expect(
|
|
647
|
+
screen.getByRole("button", { name: "Go to next page" }),
|
|
648
|
+
).toBeInTheDocument();
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
it("labels a page number as a jump target", () => {
|
|
652
|
+
renderPagination({ totalItemsCount: 30 });
|
|
653
|
+
|
|
654
|
+
expect(
|
|
655
|
+
screen.getByRole("button", { name: "Go to page 2" }),
|
|
656
|
+
).toBeInTheDocument();
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
it("says what a collapsed gap does rather than reading as three dots", () => {
|
|
660
|
+
renderPagination({ currentPageNumber: 12 });
|
|
661
|
+
|
|
662
|
+
expect(
|
|
663
|
+
screen.getAllByRole("button", { name: "Go to a page in between" }),
|
|
664
|
+
).toHaveLength(2);
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
it("passes the caller's test id through to the region", () => {
|
|
668
|
+
renderPagination({ dataTestId: "list-pagination" });
|
|
669
|
+
|
|
670
|
+
expect(screen.getByTestId("list-pagination")).toBeInTheDocument();
|
|
168
671
|
});
|
|
169
672
|
});
|
|
170
673
|
|
|
171
674
|
/*
|
|
172
675
|
* Has-more mode. The analytics list endpoints skip COUNT(*) and instead
|
|
173
676
|
* over-fetch one probe row, so `totalItemsCount` is a lower bound that
|
|
174
|
-
* includes a row the response dropped.
|
|
175
|
-
* from the rows the page
|
|
677
|
+
* includes a row the response dropped. There is no last page to link to
|
|
678
|
+
* and the printed range has to come from the rows the page rendered.
|
|
176
679
|
*/
|
|
177
680
|
describe("has-more mode", () => {
|
|
178
|
-
const hasMoreProps: ComponentProps = {
|
|
681
|
+
const hasMoreProps: Partial<ComponentProps> = {
|
|
179
682
|
currentPageNumber: 1,
|
|
180
683
|
totalItemsCount: 11,
|
|
181
684
|
itemsOnPage: 10,
|
|
182
685
|
itemsOnCurrentPage: 10,
|
|
183
686
|
hasMore: true,
|
|
184
|
-
onNavigateToPage: jest.fn(),
|
|
185
|
-
isLoading: false,
|
|
186
|
-
isError: false,
|
|
187
687
|
singularLabel: "Trace",
|
|
188
688
|
pluralLabel: "Traces",
|
|
189
689
|
};
|
|
190
690
|
|
|
191
691
|
it("does not print the probe row the response dropped", () => {
|
|
192
|
-
|
|
692
|
+
renderPagination(hasMoreProps);
|
|
193
693
|
|
|
194
|
-
expect(screen.
|
|
195
|
-
|
|
694
|
+
expect(screen.getByTestId("pagination-summary")).toHaveTextContent(
|
|
695
|
+
"Showing 1-10+ traces",
|
|
696
|
+
);
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
it("does not claim a total it cannot know", () => {
|
|
700
|
+
renderPagination(hasMoreProps);
|
|
701
|
+
|
|
702
|
+
expect(screen.getByTestId("pagination-summary")).not.toHaveTextContent(
|
|
703
|
+
"of 11",
|
|
704
|
+
);
|
|
196
705
|
});
|
|
197
706
|
|
|
198
707
|
it("keeps the range on the page when paging past the first page", () => {
|
|
199
|
-
|
|
708
|
+
renderPagination({
|
|
200
709
|
...hasMoreProps,
|
|
201
710
|
currentPageNumber: 3,
|
|
202
711
|
totalItemsCount: 31,
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
render(<Pagination {...props} />);
|
|
712
|
+
});
|
|
206
713
|
|
|
207
|
-
expect(screen.
|
|
714
|
+
expect(screen.getByTestId("pagination-summary")).toHaveTextContent(
|
|
715
|
+
"Showing 21-30+ traces",
|
|
716
|
+
);
|
|
208
717
|
});
|
|
209
718
|
|
|
210
719
|
it("prints a partial last page without the trailing plus", () => {
|
|
211
|
-
|
|
720
|
+
renderPagination({
|
|
212
721
|
...hasMoreProps,
|
|
213
722
|
currentPageNumber: 2,
|
|
214
723
|
totalItemsCount: 13,
|
|
215
724
|
itemsOnCurrentPage: 3,
|
|
216
725
|
hasMore: false,
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
render(<Pagination {...props} />);
|
|
726
|
+
});
|
|
220
727
|
|
|
221
|
-
expect(screen.
|
|
728
|
+
expect(screen.getByTestId("pagination-summary")).toHaveTextContent(
|
|
729
|
+
"Showing 11-13 traces",
|
|
730
|
+
);
|
|
222
731
|
});
|
|
223
732
|
|
|
224
733
|
it("has no range to print for an empty page", () => {
|
|
225
|
-
|
|
734
|
+
renderPagination({
|
|
226
735
|
...hasMoreProps,
|
|
227
736
|
currentPageNumber: 2,
|
|
228
737
|
totalItemsCount: 10,
|
|
229
738
|
itemsOnCurrentPage: 0,
|
|
230
739
|
hasMore: false,
|
|
231
|
-
};
|
|
740
|
+
});
|
|
232
741
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
742
|
+
expect(screen.getByTestId("pagination-summary")).toHaveTextContent(
|
|
743
|
+
"No traces",
|
|
744
|
+
);
|
|
236
745
|
});
|
|
237
746
|
|
|
238
747
|
/*
|
|
@@ -240,16 +749,82 @@ describe("Pagination", () => {
|
|
|
240
749
|
* passes skip + rendered rows) are left to the count alone.
|
|
241
750
|
*/
|
|
242
751
|
it("falls back to the count when the rendered row count is absent", () => {
|
|
243
|
-
|
|
752
|
+
renderPagination({
|
|
244
753
|
...hasMoreProps,
|
|
245
754
|
currentPageNumber: 2,
|
|
246
755
|
totalItemsCount: 17,
|
|
247
756
|
itemsOnCurrentPage: undefined,
|
|
248
|
-
};
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
expect(screen.getByTestId("pagination-summary")).toHaveTextContent(
|
|
760
|
+
"Showing 11-17+ traces",
|
|
761
|
+
);
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
it("renders no page numbers, because there is no last page", () => {
|
|
765
|
+
renderPagination(hasMoreProps);
|
|
766
|
+
|
|
767
|
+
expect(screen.queryByTestId("pagination-page-1")).toBeNull();
|
|
768
|
+
expect(screen.queryByTestId("pagination-page-2")).toBeNull();
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
it("offers no gap to jump from, because the page count is unknown", () => {
|
|
772
|
+
renderPagination(hasMoreProps);
|
|
773
|
+
|
|
774
|
+
expect(screen.queryByTestId("pagination-ellipsis-start")).toBeNull();
|
|
775
|
+
expect(screen.queryByTestId("pagination-ellipsis-end")).toBeNull();
|
|
776
|
+
expect(screen.queryByTestId("pagination-go-to-page-input")).toBeNull();
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
it("still offers the page size inline", () => {
|
|
780
|
+
const onNavigateToPage: MockFunction = renderPagination(hasMoreProps);
|
|
781
|
+
|
|
782
|
+
fireEvent.change(screen.getByTestId("pagination-items-on-page-select"), {
|
|
783
|
+
target: { value: "25" },
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
expect(onNavigateToPage).toHaveBeenCalledWith(1, 25);
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
it("shows the page it is on without a page count", () => {
|
|
790
|
+
renderPagination({ ...hasMoreProps, currentPageNumber: 4 });
|
|
791
|
+
|
|
792
|
+
expect(
|
|
793
|
+
screen.getByTestId("pagination-current-page-indicator-desktop"),
|
|
794
|
+
).toHaveTextContent("Page 4");
|
|
795
|
+
expect(
|
|
796
|
+
screen.getByTestId("pagination-current-page-indicator-desktop"),
|
|
797
|
+
).not.toHaveTextContent("of");
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
it("pages forward while there is more to fetch", () => {
|
|
801
|
+
const onNavigateToPage: MockFunction = renderPagination(hasMoreProps);
|
|
802
|
+
|
|
803
|
+
fireEvent.click(screen.getByTestId("pagination-next-button"));
|
|
804
|
+
|
|
805
|
+
expect(onNavigateToPage).toHaveBeenCalledWith(2, 10);
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
it("stops at the page that reports no more rows", () => {
|
|
809
|
+
renderPagination({ ...hasMoreProps, hasMore: false });
|
|
810
|
+
|
|
811
|
+
expect(screen.getByTestId("pagination-next-button")).toBeDisabled();
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
/*
|
|
815
|
+
* The count is a lower bound, so it can be smaller than the rows already
|
|
816
|
+
* paged past. That must not disable the way back.
|
|
817
|
+
*/
|
|
818
|
+
it("keeps the way back open on a later page", () => {
|
|
819
|
+
const onNavigateToPage: MockFunction = renderPagination({
|
|
820
|
+
...hasMoreProps,
|
|
821
|
+
currentPageNumber: 3,
|
|
822
|
+
totalItemsCount: 31,
|
|
823
|
+
});
|
|
249
824
|
|
|
250
|
-
|
|
825
|
+
fireEvent.click(screen.getByTestId("pagination-previous-button"));
|
|
251
826
|
|
|
252
|
-
expect(
|
|
827
|
+
expect(onNavigateToPage).toHaveBeenCalledWith(2, 10);
|
|
253
828
|
});
|
|
254
829
|
});
|
|
255
830
|
});
|