Matchers:
diff --git a/ui/src/Components/NavBar/FilterInput/History.test.tsx b/ui/src/Components/NavBar/FilterInput/History.test.tsx
index 6a11a4173..357bf9783 100644
--- a/ui/src/Components/NavBar/FilterInput/History.test.tsx
+++ b/ui/src/Components/NavBar/FilterInput/History.test.tsx
@@ -4,7 +4,7 @@ import { render, screen, fireEvent } from "@testing-library/react";
import { AlertStore, NewUnappliedFilter } from "Stores/AlertStore";
import { Settings } from "Stores/Settings";
-import { History } from "./History";
+import { History, HistoryMenu, ReduceFilter } from "./History";
let alertStore: AlertStore;
let settingsStore: Settings;
@@ -261,7 +261,6 @@ describe("
", () => {
});
it("clicking on 'Clear history' button triggers clear action", async () => {
- // Verifies that clicking Clear history button calls the onClear callback
const promise = Promise.resolve();
const { container } = renderHistory();
@@ -272,24 +271,54 @@ describe("
", () => {
const toggle = container.querySelector("button.cursor-pointer");
fireEvent.click(toggle!);
- // Verify history has items before clearing
const historyItemsBefore = document.body.querySelectorAll(
".components-navbar-historymenu-labels",
);
expect(historyItemsBefore.length).toBeGreaterThan(0);
- // Click clear history button - this calls history.setFilters([])
const clearButton = screen.getByText("Clear history");
fireEvent.click(clearButton);
act(() => {
jest.runOnlyPendingTimers();
});
- // Menu should close after clicking clear (afterClick is called)
expect(
container.querySelector("div.dropdown-menu"),
).not.toBeInTheDocument();
await act(() => promise);
});
+
+ it("HistoryMenu renders correctly with null coordinates", () => {
+ render(
+
,
+ );
+ const menu = document.body.querySelector(
+ ".components-navbar-historymenu",
+ ) as HTMLElement;
+ expect(menu.style.top).toBe("");
+ expect(menu.style.left).toBe("");
+ });
+
+ it("ReduceFilter returns a reduced filter object", () => {
+ const filter = AppliedFilter("foo", "=", "bar");
+ const reduced = ReduceFilter(filter);
+ expect(reduced).toEqual({
+ raw: "foo=bar",
+ name: "foo",
+ matcher: "=",
+ value: "bar",
+ });
+ });
});
diff --git a/ui/src/Components/NavBar/index.test.tsx b/ui/src/Components/NavBar/index.test.tsx
index 44f3c47ba..0362ddb02 100644
--- a/ui/src/Components/NavBar/index.test.tsx
+++ b/ui/src/Components/NavBar/index.test.tsx
@@ -6,6 +6,7 @@ import fetchMock from "@fetch-mock/jest";
import { useIdleTimer } from "react-idle-timer";
+import { IsMobile } from "Common/Device";
import { MockThemeContext } from "__fixtures__/Theme";
import { EmptyAPIResponse } from "__fixtures__/Fetch";
import { AlertStore } from "Stores/AlertStore";
@@ -15,6 +16,7 @@ import { ThemeContext } from "Components/Theme";
import NavBar from ".";
jest.mock("react-idle-timer");
+jest.mock("Common/Device");
let alertStore: AlertStore;
let settingsStore: Settings;
@@ -245,4 +247,19 @@ describe("
", () => {
.getPropertyValue("padding-top"),
).toBe("44px");
});
+
+ it("uses mobile idle timeout when IsMobile returns true", () => {
+ // Verifies that MobileIdleTimeout is used when IsMobile() returns true (line 61)
+ (IsMobile as jest.Mock).mockReturnValue(true);
+ renderNavbar();
+
+ expect(useIdleTimer).toHaveBeenCalledWith(
+ expect.objectContaining({
+ timeout: expect.any(Number),
+ }),
+ );
+
+ const callArgs = (useIdleTimer as jest.Mock).mock.calls[0][0];
+ expect(callArgs.timeout).toBeGreaterThan(0);
+ });
});
diff --git a/ui/src/Components/NavBar/index.tsx b/ui/src/Components/NavBar/index.tsx
index cb219e0d9..3286a35fb 100644
--- a/ui/src/Components/NavBar/index.tsx
+++ b/ui/src/Components/NavBar/index.tsx
@@ -114,7 +114,7 @@ const NavBar: FC<{
ref={(el) => {
observe(el as HTMLElement);
ref.current = el as HTMLElement;
- (navRef as React.MutableRefObject
).current = el;
+ navRef.current = el;
}}
className={`navbar navbar-expand navbar-dark p-1 bg-primary-transparent d-flex ${
fixedTop ? "fixed-top" : "w-100"
diff --git a/ui/src/Components/OverviewModal/index.tsx b/ui/src/Components/OverviewModal/index.tsx
index fefcac603..a22f5713a 100644
--- a/ui/src/Components/OverviewModal/index.tsx
+++ b/ui/src/Components/OverviewModal/index.tsx
@@ -37,8 +37,7 @@ const OverviewModal: FC<{
{
ref(node);
- (nodeRef as React.MutableRefObject).current =
- node;
+ nodeRef.current = node;
}}
className={`text-center d-inline-block cursor-pointer navbar-brand m-0 components-navbar-button ${
isVisible ? "border-info" : ""
diff --git a/ui/src/Components/SilenceModal/Browser/MassDelete.tsx b/ui/src/Components/SilenceModal/Browser/MassDelete.tsx
index 1149bad36..aa2fab1d6 100644
--- a/ui/src/Components/SilenceModal/Browser/MassDelete.tsx
+++ b/ui/src/Components/SilenceModal/Browser/MassDelete.tsx
@@ -63,8 +63,8 @@ export const SelectableSilence: FC<{
};
const SilenceDeleteMenu: FC<{
- x: number | null;
- y: number | null;
+ x: number;
+ y: number;
floating: Ref | null;
strategy: CSSProperties["position"];
maxHeight: number | null;
@@ -76,8 +76,8 @@ const SilenceDeleteMenu: FC<{
ref={floating}
style={{
position: strategy,
- top: y ?? "",
- left: x ?? "",
+ top: y,
+ left: x,
maxHeight: maxHeight ?? "",
}}
>
diff --git a/ui/src/Components/SilenceModal/Browser/index.test.tsx b/ui/src/Components/SilenceModal/Browser/index.test.tsx
index 5d03e41f7..cbe7b8ca1 100644
--- a/ui/src/Components/SilenceModal/Browser/index.test.tsx
+++ b/ui/src/Components/SilenceModal/Browser/index.test.tsx
@@ -1077,4 +1077,91 @@ describe("", () => {
await act(() => promise);
});
+
+ it("closes modal when close button is clicked", async () => {
+ // Verifies the onHide callback is invoked when the modal close button is clicked
+ const promise = Promise.resolve();
+
+ const newSilence = (id: string): APISilenceT => {
+ const s = MockSilence();
+ s.id = id;
+ return s;
+ };
+
+ useFetchGetMock.fetch.setMockedData({
+ response: [
+ {
+ cluster: cluster,
+ alertCount: 1,
+ silence: newSilence("1"),
+ isExpired: false,
+ },
+ ],
+ error: null,
+ isLoading: false,
+ isRetrying: false,
+ retryCount: 0,
+ get: jest.fn(),
+ cancelGet: jest.fn(),
+ });
+
+ const { container } = renderBrowser();
+
+ const checkboxes = container.querySelectorAll("input.form-check-input");
+ fireEvent.click(checkboxes[1]);
+
+ const deleteButtons = container.querySelectorAll("button.btn-danger");
+ const trashBtn = deleteButtons[0];
+ fireEvent.click(trashBtn);
+
+ let closeBtn = document.querySelector(".btn-close") as HTMLElement;
+ expect(closeBtn).toBeInTheDocument();
+ fireEvent.click(closeBtn);
+
+ act(() => {
+ jest.advanceTimersByTime(300);
+ });
+
+ closeBtn = document.querySelector(".btn-close") as HTMLElement;
+ expect(closeBtn).not.toBeInTheDocument();
+
+ await act(() => promise);
+ });
+
+ it("renders dropdown menu", async () => {
+ // Verifies the SilenceDeleteMenu renders when dropdown is opened
+ const newSilence = (id: string): APISilenceT => {
+ const s = MockSilence();
+ s.id = id;
+ return s;
+ };
+
+ useFetchGetMock.fetch.setMockedData({
+ response: [
+ {
+ cluster: cluster,
+ alertCount: 1,
+ silence: newSilence("1"),
+ isExpired: false,
+ },
+ ],
+ error: null,
+ isLoading: false,
+ isRetrying: false,
+ retryCount: 0,
+ get: jest.fn(),
+ cancelGet: jest.fn(),
+ });
+
+ const { container } = renderBrowser();
+
+ const checkboxes = container.querySelectorAll("input.form-check-input");
+ fireEvent.click(checkboxes[1]);
+
+ const dropdownToggles = container.querySelectorAll(".btn.dropdown-toggle");
+ fireEvent.click(dropdownToggles[dropdownToggles.length - 1]);
+
+ const dropdownMenu = container.querySelector(".dropdown-menu");
+ expect(dropdownMenu).toBeInTheDocument();
+ });
});
diff --git a/ui/src/Components/SilenceModal/DateTimeSelect/index.tsx b/ui/src/Components/SilenceModal/DateTimeSelect/index.tsx
index cf2f15040..29081c586 100644
--- a/ui/src/Components/SilenceModal/DateTimeSelect/index.tsx
+++ b/ui/src/Components/SilenceModal/DateTimeSelect/index.tsx
@@ -71,7 +71,7 @@ const TabContentStart: FC<{
disabled={isSameMonth(today, month)}
onClick={() => setMonth(today)}
>
-
+
Today
@@ -130,7 +130,7 @@ const TabContentEnd: FC<{ silenceFormStore: SilenceFormStore }> = observer(
disabled={isSameMonth(today, month)}
onClick={() => setMonth(today)}
>
-
+
Today
diff --git a/ui/src/Components/SilenceModal/SilenceForm.tsx b/ui/src/Components/SilenceModal/SilenceForm.tsx
index baf1ab2b2..b3cdb15d9 100644
--- a/ui/src/Components/SilenceModal/SilenceForm.tsx
+++ b/ui/src/Components/SilenceModal/SilenceForm.tsx
@@ -1,4 +1,4 @@
-import { FC, useEffect, useState, MouseEvent, FormEvent } from "react";
+import { FC, useEffect, useState, MouseEvent, SyntheticEvent } from "react";
import { observer } from "mobx-react-lite";
@@ -177,7 +177,7 @@ const SilenceForm: FC<{
silenceFormStore.data.setComment(comment);
};
- const handleSubmit = (event: FormEvent) => {
+ const handleSubmit = (event: SyntheticEvent