fix(web): remove mantine-border-animate, use green highlight border (#1536)
* fix(web): remove mantine-border-animate, use green highlight border (#1535) The @gfazioli/mantine-border-animate dependency emitted a persistent React "forwardRef render functions accept exactly two parameters" warning on every page load. Remove the dependency entirely and replace the freshly-added card's animated border with a green border. - Drop @gfazioli/mantine-border-animate from clients/web (package + lock) and remove its styles.css import from main.tsx. - Add a `highlighted` variant to the Card theme that draws a 2px green border, driven by a new --inspector-highlight-border token in App.css. - ServerCard now selects the variant (disabled > highlighted) instead of wrapping in BorderAnimate; the BorderAnimate flex-wrapper `w="100%"` workaround is no longer needed. - Update ServerCard/ServerListScreen tests to assert the highlighted variant instead of the removed BorderAnimate DOM. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(web): drop redundant Card props on ServerCard (#1535) ThemeCard.defaultProps already sets withBorder and padding="lg", so the explicit props on ServerCard's <Card> were redundant. Addresses review feedback on PR #1536. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(web): note highlighted Card variant relies on inherited withBorder (#1535) Addresses a maintainability flag from the PR #1536 re-review: the highlighted variant only sets borderColor/borderWidth and depends on the withBorder:true default, so make that coupling explicit in a comment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1dc0c744cf
commit
0b0bd40e5d
8 changed files with 48 additions and 75 deletions
42
clients/web/package-lock.json
generated
42
clients/web/package-lock.json
generated
|
|
@ -12,7 +12,6 @@
|
|||
"@dnd-kit/sortable": "^8.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@gfazioli/mantine-border-animate": "^2.0.3",
|
||||
"@hono/node-server": "^1.19.14",
|
||||
"@mantine/core": "^8.3.17",
|
||||
"@mantine/form": "^8.3.17",
|
||||
|
|
@ -1242,19 +1241,6 @@
|
|||
"integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@gfazioli/mantine-border-animate": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@gfazioli/mantine-border-animate/-/mantine-border-animate-2.0.3.tgz",
|
||||
"integrity": "sha512-6JmO4Cox2KAjIXWWNIewwuudTtTGS5QrcWqjgch8Kn1Zu2bwhZAk4n1y2z7YNJsnfZGVM3XcxLbdGivJcCiNMQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@mantine/core": ">=7.0.0",
|
||||
"@mantine/hooks": ">=7.0.0",
|
||||
"@tabler/icons-react": "^3.34.0",
|
||||
"react": "^18.x || ^19.x",
|
||||
"react-dom": "^18.x || ^19.x"
|
||||
}
|
||||
},
|
||||
"node_modules/@hono/node-server": {
|
||||
"version": "1.19.14",
|
||||
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz",
|
||||
|
|
@ -2689,34 +2675,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@tabler/icons": {
|
||||
"version": "3.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.44.0.tgz",
|
||||
"integrity": "sha512-Wn0AOZG9sg0L+bjfMqq4eNhC6pQjIrk94LvvWYNYkY8KH8wC3YILRzQlrnVJc4FUeMxH/AK97QsYCX35H3LndA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/codecalm"
|
||||
}
|
||||
},
|
||||
"node_modules/@tabler/icons-react": {
|
||||
"version": "3.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-3.44.0.tgz",
|
||||
"integrity": "sha512-8+rvzBbVm/1Z3sG3x7GUNAaxIKxwgz8xaMhRs23nrCnMTKRFAhEC+82zAIFeAA0seXdrAGX5HFCkaLpGK2rVHg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@tabler/icons": "3.44.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/codecalm"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/dom": {
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@
|
|||
"@dnd-kit/sortable": "^8.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@gfazioli/mantine-border-animate": "^2.0.3",
|
||||
"@hono/node-server": "^1.19.14",
|
||||
"@mantine/core": "^8.3.17",
|
||||
"@mantine/form": "^8.3.17",
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@
|
|||
/* Red glow pulsed around a newly appearing tab's label (#1450) */
|
||||
--inspector-glow-red: var(--mantine-color-red-6);
|
||||
|
||||
/* Green border drawn around a freshly-added/highlighted server card (#1535) */
|
||||
--inspector-highlight-border: var(--mantine-color-green-6);
|
||||
|
||||
/* ── Log levels (RFC 5424) ─────────────────────────────── */
|
||||
--inspector-log-debug: var(--mantine-color-gray-6);
|
||||
--inspector-log-info: var(--mantine-color-blue-6);
|
||||
|
|
|
|||
|
|
@ -291,6 +291,22 @@ describe("ServerCard", () => {
|
|||
}
|
||||
});
|
||||
|
||||
it("draws the green highlight-variant border when highlighted", () => {
|
||||
const { container } = renderWithMantine(
|
||||
<ServerCard {...baseProps} highlighted />,
|
||||
);
|
||||
expect(
|
||||
container.querySelector('[data-variant="highlighted"]'),
|
||||
).not.toBeNull();
|
||||
});
|
||||
|
||||
it("does not draw the highlight border when not highlighted", () => {
|
||||
const { container } = renderWithMantine(<ServerCard {...baseProps} />);
|
||||
expect(
|
||||
container.querySelector('[data-variant="highlighted"]'),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("does not scroll when not highlighted", () => {
|
||||
const scrollIntoView = vi.fn();
|
||||
const orig = Element.prototype.scrollIntoView;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { useEffect, useRef, type ReactNode } from "react";
|
||||
import { Badge, Button, Card, Group, Stack, Text } from "@mantine/core";
|
||||
import { BorderAnimate } from "@gfazioli/mantine-border-animate";
|
||||
import type {
|
||||
MCPServerConfig,
|
||||
ServerEntry,
|
||||
|
|
@ -35,9 +34,8 @@ export interface ServerCardProps extends ServerEntry {
|
|||
*/
|
||||
dragHandle?: ReactNode;
|
||||
/**
|
||||
* When true, the card is freshly added: it draws an animated border (Mantine
|
||||
* Border Animate) to draw the eye. Cleared by `onClearHighlight` on any click
|
||||
* on the card.
|
||||
* When true, the card is freshly added: it draws a green border to draw the
|
||||
* eye. Cleared by `onClearHighlight` on any click on the card.
|
||||
*/
|
||||
highlighted?: boolean;
|
||||
/**
|
||||
|
|
@ -47,7 +45,7 @@ export interface ServerCardProps extends ServerEntry {
|
|||
* Defaults to true.
|
||||
*/
|
||||
scrollOnHighlight?: boolean;
|
||||
/** Called when a highlighted card is clicked, to dismiss the animated border. */
|
||||
/** Called when a highlighted card is clicked, to dismiss the green border. */
|
||||
onClearHighlight?: () => void;
|
||||
}
|
||||
|
||||
|
|
@ -156,16 +154,18 @@ export function ServerCard({
|
|||
const protocolVersion =
|
||||
connection.status === "connected" ? connection.protocolVersion : undefined;
|
||||
|
||||
const card = (
|
||||
// A dimmed card (another server is active) is inert, so the disabled variant
|
||||
// wins; otherwise a freshly-added card draws the highlighted green border.
|
||||
const variant = isDimmed
|
||||
? "disabled"
|
||||
: highlighted
|
||||
? "highlighted"
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<Card
|
||||
ref={rootRef}
|
||||
withBorder
|
||||
padding="lg"
|
||||
// BorderAnimate's wrapper is display:flex, so the card must stretch to
|
||||
// fill it — otherwise the card shrinks to content width while the
|
||||
// animated border spans the full grid cell.
|
||||
w="100%"
|
||||
variant={isDimmed ? "disabled" : undefined}
|
||||
variant={variant}
|
||||
onClick={highlighted ? onClearHighlight : undefined}
|
||||
{...(isDimmed ? { "aria-disabled": true, inert: true } : {})}
|
||||
>
|
||||
|
|
@ -237,19 +237,4 @@ export function ServerCard({
|
|||
</Stack>
|
||||
</Card>
|
||||
);
|
||||
|
||||
// Always render the wrapper and toggle `show`/`animate` rather than
|
||||
// conditionally wrapping — swapping the element type on clear would remount
|
||||
// the card (and its connect toggle / buttons), swallowing the click that
|
||||
// triggered the clear.
|
||||
return (
|
||||
<BorderAnimate
|
||||
show={highlighted}
|
||||
animate={highlighted}
|
||||
radius="md"
|
||||
w="100%"
|
||||
>
|
||||
{card}
|
||||
</BorderAnimate>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,16 +137,16 @@ describe("ServerListScreen", () => {
|
|||
});
|
||||
|
||||
describe("freshly-added highlight", () => {
|
||||
it("draws an animated border on every highlighted server", () => {
|
||||
it("draws a green highlight border on every highlighted server", () => {
|
||||
const { container } = renderWithMantine(
|
||||
<ServerListScreen
|
||||
{...baseProps}
|
||||
highlightedServerIds={["alpha", "beta"]}
|
||||
/>,
|
||||
);
|
||||
// One animated border per highlighted card.
|
||||
// One highlighted-variant card per highlighted server.
|
||||
expect(
|
||||
container.querySelectorAll(".mantine-BorderAnimate-border"),
|
||||
container.querySelectorAll('[data-variant="highlighted"]'),
|
||||
).toHaveLength(2);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import { MantineProvider, type CSSVariablesResolver } from "@mantine/core";
|
|||
import { Notifications } from "@mantine/notifications";
|
||||
import "@mantine/core/styles.css";
|
||||
import "@mantine/notifications/styles.css";
|
||||
import "@gfazioli/mantine-border-animate/styles.css";
|
||||
import "./App.css";
|
||||
import { theme } from "./theme/theme";
|
||||
import App from "./App";
|
||||
|
|
|
|||
|
|
@ -37,6 +37,19 @@ export const ThemeCard = Card.extend({
|
|||
},
|
||||
};
|
||||
}
|
||||
if (props.variant === "highlighted") {
|
||||
// Freshly-added / called-out server card: a prominent green border draws
|
||||
// the eye until the highlight is dismissed (#1535). Only the color/width
|
||||
// are set here — the border itself comes from the inherited
|
||||
// `withBorder: true` default above, so keep that default if changing it.
|
||||
return {
|
||||
root: {
|
||||
backgroundColor: "var(--inspector-surface-card)",
|
||||
borderColor: "var(--inspector-highlight-border)",
|
||||
borderWidth: 2,
|
||||
},
|
||||
};
|
||||
}
|
||||
if (props.variant === "preview") {
|
||||
// Container for the resource preview / template form panels: sizes to
|
||||
// content (no forced height) but caps at the screen's available area
|
||||
|
|
|
|||
Loading…
Reference in a new issue