mirror of
https://github.com/gethomepage/homepage.git
synced 2026-09-27 06:21:18 -07:00
Chore: full react hooks eslint compliance (#7040)
This commit is contained in:
@@ -41,7 +41,7 @@ export default function Component({ service }) {
|
||||
);
|
||||
}
|
||||
|
||||
data.splice(chart ? 5 : 1);
|
||||
const visibleData = data.slice(0, chart ? 5 : 1);
|
||||
let headerYPosition = "top-4";
|
||||
let listYPosition = "bottom-4";
|
||||
if (chart) {
|
||||
@@ -61,7 +61,7 @@ export default function Component({ service }) {
|
||||
|
||||
<Block position={`${listYPosition} right-3 left-3`}>
|
||||
<div className="pointer-events-none text-theme-900 dark:text-theme-200">
|
||||
{data.map((item) => (
|
||||
{visibleData.map((item) => (
|
||||
<div key={item[idKey]} className="text-[0.75rem] h-[0.8rem]">
|
||||
<div className="flex items-center">
|
||||
<div className="w-3 h-3 mr-1.5 opacity-50">{statusMap[item[statusKey]]}</div>
|
||||
|
||||
@@ -103,5 +103,6 @@ describe("widgets/glances/metrics/containers", () => {
|
||||
expect(screen.getByText("item-0")).toBeInTheDocument();
|
||||
expect(screen.getByText("item-4")).toBeInTheDocument();
|
||||
expect(screen.queryByText("item-5")).not.toBeInTheDocument();
|
||||
expect(data).toHaveLength(6);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { useTranslation } from "next-i18next/pages";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCallback } from "react";
|
||||
|
||||
import Block from "../components/block";
|
||||
import Container from "../components/container";
|
||||
|
||||
import useDataPoints from "./use-data-points";
|
||||
|
||||
import { parseVersionForUrl } from "utils/proxy/api-helpers";
|
||||
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||
|
||||
@@ -19,26 +21,26 @@ export default function Component({ service }) {
|
||||
const { chart, refreshInterval = defaultInterval, pointsLimit = defaultPointsLimit, version = 3 } = widget;
|
||||
const apiVersion = parseVersionForUrl(version, 3);
|
||||
|
||||
const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit));
|
||||
const [dataPoints, addDataPoint] = useDataPoints(pointsLimit, { value: 0 });
|
||||
|
||||
const { data, error } = useWidgetAPI(service.widget, `${apiVersion}/cpu`, {
|
||||
refreshInterval: Math.max(defaultInterval, refreshInterval),
|
||||
});
|
||||
const handleData = useCallback(
|
||||
(newData) => {
|
||||
if (newData) addDataPoint({ value: newData.total });
|
||||
},
|
||||
[addDataPoint],
|
||||
);
|
||||
|
||||
const { data, error } = useWidgetAPI(
|
||||
service.widget,
|
||||
`${apiVersion}/cpu`,
|
||||
{
|
||||
refreshInterval: Math.max(defaultInterval, refreshInterval),
|
||||
},
|
||||
{ onSuccess: handleData },
|
||||
);
|
||||
|
||||
const { data: quicklookData, error: quicklookError } = useWidgetAPI(service.widget, `${apiVersion}/quicklook`);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setDataPoints((prevDataPoints) => {
|
||||
const newDataPoints = [...prevDataPoints, { value: data.total }];
|
||||
if (newDataPoints.length > pointsLimit) {
|
||||
newDataPoints.shift();
|
||||
}
|
||||
return newDataPoints;
|
||||
});
|
||||
}
|
||||
}, [data, pointsLimit]);
|
||||
|
||||
if (error) {
|
||||
return <Container error={error} widget={widget} />;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { useTranslation } from "next-i18next/pages";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCallback } from "react";
|
||||
|
||||
import Block from "../components/block";
|
||||
import Container from "../components/container";
|
||||
|
||||
import useDataPoints from "./use-data-points";
|
||||
|
||||
import { parseVersionForUrl } from "utils/proxy/api-helpers";
|
||||
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||
|
||||
@@ -20,39 +22,37 @@ export default function Component({ service }) {
|
||||
const apiVersion = parseVersionForUrl(version, 3);
|
||||
const [, diskName] = widget.metric.split(":");
|
||||
|
||||
const [dataPoints, setDataPoints] = useState(
|
||||
new Array(pointsLimit).fill({ read_bytes: 0, write_bytes: 0, time_since_update: 0 }, 0, pointsLimit),
|
||||
);
|
||||
const [ratePoints, setRatePoints] = useState(new Array(pointsLimit).fill({ a: 0, b: 0 }, 0, pointsLimit));
|
||||
|
||||
const { data, error } = useWidgetAPI(service.widget, `${apiVersion}/diskio`, {
|
||||
refreshInterval: Math.max(defaultInterval, refreshInterval),
|
||||
const [dataPoints, addDataPoint] = useDataPoints(pointsLimit, {
|
||||
read_bytes: 0,
|
||||
write_bytes: 0,
|
||||
time_since_update: 0,
|
||||
});
|
||||
|
||||
const handleData = useCallback(
|
||||
(newData) => {
|
||||
if (!newData?.error) {
|
||||
const diskData = newData.find((item) => item.disk_name === diskName);
|
||||
if (diskData) addDataPoint(diskData);
|
||||
}
|
||||
},
|
||||
[addDataPoint, diskName],
|
||||
);
|
||||
|
||||
const { data, error } = useWidgetAPI(
|
||||
service.widget,
|
||||
`${apiVersion}/diskio`,
|
||||
{
|
||||
refreshInterval: Math.max(defaultInterval, refreshInterval),
|
||||
},
|
||||
{ onSuccess: handleData },
|
||||
);
|
||||
|
||||
const calculateRates = (d) =>
|
||||
d.map((item) => ({
|
||||
a: item.read_bytes / item.time_since_update,
|
||||
b: item.write_bytes / item.time_since_update,
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
if (data && !data.error) {
|
||||
const diskData = data.find((item) => item.disk_name === diskName);
|
||||
|
||||
setDataPoints((prevDataPoints) => {
|
||||
const newDataPoints = [...prevDataPoints, diskData];
|
||||
if (newDataPoints.length > pointsLimit) {
|
||||
newDataPoints.shift();
|
||||
}
|
||||
return newDataPoints;
|
||||
});
|
||||
}
|
||||
}, [data, diskName, pointsLimit]);
|
||||
|
||||
useEffect(() => {
|
||||
setRatePoints(calculateRates(dataPoints));
|
||||
}, [dataPoints]);
|
||||
|
||||
if (error || (data && data.error)) {
|
||||
const finalError = error || data.error;
|
||||
return <Container error={finalError} widget={widget} />;
|
||||
@@ -83,7 +83,7 @@ export default function Component({ service }) {
|
||||
<Container chart={chart}>
|
||||
{chart && (
|
||||
<ChartDual
|
||||
dataPoints={ratePoints}
|
||||
dataPoints={diskRates}
|
||||
label={[t("glances.read"), t("glances.write")]}
|
||||
max={diskData.critical}
|
||||
formatter={(value) =>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { useTranslation } from "next-i18next/pages";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCallback } from "react";
|
||||
|
||||
import Block from "../components/block";
|
||||
import Container from "../components/container";
|
||||
|
||||
import useDataPoints from "./use-data-points";
|
||||
|
||||
import { parseVersionForUrl } from "utils/proxy/api-helpers";
|
||||
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||
|
||||
@@ -20,27 +22,26 @@ export default function Component({ service }) {
|
||||
const apiVersion = parseVersionForUrl(version, 3);
|
||||
const [, gpuName] = widget.metric.split(":");
|
||||
|
||||
const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ a: 0, b: 0 }, 0, pointsLimit));
|
||||
const [dataPoints, addDataPoint] = useDataPoints(pointsLimit, { a: 0, b: 0 });
|
||||
|
||||
const { data, error } = useWidgetAPI(widget, `${apiVersion}/gpu`, {
|
||||
refreshInterval: Math.max(defaultInterval, refreshInterval),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (data && !data.error) {
|
||||
const gpuData = data.find((item) => item[item.key] == gpuName);
|
||||
|
||||
if (gpuData) {
|
||||
setDataPoints((prevDataPoints) => {
|
||||
const newDataPoints = [...prevDataPoints, { a: gpuData.mem, b: gpuData.proc }];
|
||||
if (newDataPoints.length > pointsLimit) {
|
||||
newDataPoints.shift();
|
||||
}
|
||||
return newDataPoints;
|
||||
});
|
||||
const handleData = useCallback(
|
||||
(newData) => {
|
||||
if (!newData?.error) {
|
||||
const gpuData = newData.find((item) => item[item.key] == gpuName);
|
||||
if (gpuData) addDataPoint({ a: gpuData.mem, b: gpuData.proc });
|
||||
}
|
||||
}
|
||||
}, [data, gpuName, pointsLimit]);
|
||||
},
|
||||
[addDataPoint, gpuName],
|
||||
);
|
||||
|
||||
const { data, error } = useWidgetAPI(
|
||||
widget,
|
||||
`${apiVersion}/gpu`,
|
||||
{
|
||||
refreshInterval: Math.max(defaultInterval, refreshInterval),
|
||||
},
|
||||
{ onSuccess: handleData },
|
||||
);
|
||||
|
||||
if (error || (data && data.error)) {
|
||||
const finalError = error || data.error;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { useTranslation } from "next-i18next/pages";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCallback } from "react";
|
||||
|
||||
import Block from "../components/block";
|
||||
import Container from "../components/container";
|
||||
|
||||
import useDataPoints from "./use-data-points";
|
||||
|
||||
import { parseVersionForUrl } from "utils/proxy/api-helpers";
|
||||
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||
|
||||
@@ -20,23 +22,23 @@ export default function Component({ service }) {
|
||||
const { refreshInterval = defaultInterval(chart), pointsLimit = defaultPointsLimit, version = 3 } = widget;
|
||||
const apiVersion = parseVersionForUrl(version, 3);
|
||||
|
||||
const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit));
|
||||
const [dataPoints, addDataPoint] = useDataPoints(pointsLimit, { a: 0, b: 0 });
|
||||
|
||||
const { data, error } = useWidgetAPI(service.widget, `${apiVersion}/mem`, {
|
||||
refreshInterval: Math.max(defaultInterval(chart), refreshInterval),
|
||||
});
|
||||
const handleData = useCallback(
|
||||
(newData) => {
|
||||
if (newData) addDataPoint({ a: newData.used, b: newData.available });
|
||||
},
|
||||
[addDataPoint],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setDataPoints((prevDataPoints) => {
|
||||
const newDataPoints = [...prevDataPoints, { a: data.used, b: data.available }];
|
||||
if (newDataPoints.length > pointsLimit) {
|
||||
newDataPoints.shift();
|
||||
}
|
||||
return newDataPoints;
|
||||
});
|
||||
}
|
||||
}, [data, pointsLimit]);
|
||||
const { data, error } = useWidgetAPI(
|
||||
service.widget,
|
||||
`${apiVersion}/mem`,
|
||||
{
|
||||
refreshInterval: Math.max(defaultInterval(chart), refreshInterval),
|
||||
},
|
||||
{ onSuccess: handleData },
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return <Container error={error} widget={widget} />;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { useTranslation } from "next-i18next/pages";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCallback } from "react";
|
||||
|
||||
import Block from "../components/block";
|
||||
import Container from "../components/container";
|
||||
|
||||
import useDataPoints from "./use-data-points";
|
||||
|
||||
import { parseVersionForUrl } from "utils/proxy/api-helpers";
|
||||
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||
|
||||
@@ -25,33 +27,32 @@ export default function Component({ service }) {
|
||||
|
||||
const [, interfaceName] = metric.split(":");
|
||||
|
||||
const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit));
|
||||
const [dataPoints, addDataPoint] = useDataPoints(pointsLimit, { a: 0, b: 0 });
|
||||
|
||||
const { data, error } = useWidgetAPI(widget, `${apiVersion}/network`, {
|
||||
refreshInterval: Math.max(defaultInterval(chart), refreshInterval),
|
||||
});
|
||||
const handleData = useCallback(
|
||||
(newData) => {
|
||||
if (!newData?.error) {
|
||||
const interfaceData = newData.find((item) => item[item.key] === interfaceName);
|
||||
|
||||
useEffect(() => {
|
||||
if (data && !data.error) {
|
||||
const interfaceData = data.find((item) => item[item.key] === interfaceName);
|
||||
|
||||
if (interfaceData) {
|
||||
setDataPoints((prevDataPoints) => {
|
||||
const newDataPoints = [
|
||||
...prevDataPoints,
|
||||
{
|
||||
a: (interfaceData[rxKey] * 8) / interfaceData.time_since_update,
|
||||
b: (interfaceData[txKey] * 8) / interfaceData.time_since_update,
|
||||
},
|
||||
];
|
||||
if (newDataPoints.length > pointsLimit) {
|
||||
newDataPoints.shift();
|
||||
}
|
||||
return newDataPoints;
|
||||
});
|
||||
if (interfaceData) {
|
||||
addDataPoint({
|
||||
a: (interfaceData[rxKey] * 8) / interfaceData.time_since_update,
|
||||
b: (interfaceData[txKey] * 8) / interfaceData.time_since_update,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [data, interfaceName, pointsLimit, rxKey, txKey]);
|
||||
},
|
||||
[addDataPoint, interfaceName, rxKey, txKey],
|
||||
);
|
||||
|
||||
const { data, error } = useWidgetAPI(
|
||||
widget,
|
||||
`${apiVersion}/network`,
|
||||
{
|
||||
refreshInterval: Math.max(defaultInterval(chart), refreshInterval),
|
||||
},
|
||||
{ onSuccess: handleData },
|
||||
);
|
||||
|
||||
if (error || (data && data.error)) {
|
||||
const finalError = error || data.error;
|
||||
|
||||
@@ -43,7 +43,7 @@ export default function Component({ service }) {
|
||||
);
|
||||
}
|
||||
|
||||
data.splice(chart ? 5 : 1);
|
||||
const visibleData = data.slice(0, chart ? 5 : 1);
|
||||
let headerYPosition = "top-4";
|
||||
let listYPosition = "bottom-4";
|
||||
if (chart) {
|
||||
@@ -63,7 +63,7 @@ export default function Component({ service }) {
|
||||
|
||||
<Block position={`${listYPosition} right-3 left-3`}>
|
||||
<div className="pointer-events-none text-theme-900 dark:text-theme-200">
|
||||
{data.map((item) => (
|
||||
{visibleData.map((item) => (
|
||||
<div key={item.pid} className="text-[0.75rem] h-[0.8rem]">
|
||||
<div className="flex items-center">
|
||||
<div className="w-3 h-3 mr-1.5 opacity-50">{statusMap[item.status]}</div>
|
||||
|
||||
@@ -19,4 +19,20 @@ describe("widgets/glances/metrics/process", () => {
|
||||
});
|
||||
expect(screen.getByText("-")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("limits displayed processes without modifying the response", () => {
|
||||
const data = [
|
||||
{ pid: 1, status: "R", name: "first", cpu_percent: 1, memory_info: [100] },
|
||||
{ pid: 2, status: "S", name: "second", cpu_percent: 2, memory_info: [200] },
|
||||
];
|
||||
useWidgetAPI.mockReturnValue({ data, error: undefined });
|
||||
|
||||
renderWithProviders(<Component service={{ widget: { chart: false, version: 3 } }} />, {
|
||||
settings: { hideErrors: false },
|
||||
});
|
||||
|
||||
expect(screen.getByText("first")).toBeInTheDocument();
|
||||
expect(screen.queryByText("second")).not.toBeInTheDocument();
|
||||
expect(data).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { useTranslation } from "next-i18next/pages";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCallback } from "react";
|
||||
|
||||
import Block from "../components/block";
|
||||
import Container from "../components/container";
|
||||
|
||||
import useDataPoints from "./use-data-points";
|
||||
|
||||
import { parseVersionForUrl } from "utils/proxy/api-helpers";
|
||||
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||
|
||||
@@ -20,32 +22,29 @@ export default function Component({ service }) {
|
||||
const apiVersion = parseVersionForUrl(version, 3);
|
||||
const [, sensorName] = widget.metric.split(":");
|
||||
|
||||
const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit));
|
||||
const [dataPoints, addDataPoint] = useDataPoints(pointsLimit, { value: 0 });
|
||||
|
||||
const { data, error } = useWidgetAPI(service.widget, `${apiVersion}/sensors`, {
|
||||
refreshInterval: Math.max(defaultInterval, refreshInterval),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (data && !data.error) {
|
||||
const sensorData = data.find((item) => item.label === sensorName);
|
||||
if (sensorData) {
|
||||
setDataPoints((prevDataPoints) => {
|
||||
const newDataPoints = [...prevDataPoints, { value: sensorData.value }];
|
||||
if (newDataPoints.length > pointsLimit) {
|
||||
newDataPoints.shift();
|
||||
}
|
||||
return newDataPoints;
|
||||
});
|
||||
} else {
|
||||
data.error = true;
|
||||
const handleData = useCallback(
|
||||
(newData) => {
|
||||
if (!newData?.error) {
|
||||
const sensorData = newData.find((item) => item.label === sensorName);
|
||||
if (sensorData) addDataPoint({ value: sensorData.value });
|
||||
}
|
||||
}
|
||||
}, [data, sensorName, pointsLimit]);
|
||||
},
|
||||
[addDataPoint, sensorName],
|
||||
);
|
||||
|
||||
if (error || (data && data.error)) {
|
||||
const finalError = error || data.error;
|
||||
return <Container error={finalError} widget={widget} />;
|
||||
const { data, error } = useWidgetAPI(
|
||||
service.widget,
|
||||
`${apiVersion}/sensors`,
|
||||
{
|
||||
refreshInterval: Math.max(defaultInterval, refreshInterval),
|
||||
},
|
||||
{ onSuccess: handleData },
|
||||
);
|
||||
|
||||
if (error || data?.error) {
|
||||
return <Container error={error || data.error} widget={widget} />;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
|
||||
@@ -22,4 +22,17 @@ describe("widgets/glances/metrics/sensor", () => {
|
||||
);
|
||||
expect(screen.getByText("-")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders a placeholder for a missing sensor without modifying the response", () => {
|
||||
const data = [{ label: "Other", value: 42 }];
|
||||
useWidgetAPI.mockReturnValue({ data, error: undefined });
|
||||
|
||||
renderWithProviders(
|
||||
<Component service={{ widget: { chart: false, version: 3, pointsLimit: 3, metric: "sensor:CPU" } }} />,
|
||||
{ settings: { hideErrors: false } },
|
||||
);
|
||||
|
||||
expect(screen.getByText("-")).toBeInTheDocument();
|
||||
expect(data).toEqual([{ label: "Other", value: 42 }]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function useDataPoints(pointsLimit, initialPoint) {
|
||||
const [dataPoints, setDataPoints] = useState(() => new Array(pointsLimit).fill(initialPoint));
|
||||
|
||||
const addDataPoint = useCallback(
|
||||
(dataPoint) => {
|
||||
setDataPoints((currentDataPoints) => {
|
||||
if (pointsLimit <= 0) return [];
|
||||
return [...currentDataPoints, dataPoint].slice(-pointsLimit);
|
||||
});
|
||||
},
|
||||
[pointsLimit],
|
||||
);
|
||||
|
||||
return [dataPoints, addDataPoint];
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { act, renderHook } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import useDataPoints from "./use-data-points";
|
||||
|
||||
describe("widgets/glances/metrics/use-data-points", () => {
|
||||
it("appends data points while keeping the requested history length", () => {
|
||||
const { result, rerender } = renderHook(({ pointsLimit }) => useDataPoints(pointsLimit, { value: 0 }), {
|
||||
initialProps: { pointsLimit: 3 },
|
||||
});
|
||||
|
||||
expect(result.current[0]).toEqual([{ value: 0 }, { value: 0 }, { value: 0 }]);
|
||||
|
||||
act(() => result.current[1]({ value: 1 }));
|
||||
expect(result.current[0]).toEqual([{ value: 0 }, { value: 0 }, { value: 1 }]);
|
||||
|
||||
rerender({ pointsLimit: 2 });
|
||||
act(() => result.current[1]({ value: 2 }));
|
||||
expect(result.current[0]).toEqual([{ value: 1 }, { value: 2 }]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user