feat: mock all demo widgets
This commit is contained in:
1 parent
e7c8e2e01e
commit
71c328cd7a
49 files changed
+1008
-170
No files matched your search
@@ -8,7 +8,7 @@ export const { definition, componentLoader } = createWidgetDefinition("anchorNot
|
||||
supportsAdvancedFocus: true,
|
||||
queryKey: [["widget", "anchorNotes"]],
|
||||
maxIntegrations: 1,
|
||||
supportedIntegrations: ["anchor"],
|
||||
supportedIntegrations: ["anchor", "mock"],
|
||||
createOptions() {
|
||||
return optionsBuilder.from((factory) => ({
|
||||
noteId: factory.anchorNote(),
|
||||
|
||||
@@ -14,5 +14,5 @@ export const { definition, componentLoader } = createWidgetDefinition("archiveTe
|
||||
}),
|
||||
}));
|
||||
},
|
||||
supportedIntegrations: ["archiveTeamWarrior"],
|
||||
supportedIntegrations: ["archiveTeamWarrior", "mock"],
|
||||
}).withDynamicImport(() => import("./component"));
|
||||
@@ -4,7 +4,8 @@ import { createWidgetDefinition, matchesWidgetRuntimeQuery } from "../definition
|
||||
import { optionsBuilder } from "../options";
|
||||
|
||||
const hideUnlessNavidrome = {
|
||||
shouldHide: (_: unknown, integrationKinds: string[]) => !integrationKinds.includes("navidrome"),
|
||||
shouldHide: (_: unknown, integrationKinds: string[]) =>
|
||||
!integrationKinds.includes("navidrome") && !integrationKinds.includes("mock"),
|
||||
};
|
||||
|
||||
const hideUnlessAudiobookshelf = {
|
||||
@@ -16,7 +17,7 @@ export const { definition, componentLoader } = createWidgetDefinition("audioStat
|
||||
supportsAdvancedFocus: true,
|
||||
queryKeys: [[["widget", "audioStats", "getStats"]], [["widget", "mediaServer", "getCurrentStreams"]]],
|
||||
queryMatcher: matchesWidgetRuntimeQuery,
|
||||
supportedIntegrations: ["navidrome", "audiobookshelf"],
|
||||
supportedIntegrations: ["navidrome", "audiobookshelf", "mock"],
|
||||
integrationsRequired: true,
|
||||
maxIntegrations: 1,
|
||||
createOptions() {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { optionsBuilder } from "../options";
|
||||
export const { definition, componentLoader } = createWidgetDefinition("bazarr", {
|
||||
supportsAdvancedFocus: true,
|
||||
icon: IconBadgeCc,
|
||||
supportedIntegrations: ["bazarr"],
|
||||
supportedIntegrations: ["bazarr", "mock"],
|
||||
integrationsRequired: true,
|
||||
maxIntegrations: 1,
|
||||
createOptions() {
|
||||
|
||||
@@ -19,7 +19,7 @@ export const { definition, componentLoader } = createWidgetDefinition("coolify",
|
||||
}),
|
||||
}));
|
||||
},
|
||||
supportedIntegrations: ["coolify"],
|
||||
supportedIntegrations: ["coolify", "mock"],
|
||||
errors: {
|
||||
INTERNAL_SERVER_ERROR: {
|
||||
icon: IconServerOff,
|
||||
|
||||
@@ -12,7 +12,7 @@ export const { definition, componentLoader } = createWidgetDefinition("firewall"
|
||||
createOptions() {
|
||||
return optionsBuilder.from(() => ({}));
|
||||
},
|
||||
supportedIntegrations: getIntegrationKindsByCategory("firewall"),
|
||||
supportedIntegrations: [...getIntegrationKindsByCategory("firewall"), "mock"],
|
||||
errors: {
|
||||
INTERNAL_SERVER_ERROR: {
|
||||
icon: IconWallOff,
|
||||
|
||||
@@ -18,8 +18,9 @@ describe("iframe policy", () => {
|
||||
expect(getSandboxFlags(permissions)).toContain("allow-modals");
|
||||
});
|
||||
|
||||
it("accepts only HTTP protocols", () => {
|
||||
it("accepts HTTP URLs and same-origin paths", () => {
|
||||
expect(isSupportedProtocol("https://example.com")).toBe(true);
|
||||
expect(isSupportedProtocol("/api/demo-assets/overview")).toBe(true);
|
||||
expect(isSupportedProtocol("javascript:alert(1)")).toBe(false);
|
||||
expect(isSupportedProtocol("not a url")).toBe(false);
|
||||
});
|
||||
|
||||
@@ -7,13 +7,15 @@ import { objectEntries } from "@homarr/common";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
|
||||
import type { WidgetComponentProps } from "../definition";
|
||||
import { getSafeApplicationUrl } from "../common/application-url";
|
||||
import classes from "./component.module.css";
|
||||
import { getSafeIframeUrl } from "./safe-url";
|
||||
|
||||
export { getSafeIframeUrl } from "./safe-url";
|
||||
|
||||
export default function IFrameWidget({ options, isEditMode }: WidgetComponentProps<"iframe">) {
|
||||
const t = useI18n("widget.iframe");
|
||||
const { embedUrl, allowScrolling, ...permissions } = options;
|
||||
const safeEmbedUrl = getSafeApplicationUrl(embedUrl);
|
||||
const safeEmbedUrl = getSafeIframeUrl(embedUrl);
|
||||
const allowedPermissions = getAllowedPermissions(permissions);
|
||||
const sandboxFlags = getSandboxFlags(permissions);
|
||||
|
||||
@@ -45,7 +47,7 @@ export default function IFrameWidget({ options, isEditMode }: WidgetComponentPro
|
||||
const supportedProtocols = ["http", "https"];
|
||||
|
||||
export const isSupportedProtocol = (url: string) => {
|
||||
return getSafeApplicationUrl(url) !== undefined;
|
||||
return getSafeIframeUrl(url) !== undefined;
|
||||
};
|
||||
|
||||
const NoUrl = () => {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { IconBrowser, IconExternalLink } from "@tabler/icons-react";
|
||||
|
||||
import { createWidgetDefinition } from "../definition";
|
||||
import { getSafeApplicationUrl } from "../common/application-url";
|
||||
import { optionsBuilder } from "../options";
|
||||
import { getSafeIframeUrl } from "./safe-url";
|
||||
|
||||
const createOptions = () =>
|
||||
optionsBuilder.from((factory) => ({
|
||||
@@ -23,7 +23,7 @@ export const { definition, componentLoader } = createWidgetDefinition("iframe",
|
||||
supportsAdvancedFocus: false,
|
||||
icon: IconBrowser,
|
||||
contextActions: ({ options }) => {
|
||||
const embedUrl = getSafeApplicationUrl(options.embedUrl);
|
||||
const embedUrl = getSafeIframeUrl(options.embedUrl);
|
||||
return embedUrl
|
||||
? [
|
||||
{
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { getSafeAppHref, getSafeApplicationUrl } from "@homarr/common";
|
||||
|
||||
export const getSafeIframeUrl = (url: unknown) => {
|
||||
const applicationUrl = getSafeApplicationUrl(url);
|
||||
if (applicationUrl) return applicationUrl;
|
||||
|
||||
const relativeUrl = getSafeAppHref(url);
|
||||
if (relativeUrl?.startsWith("/") && !relativeUrl.startsWith("//")) return relativeUrl;
|
||||
return undefined;
|
||||
};
|
||||
@@ -62,7 +62,7 @@ export const { definition, componentLoader } = createWidgetDefinition("immich-al
|
||||
: undefined,
|
||||
}),
|
||||
refetchInterval: null,
|
||||
supportedIntegrations: ["immich"],
|
||||
supportedIntegrations: ["immich", "mock"],
|
||||
integrationsRequired: true,
|
||||
maxIntegrations: 1,
|
||||
contextActions: ({ widgetRuntimeRef }) => {
|
||||
|
||||
@@ -9,7 +9,7 @@ export const { definition, componentLoader } = createWidgetDefinition("immich-se
|
||||
queryKeys: [[["widget", "immich", "getServerStats"]], [["widget", "immich", "getAlbums"]]],
|
||||
queryMatcher: matchesWidgetRuntimeQuery,
|
||||
refetchInterval: null,
|
||||
supportedIntegrations: ["immich"],
|
||||
supportedIntegrations: ["immich", "mock"],
|
||||
integrationsRequired: true,
|
||||
maxIntegrations: 1,
|
||||
createOptions() {
|
||||
|
||||
@@ -7,7 +7,7 @@ export const { definition, componentLoader } = createWidgetDefinition("paperless
|
||||
supportsAdvancedFocus: false,
|
||||
icon: IconFileText,
|
||||
refetchInterval: null,
|
||||
supportedIntegrations: ["paperlessNgx"],
|
||||
supportedIntegrations: ["paperlessNgx", "mock"],
|
||||
integrationsRequired: true,
|
||||
maxIntegrations: 1,
|
||||
createOptions() {
|
||||
|
||||
@@ -9,7 +9,7 @@ import { patchmonOptionsSuperRefine } from "./threshold-validation";
|
||||
export const { definition, componentLoader } = createWidgetDefinition("patchmon", {
|
||||
supportsAdvancedFocus: true,
|
||||
icon: IconShieldCheck,
|
||||
supportedIntegrations: ["patchmon"],
|
||||
supportedIntegrations: ["patchmon", "mock"],
|
||||
integrationsRequired: true,
|
||||
maxIntegrations: 1,
|
||||
createOptions() {
|
||||
|
||||
@@ -30,7 +30,7 @@ export const { definition, componentLoader } = createWidgetDefinition("speedtest
|
||||
},
|
||||
);
|
||||
},
|
||||
supportedIntegrations: ["speedtestTracker"],
|
||||
supportedIntegrations: ["speedtestTracker", "mock"],
|
||||
errors: {
|
||||
INTERNAL_SERVER_ERROR: {
|
||||
icon: IconServerOff,
|
||||
|
||||
@@ -9,7 +9,7 @@ export const { definition, componentLoader } = createWidgetDefinition("systemDis
|
||||
icon: IconServer2,
|
||||
queryKey: [["widget", "healthMonitoring"]],
|
||||
refetchInterval: 10,
|
||||
supportedIntegrations: ["dashDot", "openmediavault", "truenas", "unraid", "synology"],
|
||||
supportedIntegrations: ["dashDot", "openmediavault", "truenas", "unraid", "synology", "mock"],
|
||||
createOptions() {
|
||||
return optionsBuilder.from(
|
||||
(factory) => ({
|
||||
|
||||
@@ -24,7 +24,7 @@ export const { definition, componentLoader } = createWidgetDefinition("systemRes
|
||||
supportsAdvancedFocus: true,
|
||||
queryKey: [["widget", "healthMonitoring"]],
|
||||
refetchInterval: 10,
|
||||
supportedIntegrations: ["dashDot", "openmediavault", "truenas", "unraid", "glances", "synology"],
|
||||
supportedIntegrations: ["dashDot", "openmediavault", "truenas", "unraid", "glances", "synology", "mock"],
|
||||
createOptions() {
|
||||
return optionsBuilder.from((factory) => ({
|
||||
hasShadow: factory.switch({ defaultValue: true }),
|
||||
|
||||
@@ -24,7 +24,7 @@ export const { definition, componentLoader } = createWidgetDefinition("tracearr"
|
||||
}),
|
||||
}));
|
||||
},
|
||||
supportedIntegrations: ["tracearr"],
|
||||
supportedIntegrations: ["tracearr", "mock"],
|
||||
errors: {
|
||||
INTERNAL_SERVER_ERROR: {
|
||||
icon: IconServerOff,
|
||||
|
||||
@@ -19,7 +19,7 @@ export const { definition, componentLoader } = createWidgetDefinition("traefik",
|
||||
}),
|
||||
}));
|
||||
},
|
||||
supportedIntegrations: ["traefik"],
|
||||
supportedIntegrations: ["traefik", "mock"],
|
||||
errors: {
|
||||
INTERNAL_SERVER_ERROR: {
|
||||
icon: IconRouteOff,
|
||||
|
||||
@@ -18,7 +18,7 @@ export const { definition, componentLoader } = createWidgetDefinition("umami", {
|
||||
],
|
||||
queryMatcher: matchesWidgetRuntimeQuery,
|
||||
refetchInterval: null,
|
||||
supportedIntegrations: ["umami"],
|
||||
supportedIntegrations: ["umami", "mock"],
|
||||
integrationsRequired: true,
|
||||
maxIntegrations: 1,
|
||||
createOptions() {
|
||||
|
||||
@@ -34,7 +34,7 @@ export const { definition, componentLoader } = createWidgetDefinition("uptimeKum
|
||||
}),
|
||||
}));
|
||||
},
|
||||
supportedIntegrations: ["uptimeKuma"],
|
||||
supportedIntegrations: ["uptimeKuma", "mock"],
|
||||
errors: {
|
||||
INTERNAL_SERVER_ERROR: {
|
||||
icon: IconServerOff,
|
||||
|
||||
@@ -12,5 +12,5 @@ export const { componentLoader, definition } = createWidgetDefinition("vpn", {
|
||||
createOptions() {
|
||||
return optionsBuilder.from(() => ({}));
|
||||
},
|
||||
supportedIntegrations: getIntegrationKindsByCategory("vpn"),
|
||||
supportedIntegrations: [...getIntegrationKindsByCategory("vpn"), "mock"],
|
||||
}).withDynamicImport(() => import("./component"));
|
||||
@@ -7,7 +7,7 @@ export const { definition, componentLoader } = createWidgetDefinition("wud", {
|
||||
icon: IconBrandDocker,
|
||||
supportsAdvancedFocus: true,
|
||||
refetchInterval: null,
|
||||
supportedIntegrations: ["wud"],
|
||||
supportedIntegrations: ["wud", "mock"],
|
||||
integrationsRequired: true,
|
||||
maxIntegrations: 1,
|
||||
createOptions() {
|
||||
|
||||
Reference in new issue
Block a user