fix: address widget review feedback
This commit is contained in:
1 parent
b1b17ad69f
commit
a38e731209
79 files changed
+2138
-244
No files matched your search
@@ -13,6 +13,7 @@ import { useScopedI18n } from "@homarr/translation/client";
|
||||
import classes from "./component.module.css";
|
||||
|
||||
import type { WidgetComponentProps } from "../definition";
|
||||
import { setWidgetRuntimeQueries } from "../definition";
|
||||
import type { BeszelTimePeriod } from "../beszel/_shared/chart";
|
||||
import { BeszelIntegrationErrorIndicator } from "../beszel/_shared/error-indicator";
|
||||
import { BeszelStatsView } from "../beszel/_shared/stats-view";
|
||||
@@ -28,6 +29,7 @@ export default function BeszelSystemStatsWidget({
|
||||
boardId,
|
||||
itemId,
|
||||
setOptions,
|
||||
widgetStateRef,
|
||||
}: WidgetComponentProps<"beszelSystemStats">) {
|
||||
const t = useScopedI18n("widget.beszelSystemStats");
|
||||
const board = useOptionalBoard();
|
||||
@@ -45,6 +47,25 @@ export default function BeszelSystemStatsWidget({
|
||||
const selectedValue = selectedSystem?.value ?? "";
|
||||
const selectedLabel = selectedSystem?.label;
|
||||
const systemExists = selectedSystem !== undefined;
|
||||
const includeDocker =
|
||||
(displayMode === "advanced" || height >= 380) &&
|
||||
(options.showDockerCpu || options.showDockerMemory || options.showDockerNetwork);
|
||||
setWidgetRuntimeQueries(
|
||||
widgetStateRef,
|
||||
selectedSystem && options.timePeriod !== "1m"
|
||||
? [
|
||||
{
|
||||
path: ["widget", "beszel", "getSystemStats"],
|
||||
input: {
|
||||
integrationIds: [selectedSystem.integrationId],
|
||||
systemId: selectedSystem.systemId,
|
||||
timePeriod: options.timePeriod,
|
||||
includeDocker,
|
||||
},
|
||||
},
|
||||
]
|
||||
: [],
|
||||
);
|
||||
|
||||
const handleSelectSystem = useCallback(
|
||||
(value: string) => {
|
||||
@@ -178,7 +199,7 @@ export default function BeszelSystemStatsWidget({
|
||||
dockerMemory: options.showDockerMemory && (displayMode === "advanced" || height >= 380),
|
||||
dockerNetwork: options.showDockerNetwork && (displayMode === "advanced" || height >= 380),
|
||||
}}
|
||||
onSwitchToHistorical={() => setOptions({ newOptions: { timePeriod: "1h" } })}
|
||||
onSwitchToHistorical={() => handleTimePeriod("1h")}
|
||||
/>
|
||||
</Stack>
|
||||
</ScrollArea>
|
||||
|
||||
@@ -2,9 +2,9 @@ import { IconChartAreaLine, IconServerOff } from "@tabler/icons-react";
|
||||
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
|
||||
import { createWidgetDefinition } from "../definition";
|
||||
import { createWidgetDefinition, matchesWidgetRuntimeQuery, widgetQueryInputMatches } from "../definition";
|
||||
import { optionsBuilder } from "../options";
|
||||
import { createBeszelSystemChoices } from "./selection";
|
||||
import { createBeszelSystemChoices, resolveStoredBeszelQuerySelection } from "./selection";
|
||||
|
||||
const timePeriodOptions = [
|
||||
{ value: "1m", label: "Live" },
|
||||
@@ -17,7 +17,29 @@ const timePeriodOptions = [
|
||||
|
||||
export const { definition, componentLoader } = createWidgetDefinition("beszelSystemStats", {
|
||||
icon: IconChartAreaLine,
|
||||
queryKey: [["widget", "beszel"]],
|
||||
queryKeys: [[["widget", "beszel", "getSystems"]], [["widget", "beszel", "getSystemStats"]]],
|
||||
queryMatcher(query, scope) {
|
||||
if (query.path.at(-1) === "getSystems") {
|
||||
return widgetQueryInputMatches(query.input, { integrationIds: scope.integrationIds });
|
||||
}
|
||||
|
||||
const hasRuntimeStatsQuery = scope.runtimeQueries.some(({ path }) => path.at(-1) === "getSystemStats");
|
||||
if (hasRuntimeStatsQuery) return matchesWidgetRuntimeQuery(query, scope);
|
||||
|
||||
const selection = resolveStoredBeszelQuerySelection(String(scope.options.systemId ?? ""), scope.integrationIds);
|
||||
if (!selection) return false;
|
||||
const dockerEnabled = ["showDockerCpu", "showDockerMemory", "showDockerNetwork"].some(
|
||||
(key) => scope.options[key] === true,
|
||||
);
|
||||
const expected = {
|
||||
...selection,
|
||||
timePeriod: scope.options.timePeriod,
|
||||
};
|
||||
return (
|
||||
widgetQueryInputMatches(query.input, { ...expected, includeDocker: false }) ||
|
||||
(dockerEnabled && widgetQueryInputMatches(query.input, { ...expected, includeDocker: true }))
|
||||
);
|
||||
},
|
||||
supportedIntegrations: ["beszel", "mock"],
|
||||
integrationsRequired: true,
|
||||
createOptions() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { createBeszelSystemChoices, resolveBeszelSystemChoice } from "./selection";
|
||||
import { createBeszelSystemChoices, resolveBeszelSystemChoice, resolveStoredBeszelQuerySelection } from "./selection";
|
||||
|
||||
const choices = createBeszelSystemChoices([
|
||||
{ integrationId: "one", integrationName: "Primary", systems: [{ id: "same", name: "Host A" }] },
|
||||
@@ -13,7 +13,34 @@ describe("Beszel system selection", () => {
|
||||
expect(resolveBeszelSystemChoice(choices, "two:same")?.integrationId).toBe("two");
|
||||
});
|
||||
|
||||
it("keeps legacy plain system ids working", () => {
|
||||
expect(resolveBeszelSystemChoice(choices, "same")?.integrationId).toBe("one");
|
||||
it("keeps unique legacy plain system ids working", () => {
|
||||
expect(resolveBeszelSystemChoice(choices.slice(0, 1), "same")?.integrationId).toBe("one");
|
||||
});
|
||||
|
||||
it("defaults an empty selection to the first system", () => {
|
||||
expect(resolveBeszelSystemChoice(choices, "")?.integrationId).toBe("one");
|
||||
});
|
||||
|
||||
it("does not guess when a legacy plain system id is ambiguous", () => {
|
||||
expect(resolveBeszelSystemChoice(choices, "same")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("does not silently replace an unresolved stored system", () => {
|
||||
expect(resolveBeszelSystemChoice(choices, "missing")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("derives the queried integration and system from a composite selection", () => {
|
||||
expect(resolveStoredBeszelQuerySelection("two:same", ["one", "two"])).toEqual({
|
||||
integrationIds: ["two"],
|
||||
systemId: "same",
|
||||
});
|
||||
});
|
||||
|
||||
it("supports a legacy plain system id only when the integration is unambiguous", () => {
|
||||
expect(resolveStoredBeszelQuerySelection("same", ["one"])).toEqual({
|
||||
integrationIds: ["one"],
|
||||
systemId: "same",
|
||||
});
|
||||
expect(resolveStoredBeszelQuerySelection("same", ["one", "two"])).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -24,9 +24,25 @@ export function createBeszelSystemChoices(results: BeszelSystemsResult[]): Besze
|
||||
}
|
||||
|
||||
export function resolveBeszelSystemChoice(choices: BeszelSystemChoice[], storedValue: string) {
|
||||
return (
|
||||
choices.find((choice) => choice.value === storedValue) ??
|
||||
choices.find((choice) => choice.systemId === storedValue) ??
|
||||
choices[0]
|
||||
);
|
||||
const exactMatch = choices.find((choice) => choice.value === storedValue);
|
||||
if (exactMatch) return exactMatch;
|
||||
if (storedValue === "") return choices[0];
|
||||
|
||||
const legacyMatches = choices.filter((choice) => choice.systemId === storedValue);
|
||||
return legacyMatches.length === 1 ? legacyMatches[0] : undefined;
|
||||
}
|
||||
|
||||
export function resolveStoredBeszelQuerySelection(storedValue: string, integrationIds: readonly string[]) {
|
||||
for (const integrationId of integrationIds) {
|
||||
const prefix = `${integrationId}:`;
|
||||
if (storedValue.startsWith(prefix)) {
|
||||
return { integrationIds: [integrationId], systemId: storedValue.slice(prefix.length) };
|
||||
}
|
||||
}
|
||||
|
||||
if (storedValue !== "" && integrationIds.length === 1) {
|
||||
return { integrationIds: [integrationIds[0] as string], systemId: storedValue };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
Reference in new issue
Block a user