@oneuptime/common 12.0.31 → 12.0.32
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/AlertState.ts +0 -2
- package/Models/DatabaseModels/Domain.ts +1 -1
- package/Models/DatabaseModels/Incident.ts +1 -1
- package/Models/DatabaseModels/IncidentTemplate.ts +1 -1
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/MetricType.ts +0 -2
- package/Models/DatabaseModels/MonitorStatusTimeline.ts +0 -2
- package/Models/DatabaseModels/MonitorTest.ts +0 -2
- package/Models/DatabaseModels/NetworkDevice.ts +121 -2
- package/Models/DatabaseModels/NetworkDeviceOidTemplate.ts +3 -1
- package/Models/DatabaseModels/NetworkDeviceRole.ts +648 -0
- package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -1
- package/Models/DatabaseModels/ScheduledMaintenanceTemplate.ts +1 -1
- package/Models/DatabaseModels/StatusPageAnnouncement.ts +0 -2
- package/Models/DatabaseModels/StatusPageAnnouncementTemplate.ts +0 -2
- package/Models/DatabaseModels/StatusPagePrivateUser.ts +0 -2
- package/Models/DatabaseModels/StatusPageResource.ts +0 -2
- package/Models/DatabaseModels/StatusPageSubscriber.ts +0 -2
- package/Server/Infrastructure/Postgres/SchemaMigrations/1790800000000-AddNetworkDeviceRoleTable.ts +72 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/DatabaseService.ts +39 -9
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/NetworkDeviceRoleService.ts +90 -0
- package/Server/Services/ProjectService.ts +85 -0
- package/Server/Services/UserNotificationSettingService.ts +0 -6
- package/Tests/App/Dashboard/AddNeighborToMonitoringModal.test.tsx +31 -1
- package/Tests/App/Dashboard/DashboardChartWidgetZoom.test.tsx +35 -8
- package/Tests/App/Dashboard/DashboardCommandPalette.test.tsx +107 -3
- package/Tests/App/Dashboard/DashboardWideChartZoom.test.tsx +401 -0
- package/Tests/App/Dashboard/MonitorTemplateColumnAndFacet.test.tsx +318 -0
- package/Tests/App/Dashboard/NetworkDeviceBulkDelete.test.tsx +357 -0
- package/Tests/App/Dashboard/NetworkSideMenu.test.tsx +590 -0
- package/Tests/App/Dashboard/SideMenuHarness.tsx +11 -3
- package/Tests/Models/DatabaseModels/ModelRegistryInvariants.test.ts +115 -41
- package/Tests/Server/Infrastructure/Postgres/NetworkDeviceRoleTableMigration.test.ts +476 -0
- package/Tests/Server/Services/DatabaseServiceGenerateSlug.test.ts +215 -0
- package/Tests/Server/Services/NetworkDeviceRoleModel.test.ts +672 -0
- package/Tests/Server/Services/NetworkDeviceRuleEngines.test.ts +1 -1
- package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +67 -4
- package/Tests/Server/Services/ScheduledMaintenanceSubscriberEndTime.test.ts +369 -0
- package/Tests/Server/Services/UserNotificationSettingMonitorDefaults.test.ts +248 -0
- package/Tests/Server/Services/UserNotificationSettingWorkspaceChannels.test.ts +43 -2
- package/Tests/Server/Utils/BasicCron.test.ts +283 -0
- package/Tests/Server/Utils/IncomingCallPhoneNumber.test.ts +174 -0
- package/Tests/Types/NetworkDevice/DefaultNetworkDeviceRole.test.ts +417 -0
- package/Tests/UI/Components/Charts/ChartClickDoubleClickDisambiguation.test.tsx +221 -0
- package/Tests/UI/Components/Charts/ChartDoubleClickReset.test.tsx +220 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +751 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +475 -43
- package/Tests/UI/Components/ModelTable/ColumnPreference.test.ts +89 -0
- package/Tests/UI/Components/NavBarCommandKGating.test.tsx +103 -8
- package/Tests/UI/Components/SideMenuItem.test.tsx +30 -0
- package/Tests/UI/Utils/TableColumnsToCsv.test.ts +30 -0
- package/Tests/UI/Utils/UseDashboardTimeRangeZoom.test.tsx +187 -0
- package/Tests/Utils/Dashboard/DashboardResourceListSharedArgs.test.ts +302 -0
- package/Tests/Utils/Dashboard/DashboardTimeRangeZoom.test.ts +242 -0
- package/Tests/Utils/Monitor/NetworkDeviceRoleCatalog.test.ts +981 -0
- package/Tests/Utils/Monitor/NetworkTopologyRoleCatalog.test.ts +865 -0
- package/Tests/Utils/NetworkDevice/DeviceRoleKeyUtil.test.ts +538 -0
- package/Tests/Utils/Slo/SloEvaluation.test.ts +171 -0
- package/Tests/Utils/Slug.test.ts +61 -1
- package/Tests/jest.setup.ts +19 -0
- package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +61 -0
- package/Types/NetworkDevice/DefaultNetworkDeviceRole.ts +145 -0
- package/Types/Permission.ts +45 -0
- package/UI/Components/Charts/Area/AreaChart.tsx +7 -0
- package/UI/Components/Charts/Bar/BarChart.tsx +6 -0
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +9 -1
- package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +97 -35
- package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +14 -0
- package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +97 -35
- package/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.ts +13 -0
- package/UI/Components/Charts/Line/LineChart.tsx +7 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +50 -15
- package/UI/Components/Navbar/NavBar.tsx +62 -13
- package/UI/Components/Navbar/NavBarMenuModal.tsx +7 -2
- package/UI/Components/SideMenu/CountModelSideMenuItem.tsx +2 -0
- package/UI/Components/SideMenu/SideMenu.tsx +12 -2
- package/UI/Components/SideMenu/SideMenuItem.tsx +4 -1
- package/UI/Components/SideMenu/SideMenuSection.tsx +14 -2
- package/UI/Utils/UseDashboardTimeRangeZoom.ts +77 -0
- package/Utils/Dashboard/DashboardTimeRangeZoom.ts +118 -0
- package/Utils/Monitor/NetworkDeviceRoleCatalog.ts +232 -0
- package/Utils/Monitor/NetworkDeviceRoleUtil.ts +2 -1
- package/Utils/Monitor/NetworkTopologyUtil.ts +116 -33
- package/Utils/NetworkDevice/DeviceRoleKeyUtil.ts +137 -0
- package/Utils/Rules/RulePatternMatchUtil.ts +1 -1
- package/Utils/Slug.ts +34 -1
- package/build/dist/Models/DatabaseModels/AlertState.js +0 -2
- package/build/dist/Models/DatabaseModels/AlertState.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Domain.js +1 -1
- package/build/dist/Models/DatabaseModels/Domain.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Incident.js +1 -1
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js +1 -1
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MetricType.js +0 -2
- package/build/dist/Models/DatabaseModels/MetricType.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js +0 -2
- package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorTest.js +0 -2
- package/build/dist/Models/DatabaseModels/MonitorTest.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +124 -3
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceOidTemplate.js +3 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceOidTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceRole.js +677 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceRole.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +0 -2
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncementTemplate.js +0 -2
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncementTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js +0 -2
- package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageResource.js +0 -2
- package/build/dist/Models/DatabaseModels/StatusPageResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +0 -2
- package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790800000000-AddNetworkDeviceRoleTable.js +39 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790800000000-AddNetworkDeviceRoleTable.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/DatabaseService.js +31 -5
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceRoleService.js +70 -0
- package/build/dist/Server/Services/NetworkDeviceRoleService.js.map +1 -0
- package/build/dist/Server/Services/ProjectService.js +65 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationSettingService.js +0 -1
- package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
- package/build/dist/Types/NetworkDevice/DefaultNetworkDeviceRole.js +105 -0
- package/build/dist/Types/NetworkDevice/DefaultNetworkDeviceRole.js.map +1 -0
- package/build/dist/Types/Permission.js +40 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +1 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +6 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +79 -31
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +7 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +79 -31
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js +14 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js.map +1 -0
- package/build/dist/UI/Components/Charts/Line/LineChart.js +1 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +39 -13
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBar.js +44 -9
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +2 -2
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
- package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js +1 -1
- package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js.map +1 -1
- package/build/dist/UI/Components/SideMenu/SideMenu.js +9 -6
- package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
- package/build/dist/UI/Components/SideMenu/SideMenuItem.js +1 -1
- package/build/dist/UI/Components/SideMenu/SideMenuItem.js.map +1 -1
- package/build/dist/UI/Components/SideMenu/SideMenuSection.js +7 -2
- package/build/dist/UI/Components/SideMenu/SideMenuSection.js.map +1 -1
- package/build/dist/UI/Utils/UseDashboardTimeRangeZoom.js +42 -0
- package/build/dist/UI/Utils/UseDashboardTimeRangeZoom.js.map +1 -0
- package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js +74 -0
- package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkDeviceRoleCatalog.js +126 -0
- package/build/dist/Utils/Monitor/NetworkDeviceRoleCatalog.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +2 -1
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +67 -32
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDevice/DeviceRoleKeyUtil.js +114 -0
- package/build/dist/Utils/NetworkDevice/DeviceRoleKeyUtil.js.map +1 -0
- package/build/dist/Utils/Rules/RulePatternMatchUtil.js +1 -1
- package/build/dist/Utils/Slug.js +31 -1
- package/build/dist/Utils/Slug.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* The dashboard's command palette owns Cmd/Ctrl+K, so the NavBar grew a
|
|
3
|
-
* `disableCommandKShortcut` prop that
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
3
|
+
* `disableCommandKShortcut` prop that surrenders the chord to the dashboard
|
|
4
|
+
* command palette. These tests pin the contract in BOTH directions: with the
|
|
5
|
+
* prop the menu must NOT open or advertise Cmd+K (or the two surfaces would
|
|
6
|
+
* claim the same shortcut), and without the prop the legacy shortcut must
|
|
7
|
+
* keep working for every other NavBar consumer. An already-open products menu
|
|
8
|
+
* still listens just long enough to close without consuming the palette's
|
|
9
|
+
* keydown, preventing two modal dialogs from being stacked.
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
12
|
/*
|
|
@@ -98,6 +100,10 @@ function pressCommandK(): void {
|
|
|
98
100
|
fireEvent.keyDown(document, { key: "k", metaKey: true });
|
|
99
101
|
}
|
|
100
102
|
|
|
103
|
+
function commandKHint(): HTMLElement | null {
|
|
104
|
+
return screen.queryByText(/^(Command|Control) \+ K$/);
|
|
105
|
+
}
|
|
106
|
+
|
|
101
107
|
describe("NavBar Cmd/Ctrl+K gating", () => {
|
|
102
108
|
afterEach(() => {
|
|
103
109
|
cleanup();
|
|
@@ -110,6 +116,7 @@ describe("NavBar Cmd/Ctrl+K gating", () => {
|
|
|
110
116
|
|
|
111
117
|
pressCommandK();
|
|
112
118
|
expect(screen.getByRole("dialog")).toBeInTheDocument();
|
|
119
|
+
expect(commandKHint()).toBeInTheDocument();
|
|
113
120
|
|
|
114
121
|
// And the same chord closes it again — it is a toggle, not just an opener.
|
|
115
122
|
pressCommandK();
|
|
@@ -138,7 +145,7 @@ describe("NavBar Cmd/Ctrl+K gating", () => {
|
|
|
138
145
|
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
|
139
146
|
});
|
|
140
147
|
|
|
141
|
-
test("with disableCommandKShortcut, the menu still opens from its trigger button", () => {
|
|
148
|
+
test("with disableCommandKShortcut, the menu still opens from its trigger button without advertising Cmd/Ctrl+K", () => {
|
|
142
149
|
/*
|
|
143
150
|
* Disabling the shortcut must not disable the menu: the button path is the
|
|
144
151
|
* remaining way in, so it has to keep working.
|
|
@@ -153,6 +160,94 @@ describe("NavBar Cmd/Ctrl+K gating", () => {
|
|
|
153
160
|
|
|
154
161
|
fireEvent.click(screen.getByText("Products"));
|
|
155
162
|
expect(screen.getByRole("dialog")).toBeInTheDocument();
|
|
163
|
+
expect(commandKHint()).not.toBeInTheDocument();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("with disableCommandKShortcut, Cmd+K closes an already-open products menu without consuming the palette's event", () => {
|
|
167
|
+
render(
|
|
168
|
+
<Navbar
|
|
169
|
+
items={[homeItem]}
|
|
170
|
+
moreMenuItems={moreMenuItems}
|
|
171
|
+
disableCommandKShortcut={true}
|
|
172
|
+
/>,
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
fireEvent.click(screen.getByText("Products"));
|
|
176
|
+
expect(screen.getByRole("dialog", { name: "Products menu" })).toBeVisible();
|
|
177
|
+
|
|
178
|
+
const defaultAllowed: boolean = fireEvent.keyDown(document, {
|
|
179
|
+
key: "k",
|
|
180
|
+
metaKey: true,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
expect(defaultAllowed).toBe(true);
|
|
184
|
+
expect(
|
|
185
|
+
screen.queryByRole("dialog", { name: "Products menu" }),
|
|
186
|
+
).not.toBeInTheDocument();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("the enabled products shortcut ignores extra modifiers and a chord claimed by an earlier handler", () => {
|
|
190
|
+
render(<Navbar items={[homeItem]} moreMenuItems={moreMenuItems} />);
|
|
191
|
+
|
|
192
|
+
fireEvent.keyDown(document, { key: "k", metaKey: true, shiftKey: true });
|
|
193
|
+
fireEvent.keyDown(document, { key: "k", ctrlKey: true, altKey: true });
|
|
194
|
+
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
|
195
|
+
|
|
196
|
+
const claimChord: (event: KeyboardEvent) => void = (
|
|
197
|
+
event: KeyboardEvent,
|
|
198
|
+
): void => {
|
|
199
|
+
event.preventDefault();
|
|
200
|
+
};
|
|
201
|
+
window.addEventListener("keydown", claimChord, { capture: true });
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
pressCommandK();
|
|
205
|
+
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
|
206
|
+
} finally {
|
|
207
|
+
window.removeEventListener("keydown", claimChord, { capture: true });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
pressCommandK();
|
|
211
|
+
expect(screen.getByRole("dialog", { name: "Products menu" })).toBeVisible();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("a NavBar without a products menu leaves Cmd+K untouched", () => {
|
|
215
|
+
render(<Navbar items={[homeItem]} />);
|
|
216
|
+
|
|
217
|
+
const defaultAllowed: boolean = fireEvent.keyDown(document, {
|
|
218
|
+
key: "k",
|
|
219
|
+
metaKey: true,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
expect(defaultAllowed).toBe(true);
|
|
223
|
+
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test("the close-only listener dismisses Products even when the shortcut owner ran first", () => {
|
|
227
|
+
render(
|
|
228
|
+
<Navbar
|
|
229
|
+
items={[homeItem]}
|
|
230
|
+
moreMenuItems={moreMenuItems}
|
|
231
|
+
disableCommandKShortcut={true}
|
|
232
|
+
/>,
|
|
233
|
+
);
|
|
234
|
+
fireEvent.click(screen.getByText("Products"));
|
|
235
|
+
|
|
236
|
+
const claimChord: (event: KeyboardEvent) => void = (
|
|
237
|
+
event: KeyboardEvent,
|
|
238
|
+
): void => {
|
|
239
|
+
event.preventDefault();
|
|
240
|
+
};
|
|
241
|
+
window.addEventListener("keydown", claimChord, { capture: true });
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
pressCommandK();
|
|
245
|
+
expect(
|
|
246
|
+
screen.queryByRole("dialog", { name: "Products menu" }),
|
|
247
|
+
).not.toBeInTheDocument();
|
|
248
|
+
} finally {
|
|
249
|
+
window.removeEventListener("keydown", claimChord, { capture: true });
|
|
250
|
+
}
|
|
156
251
|
});
|
|
157
252
|
|
|
158
253
|
test("with disableCommandKShortcut, Cmd+K's default is not prevented (the palette may claim it)", () => {
|
|
@@ -166,8 +261,8 @@ describe("NavBar Cmd/Ctrl+K gating", () => {
|
|
|
166
261
|
|
|
167
262
|
/*
|
|
168
263
|
* fireEvent returns false when some handler called preventDefault. With
|
|
169
|
-
*
|
|
170
|
-
* the
|
|
264
|
+
* Products closed, no close-only listener is needed and nothing here may
|
|
265
|
+
* swallow the chord — the palette is free to claim it.
|
|
171
266
|
*/
|
|
172
267
|
const defaultAllowed: boolean = fireEvent.keyDown(document, {
|
|
173
268
|
key: "k",
|
|
@@ -135,6 +135,36 @@ describe("Side Menu Item", () => {
|
|
|
135
135
|
expect(mainLink).toHaveClass(highlightClassList);
|
|
136
136
|
});
|
|
137
137
|
|
|
138
|
+
it("Should use activeRoute for highlighting without changing the link target", () => {
|
|
139
|
+
const linkTarget: Route = new Route("/network/map?site=");
|
|
140
|
+
const activeRoute: Route = new Route("/network/map");
|
|
141
|
+
|
|
142
|
+
Navigation.default.isOnThisPage = getJestMockFunction().mockImplementation(
|
|
143
|
+
(route: Route): boolean => {
|
|
144
|
+
return route === activeRoute;
|
|
145
|
+
},
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
render(
|
|
149
|
+
<SideMenuItem
|
|
150
|
+
link={{ title: "Network Map", to: linkTarget }}
|
|
151
|
+
activeRoute={activeRoute}
|
|
152
|
+
/>,
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
const mainLink: HTMLAnchorElement = screen
|
|
156
|
+
.getByText("Network Map")
|
|
157
|
+
.closest("a") as HTMLAnchorElement;
|
|
158
|
+
|
|
159
|
+
expect(Navigation.default.isOnThisPage).toHaveBeenCalledWith(activeRoute);
|
|
160
|
+
expect(mainLink).toHaveClass(highlightClassList);
|
|
161
|
+
expect(mainLink).toHaveAttribute("href", linkTarget.toString());
|
|
162
|
+
|
|
163
|
+
fireEvent.click(mainLink);
|
|
164
|
+
|
|
165
|
+
expect(Navigation.default.navigate).toHaveBeenCalledWith(linkTarget);
|
|
166
|
+
});
|
|
167
|
+
|
|
138
168
|
it("Should highlights sub item link when on the same page", () => {
|
|
139
169
|
const subLink: {
|
|
140
170
|
title: string;
|
|
@@ -25,6 +25,7 @@ interface Row {
|
|
|
25
25
|
hosts?: Array<{ name: string }> | undefined;
|
|
26
26
|
services?: Array<{ name: string }> | undefined;
|
|
27
27
|
monitors?: Array<{ name: string }> | undefined;
|
|
28
|
+
monitorTemplate?: { templateName?: string } | undefined;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
/*
|
|
@@ -446,6 +447,35 @@ describe("TableColumnsToCsv", () => {
|
|
|
446
447
|
|
|
447
448
|
expect(result).toEqual([]);
|
|
448
449
|
});
|
|
450
|
+
|
|
451
|
+
test("keeps a column that is only hidden on mobile", () => {
|
|
452
|
+
/*
|
|
453
|
+
* hideOnMobile is a render-time decision taken by TableHeader / TableRow,
|
|
454
|
+
* and this utility never sees a viewport. The monitors list Template
|
|
455
|
+
* column sits at the intersection of the two different kinds of "hidden"
|
|
456
|
+
* a column can now have: a phone viewer who switched it on in Customize
|
|
457
|
+
* Columns gets it in their file, while a desktop viewer on the defaults
|
|
458
|
+
* does not - because the "which columns am I seeing" question is answered
|
|
459
|
+
* upstream in BaseModelTable, never here. Teaching the exporter about
|
|
460
|
+
* hideOnMobile would silently strip columns from the export of anyone on
|
|
461
|
+
* a narrow screen, and nobody would notice until a support ticket.
|
|
462
|
+
*/
|
|
463
|
+
const result: Columns<Row> = TableColumnsToCsv.getExportableColumns([
|
|
464
|
+
{ title: "Name", type: FieldType.Text, key: "name" },
|
|
465
|
+
{
|
|
466
|
+
title: "Template",
|
|
467
|
+
type: FieldType.Entity,
|
|
468
|
+
key: "monitorTemplate",
|
|
469
|
+
hideOnMobile: true,
|
|
470
|
+
},
|
|
471
|
+
]);
|
|
472
|
+
|
|
473
|
+
expect(
|
|
474
|
+
result.map((c: Column<Row>) => {
|
|
475
|
+
return c.title;
|
|
476
|
+
}),
|
|
477
|
+
).toEqual(["Name", "Template"]);
|
|
478
|
+
});
|
|
449
479
|
});
|
|
450
480
|
|
|
451
481
|
describe("getExportKeys", () => {
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { afterEach, describe, expect, test } from "@jest/globals";
|
|
3
|
+
import { act, cleanup, render, screen } from "@testing-library/react";
|
|
4
|
+
import React, { FunctionComponent, ReactElement } from "react";
|
|
5
|
+
import useDashboardTimeRangeZoom, {
|
|
6
|
+
DashboardTimeRangeZoom,
|
|
7
|
+
} from "../../../UI/Utils/UseDashboardTimeRangeZoom";
|
|
8
|
+
import InBetween from "../../../Types/BaseDatabase/InBetween";
|
|
9
|
+
import RangeStartAndEndDateTime from "../../../Types/Time/RangeStartAndEndDateTime";
|
|
10
|
+
import TimeRange from "../../../Types/Time/TimeRange";
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
* The dashboard shells hand this hook's three callbacks straight into a
|
|
14
|
+
* canvas full of React.memo'd widgets whose comparators deliberately do NOT
|
|
15
|
+
* compare function identity. So identity stability is not a nicety here —
|
|
16
|
+
* a callback that changes reference every render would be blocked from
|
|
17
|
+
* reaching the widget, leaving it holding a closure over a stale range, and
|
|
18
|
+
* the SECOND zoom of a session would silently do nothing.
|
|
19
|
+
*
|
|
20
|
+
* These tests read the hook through a real component lifecycle, and pin the
|
|
21
|
+
* identity contract explicitly.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const ROLLING_HOUR: RangeStartAndEndDateTime = {
|
|
25
|
+
range: TimeRange.PAST_ONE_HOUR,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const PINNED_DAY: RangeStartAndEndDateTime = {
|
|
29
|
+
range: TimeRange.CUSTOM,
|
|
30
|
+
startAndEndDate: new InBetween<Date>(
|
|
31
|
+
new Date("2026-08-20T00:00:00.000Z"),
|
|
32
|
+
new Date("2026-08-21T00:00:00.000Z"),
|
|
33
|
+
),
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const DRAG_START: Date = new Date("2026-08-20T09:20:00.000Z");
|
|
37
|
+
const DRAG_END: Date = new Date("2026-08-20T09:35:00.000Z");
|
|
38
|
+
|
|
39
|
+
let latest: DashboardTimeRangeZoom | null = null;
|
|
40
|
+
let renderCount: number = 0;
|
|
41
|
+
const seenCallbacks: {
|
|
42
|
+
set: Array<unknown>;
|
|
43
|
+
zoom: Array<unknown>;
|
|
44
|
+
reset: Array<unknown>;
|
|
45
|
+
} = { set: [], zoom: [], reset: [] };
|
|
46
|
+
|
|
47
|
+
const ZoomProbe: FunctionComponent = (): ReactElement => {
|
|
48
|
+
const zoom: DashboardTimeRangeZoom = useDashboardTimeRangeZoom(ROLLING_HOUR);
|
|
49
|
+
|
|
50
|
+
latest = zoom;
|
|
51
|
+
renderCount++;
|
|
52
|
+
seenCallbacks.set.push(zoom.setStartAndEndDate);
|
|
53
|
+
seenCallbacks.zoom.push(zoom.zoomToTimeRange);
|
|
54
|
+
seenCallbacks.reset.push(zoom.resetZoom);
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<div>
|
|
58
|
+
<span data-testid="range">{zoom.startAndEndDate.range}</span>
|
|
59
|
+
<span data-testid="start">
|
|
60
|
+
{zoom.startAndEndDate.startAndEndDate?.startValue?.toISOString() || ""}
|
|
61
|
+
</span>
|
|
62
|
+
<span data-testid="end">
|
|
63
|
+
{zoom.startAndEndDate.startAndEndDate?.endValue?.toISOString() || ""}
|
|
64
|
+
</span>
|
|
65
|
+
<span data-testid="is-zoomed">{String(zoom.isZoomed)}</span>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
function read(testId: string): string {
|
|
71
|
+
return screen.getByTestId(testId).textContent || "";
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function zoomApi(): DashboardTimeRangeZoom {
|
|
75
|
+
if (!latest) {
|
|
76
|
+
throw new Error("probe has not rendered");
|
|
77
|
+
}
|
|
78
|
+
return latest;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function renderProbe(): void {
|
|
82
|
+
latest = null;
|
|
83
|
+
renderCount = 0;
|
|
84
|
+
seenCallbacks.set = [];
|
|
85
|
+
seenCallbacks.zoom = [];
|
|
86
|
+
seenCallbacks.reset = [];
|
|
87
|
+
render(<ZoomProbe />);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
afterEach(() => {
|
|
91
|
+
cleanup();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe("useDashboardTimeRangeZoom", () => {
|
|
95
|
+
test("starts on the initial range with nothing to reset", () => {
|
|
96
|
+
renderProbe();
|
|
97
|
+
|
|
98
|
+
expect(read("range")).toBe(TimeRange.PAST_ONE_HOUR);
|
|
99
|
+
expect(read("is-zoomed")).toBe("false");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("a panel drag pins the board to the selected window", () => {
|
|
103
|
+
renderProbe();
|
|
104
|
+
|
|
105
|
+
act(() => {
|
|
106
|
+
zoomApi().zoomToTimeRange(DRAG_START, DRAG_END);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
expect(read("range")).toBe(TimeRange.CUSTOM);
|
|
110
|
+
expect(read("start")).toBe(DRAG_START.toISOString());
|
|
111
|
+
expect(read("end")).toBe(DRAG_END.toISOString());
|
|
112
|
+
expect(read("is-zoomed")).toBe("true");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("a double-click reset puts the board back on the pre-zoom range", () => {
|
|
116
|
+
renderProbe();
|
|
117
|
+
|
|
118
|
+
act(() => {
|
|
119
|
+
zoomApi().zoomToTimeRange(DRAG_START, DRAG_END);
|
|
120
|
+
});
|
|
121
|
+
act(() => {
|
|
122
|
+
zoomApi().resetZoom();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
expect(read("range")).toBe(TimeRange.PAST_ONE_HOUR);
|
|
126
|
+
expect(read("is-zoomed")).toBe("false");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("resetting an unzoomed board does not even re-render", () => {
|
|
130
|
+
renderProbe();
|
|
131
|
+
const rendersBefore: number = renderCount;
|
|
132
|
+
|
|
133
|
+
act(() => {
|
|
134
|
+
zoomApi().resetZoom();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
/*
|
|
138
|
+
* The util returns the same state object, so React bails out of the
|
|
139
|
+
* update — a stray double-click on an unzoomed dashboard costs nothing
|
|
140
|
+
* and cannot make every widget refetch.
|
|
141
|
+
*/
|
|
142
|
+
expect(renderCount).toBe(rendersBefore);
|
|
143
|
+
expect(read("range")).toBe(TimeRange.PAST_ONE_HOUR);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("an explicit picker change replaces the range and retires the reset", () => {
|
|
147
|
+
renderProbe();
|
|
148
|
+
|
|
149
|
+
act(() => {
|
|
150
|
+
zoomApi().zoomToTimeRange(DRAG_START, DRAG_END);
|
|
151
|
+
});
|
|
152
|
+
expect(read("is-zoomed")).toBe("true");
|
|
153
|
+
|
|
154
|
+
act(() => {
|
|
155
|
+
zoomApi().setStartAndEndDate(PINNED_DAY);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
expect(read("start")).toBe("2026-08-20T00:00:00.000Z");
|
|
159
|
+
expect(read("is-zoomed")).toBe("false");
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test("the three callbacks keep their identity across every re-render", () => {
|
|
163
|
+
renderProbe();
|
|
164
|
+
|
|
165
|
+
act(() => {
|
|
166
|
+
zoomApi().zoomToTimeRange(DRAG_START, DRAG_END);
|
|
167
|
+
});
|
|
168
|
+
act(() => {
|
|
169
|
+
zoomApi().setStartAndEndDate(PINNED_DAY);
|
|
170
|
+
});
|
|
171
|
+
act(() => {
|
|
172
|
+
zoomApi().zoomToTimeRange(DRAG_START, DRAG_END);
|
|
173
|
+
});
|
|
174
|
+
act(() => {
|
|
175
|
+
zoomApi().resetZoom();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
expect(renderCount).toBeGreaterThan(1);
|
|
179
|
+
for (const captured of [
|
|
180
|
+
seenCallbacks.set,
|
|
181
|
+
seenCallbacks.zoom,
|
|
182
|
+
seenCallbacks.reset,
|
|
183
|
+
]) {
|
|
184
|
+
expect(new Set(captured).size).toBe(1);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
});
|