mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-08 07:42:47 +03:00
fix mobile overflowing icons in system due to new health pane
This commit is contained in:
@@ -72,7 +72,15 @@ test.describe("System — tabs @medium", () => {
|
|||||||
{ timeout: 15_000 },
|
{ timeout: 15_000 },
|
||||||
);
|
);
|
||||||
await expect(frigateApp.page.getByText("0.15.0-test")).toBeVisible();
|
await expect(frigateApp.page.getByText("0.15.0-test")).toBeVisible();
|
||||||
await expect(frigateApp.page.getByText(/Last refreshed/)).toBeVisible();
|
|
||||||
|
if (frigateApp.isMobile) {
|
||||||
|
// the "Last refreshed" label is dropped on mobile so the timestamp
|
||||||
|
// clears the centered logo
|
||||||
|
await expect(frigateApp.page.getByText(/Last refreshed/)).toHaveCount(0);
|
||||||
|
await expect(frigateApp.page.getByText(/Just now|ago/)).toBeVisible();
|
||||||
|
} else {
|
||||||
|
await expect(frigateApp.page.getByText(/Last refreshed/)).toBeVisible();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test("storage tab renders content after switching", async ({
|
test("storage tab renders content after switching", async ({
|
||||||
@@ -234,4 +242,45 @@ test.describe("System — mobile @medium @mobile", () => {
|
|||||||
{ timeout: 5_000 },
|
{ timeout: 5_000 },
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("header controls leave the logo uncovered on a narrow phone", async ({
|
||||||
|
frigateApp,
|
||||||
|
}) => {
|
||||||
|
await frigateApp.goto("/system#general");
|
||||||
|
await expect(frigateApp.page.getByLabel("Select general")).toHaveAttribute(
|
||||||
|
"data-state",
|
||||||
|
"on",
|
||||||
|
{ timeout: 15_000 },
|
||||||
|
);
|
||||||
|
await frigateApp.page.setViewportSize({ width: 320, height: 740 });
|
||||||
|
|
||||||
|
const logo = frigateApp.page.locator("svg.fill-current").first();
|
||||||
|
const tabs = frigateApp.page
|
||||||
|
.locator("[data-radix-scroll-area-viewport]")
|
||||||
|
.filter({ has: frigateApp.page.getByLabel("Select general") });
|
||||||
|
const refreshed = frigateApp.page.getByText(/Just now|ago/);
|
||||||
|
|
||||||
|
const logoBox = await logo.boundingBox();
|
||||||
|
const tabsBox = await tabs.boundingBox();
|
||||||
|
const refreshedBox = await refreshed.boundingBox();
|
||||||
|
|
||||||
|
expect(tabsBox!.x + tabsBox!.width).toBeLessThanOrEqual(logoBox!.x + 1);
|
||||||
|
expect(refreshedBox!.x).toBeGreaterThanOrEqual(logoBox!.x + logoBox!.width);
|
||||||
|
|
||||||
|
// the clipped tabs stay reachable by scrolling
|
||||||
|
const overflow = await tabs.evaluate((el) => ({
|
||||||
|
scroll: el.scrollWidth,
|
||||||
|
client: el.clientWidth,
|
||||||
|
}));
|
||||||
|
expect(overflow.scroll).toBeGreaterThan(overflow.client);
|
||||||
|
await tabs.evaluate((el) => {
|
||||||
|
el.scrollLeft = el.scrollWidth;
|
||||||
|
});
|
||||||
|
await frigateApp.page.getByLabel("Select cameras").click();
|
||||||
|
await expect(frigateApp.page.getByLabel("Select cameras")).toHaveAttribute(
|
||||||
|
"data-state",
|
||||||
|
"on",
|
||||||
|
{ timeout: 5_000 },
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+36
-29
@@ -24,6 +24,8 @@ import HealthMetrics from "@/views/system/HealthMetrics";
|
|||||||
import NoticeFilterButton from "@/components/health/NoticeFilterButton";
|
import NoticeFilterButton from "@/components/health/NoticeFilterButton";
|
||||||
import { DEFAULT_NOTICE_FILTER, NoticeFilter } from "@/types/health";
|
import { DEFAULT_NOTICE_FILTER, NoticeFilter } from "@/types/health";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const allMetrics = [
|
const allMetrics = [
|
||||||
"health",
|
"health",
|
||||||
@@ -96,35 +98,40 @@ function System() {
|
|||||||
{isMobile && (
|
{isMobile && (
|
||||||
<Logo className="absolute inset-x-1/2 h-8 -translate-x-1/2" />
|
<Logo className="absolute inset-x-1/2 h-8 -translate-x-1/2" />
|
||||||
)}
|
)}
|
||||||
<ToggleGroup
|
<ScrollArea className={cn("whitespace-nowrap", isMobile && "w-[45%]")}>
|
||||||
className="*:rounded-md *:px-3 *:py-4"
|
<div className="flex flex-row">
|
||||||
type="single"
|
<ToggleGroup
|
||||||
size="sm"
|
className="*:rounded-md *:px-3 *:py-4"
|
||||||
value={pageToggle}
|
type="single"
|
||||||
onValueChange={(value: SystemMetric) => {
|
size="sm"
|
||||||
if (value) {
|
value={pageToggle}
|
||||||
setPageToggle(value);
|
onValueChange={(value: SystemMetric) => {
|
||||||
}
|
if (value) {
|
||||||
}} // don't allow the severity to be unselected
|
setPageToggle(value);
|
||||||
>
|
}
|
||||||
{Object.values(metrics).map((item) => (
|
}} // don't allow the severity to be unselected
|
||||||
<ToggleGroupItem
|
|
||||||
key={item}
|
|
||||||
className={`flex items-center justify-between gap-2 ${pageToggle == item ? "" : "*:text-muted-foreground"}`}
|
|
||||||
value={item}
|
|
||||||
aria-label={`Select ${item}`}
|
|
||||||
>
|
>
|
||||||
{item == "health" && <LuHeartPulse className="size-4" />}
|
{Object.values(metrics).map((item) => (
|
||||||
{item == "general" && <LuActivity className="size-4" />}
|
<ToggleGroupItem
|
||||||
{item == "enrichments" && <LuSearchCode className="size-4" />}
|
key={item}
|
||||||
{item == "storage" && <LuHardDrive className="size-4" />}
|
className={`flex items-center justify-between gap-2 ${pageToggle == item ? "" : "*:text-muted-foreground"}`}
|
||||||
{item == "cameras" && <FaVideo className="size-4" />}
|
value={item}
|
||||||
{isDesktop && (
|
aria-label={`Select ${item}`}
|
||||||
<div className="smart-capitalize">{t(item + ".title")}</div>
|
>
|
||||||
)}
|
{item == "health" && <LuHeartPulse className="size-4" />}
|
||||||
</ToggleGroupItem>
|
{item == "general" && <LuActivity className="size-4" />}
|
||||||
))}
|
{item == "enrichments" && <LuSearchCode className="size-4" />}
|
||||||
</ToggleGroup>
|
{item == "storage" && <LuHardDrive className="size-4" />}
|
||||||
|
{item == "cameras" && <FaVideo className="size-4" />}
|
||||||
|
{isDesktop && (
|
||||||
|
<div className="smart-capitalize">{t(item + ".title")}</div>
|
||||||
|
)}
|
||||||
|
</ToggleGroupItem>
|
||||||
|
))}
|
||||||
|
</ToggleGroup>
|
||||||
|
<ScrollBar orientation="horizontal" className="h-0" />
|
||||||
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
|
|
||||||
<div className="flex h-full items-center">
|
<div className="flex h-full items-center">
|
||||||
{pageToggle == "health" && (
|
{pageToggle == "health" && (
|
||||||
@@ -135,7 +142,7 @@ function System() {
|
|||||||
)}
|
)}
|
||||||
{lastUpdated && pageToggle != "health" && (
|
{lastUpdated && pageToggle != "health" && (
|
||||||
<div className="h-full content-center text-sm text-muted-foreground">
|
<div className="h-full content-center text-sm text-muted-foreground">
|
||||||
{t("lastRefreshed")}
|
{isDesktop && t("lastRefreshed")}
|
||||||
<TimeAgo time={lastUpdated * 1000} dense />
|
<TimeAgo time={lastUpdated * 1000} dense />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user