fix(ci): add css module types and resolve typecheck/lint warnings in UI and modals

This commit is contained in:
Thomas Camlong committed 2026-08-21 00:48:39 +02:00
1 parent 7a7d4449c1
commit 858af3a48a
7 files changed
+130 -118

No files matched your search

@@ -161,7 +161,7 @@ describe("Advanced Add App - Modal Subcomponents", () => {
await act(async () =>
root.render(
<MantineProvider>
<WidgetItem item={item} isSelected={false} connectionStatus="ready" onSelect={onSelect} />
<WidgetItem item={item} disabled={false} loading={false} connectionStatus="ready" onSelect={onSelect} />
</MantineProvider>,
),
);
@@ -192,7 +192,13 @@ describe("Advanced Add App - Modal Subcomponents", () => {
await act(async () =>
root.render(
<MantineProvider>
<WidgetItem item={item} isSelected={true} connectionStatus="noConnectionRequired" onSelect={vi.fn()} />
<WidgetItem
item={item}
disabled={false}
loading={false}
connectionStatus="noConnectionRequired"
onSelect={vi.fn()}
/>
</MantineProvider>,
),
);
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useRef, useState } from "react";
import { useMemo, useRef, useState } from "react";
import {
Avatar,
Badge,
@@ -66,7 +66,7 @@ interface ItemSelectModalContentProps {
initialSearch?: string;
}
interface WidgetItemData {
export interface WidgetItemData {
kind: WidgetKind;
supportedIntegrations: IntegrationKind[];
icon: TablerIcon;
@@ -556,7 +556,7 @@ export const WidgetItem = ({
);
};
const ConnectionStatusBadge = ({ status }: { status: WidgetConnectionStatus }) => {
export const ConnectionStatusBadge = ({ status }: { status: WidgetConnectionStatus }) => {
if (status === "noConnectionRequired") return null;
const t = useI18n();
const color = status === "ready" ? "green" : status === "needsSetup" ? "yellow" : "gray";
@@ -568,7 +568,7 @@ const ConnectionStatusBadge = ({ status }: { status: WidgetConnectionStatus }) =
);
};
const SupportedIntegrations = ({ integrations }: { integrations: IntegrationKind[] }) => {
export const SupportedIntegrations = ({ integrations }: { integrations: IntegrationKind[] }) => {
if (integrations.length === 0) {
return (
<Text size="xs" c="dimmed" fs="italic">
@@ -0,0 +1,7 @@
declare const classes: {
readonly root: string;
readonly header: string;
readonly body: string;
readonly footer: string;
};
export default classes;
@@ -3,7 +3,7 @@
import { act, createRef } from "react";
import { createRoot } from "react-dom/client";
import type { Root } from "react-dom/client";
import { Badge, MantineProvider, Text } from "@mantine/core";
import { Badge, MantineProvider } from "@mantine/core";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { SelectableCard } from "./selectable-card";
@@ -7,7 +7,6 @@ import {
factory,
Group,
LoadingOverlay,
Stack,
Text,
Tooltip,
useProps,
@@ -54,124 +53,126 @@ const varsResolver = createVarsResolver<SelectableCardFactory>((_theme, { radius
},
}));
export const SelectableCard = factory<SelectableCardFactory>((_props, ref) => {
const props = useProps("SelectableCard", defaultProps, _props);
const {
classNames,
className,
style,
styles,
unstyled,
vars,
attributes,
icon,
title,
topRight,
description,
footerLeft,
footerRight,
selected,
loading = false,
disabled = false,
children,
type = "button",
...others
} = props;
export const SelectableCard = factory<SelectableCardFactory>(
(_props: SelectableCardProps, ref: React.ForwardedRef<HTMLButtonElement>) => {
const props = useProps("SelectableCard", defaultProps, _props);
const {
classNames,
className,
style,
styles,
unstyled,
vars,
attributes,
icon,
title,
topRight,
description,
footerLeft,
footerRight,
selected,
loading = false,
disabled = false,
children,
type = "button",
...others
} = props;
const getStyles = useStyles<SelectableCardFactory>({
name: "SelectableCard",
classes,
props,
className,
style,
classNames,
styles,
unstyled,
vars,
attributes,
varsResolver,
});
const getStyles = useStyles<SelectableCardFactory>({
name: "SelectableCard",
classes,
props,
className,
style,
classNames,
styles,
unstyled,
vars,
attributes,
varsResolver,
});
return (
<Box
component="button"
type={type}
ref={ref}
disabled={disabled || loading}
data-selected={selected || undefined}
aria-pressed={selected !== undefined ? selected : undefined}
{...getStyles("root")}
{...others}
>
<LoadingOverlay visible={loading} loaderProps={{ size: "sm" }} />
return (
<Box
component="button"
type={type}
ref={ref}
disabled={disabled || loading}
data-selected={selected || undefined}
aria-pressed={selected !== undefined ? selected : undefined}
{...getStyles("root")}
{...others}
>
<LoadingOverlay visible={loading} loaderProps={{ size: "sm" }} />
{/* Header Section: Inset background with Title that never elides before the category */}
{(icon || title || topRight) && (
<Box {...getStyles("header")}>
<Group
justify="space-between"
align="center"
wrap="nowrap"
gap="xs"
style={{ width: "100%", overflow: "hidden" }}
>
<Group gap="xs" wrap="nowrap" style={{ flex: "1 1 auto", minWidth: 0, overflow: "hidden" }}>
{icon}
{typeof title === "string" ? (
<Text
fw={700}
size="md"
{/* Header Section: Inset background with Title that never elides before the category */}
{(icon || title || topRight) && (
<Box {...getStyles("header")}>
<Group
justify="space-between"
align="center"
wrap="nowrap"
gap="xs"
style={{ width: "100%", overflow: "hidden" }}
>
<Group gap="xs" wrap="nowrap" style={{ flex: "1 1 auto", minWidth: 0, overflow: "hidden" }}>
{icon}
{typeof title === "string" ? (
<Text
fw={700}
size="md"
style={{
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
}}
>
{title}
</Text>
) : (
title
)}
</Group>
{topRight && (
<Box
style={{
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
minWidth: 0,
flexShrink: 0,
}}
>
{title}
</Text>
) : (
title
{topRight}
</Box>
)}
</Group>
{topRight && (
<Box
style={{
minWidth: 0,
flexShrink: 0,
}}
>
{topRight}
</Box>
)}
</Group>
</Box>
)}
</Box>
)}
{/* Body: Custom Children or Default Description */}
{children ? (
<Box {...getStyles("body")}>{children}</Box>
) : description !== undefined ? (
<Box {...getStyles("body")}>
<Tooltip label={description} multiline w={260} disabled={!description}>
<Text size="sm" c="dimmed" lineClamp={2} lh={1.35}>
{description || "No description provided"}
</Text>
</Tooltip>
</Box>
) : null}
{/* Body: Custom Children or Default Description */}
{children ? (
<Box {...getStyles("body")}>{children}</Box>
) : description !== undefined ? (
<Box {...getStyles("body")}>
<Tooltip label={description} multiline w={260} disabled={!description}>
<Text size="sm" c="dimmed" lineClamp={2} lh={1.35}>
{description || "No description provided"}
</Text>
</Tooltip>
</Box>
) : null}
{/* Footer Section: Inset Dashed Bottom Meta */}
{(footerLeft || footerRight) && (
<Box {...getStyles("footer")}>
<Group justify="space-between" align="center" wrap="nowrap" gap="xs" style={{ width: "100%" }}>
<Box style={{ minWidth: 0, flexShrink: 1 }}>{footerLeft}</Box>
<Box style={{ minWidth: 0, flexShrink: 0 }}>{footerRight}</Box>
</Group>
</Box>
)}
</Box>
);
});
{/* Footer Section: Inset Dashed Bottom Meta */}
{(footerLeft || footerRight) && (
<Box {...getStyles("footer")}>
<Group justify="space-between" align="center" wrap="nowrap" gap="xs" style={{ width: "100%" }}>
<Box style={{ minWidth: 0, flexShrink: 1 }}>{footerLeft}</Box>
<Box style={{ minWidth: 0, flexShrink: 0 }}>{footerRight}</Box>
</Group>
</Box>
)}
</Box>
);
},
);
SelectableCard.displayName = "@homarr/ui/SelectableCard";
SelectableCard.classes = classes;
@@ -13,7 +13,6 @@ import {
IconBuildingBank,
IconBusStop,
IconCalendar,
IconCalendarWeek,
IconChartBar,
IconClock,
IconClockPlay,
-1
View File
@@ -15,7 +15,6 @@ import {
IconBuildingBank,
IconBusStop,
IconCalendar,
IconCalendarWeek,
IconChartAreaLine,
IconChartBar,
IconClock,