Skip to content

Commit ad80ea4

Browse files
authored
feat(mcp-ui): polish up ListDatabases UI and enable it to render as standalone component (#811)
1 parent 436b809 commit ad80ea4

File tree

9 files changed

+234
-67
lines changed

9 files changed

+234
-67
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,10 @@
9292
"@ai-sdk/openai": "^2.0.52",
9393
"@emotion/css": "^11.13.5",
9494
"@eslint/js": "^9.34.0",
95+
"@leafygreen-ui/lib": "^15.7.0",
9596
"@leafygreen-ui/table": "^15.2.2",
97+
"@leafygreen-ui/tokens": "^4.2.0",
98+
"@leafygreen-ui/typography": "^22.2.3",
9699
"@modelcontextprotocol/inspector": "^0.17.1",
97100
"@mongodb-js/oidc-mock-provider": "^0.12.0",
98101
"@redocly/cli": "^2.0.8",

pnpm-lock.yaml

Lines changed: 25 additions & 26 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/ui/build/mount.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/// <reference types="vite/client" />
2+
import "../styles/fonts.css";
23
import React from "react";
34
import { createRoot } from "react-dom/client";
45

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
import { css } from "@emotion/css";
2+
import { color, InteractionState, Property, spacing, Variant } from "@leafygreen-ui/tokens";
3+
import { Theme } from "@leafygreen-ui/lib";
24

3-
export const tableStyles = css`
4-
background: white;
5+
export const getContainerStyles = (darkMode: boolean): string => css`
6+
background-color: ${color[darkMode ? Theme.Dark : Theme.Light][Property.Background][Variant.Primary][
7+
InteractionState.Default
8+
]};
9+
padding: ${spacing[200]}px;
10+
`;
11+
12+
export const AmountTextStyles = css`
13+
margin-bottom: ${spacing[400]}px;
514
`;
Lines changed: 47 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,16 @@
1-
import React from "react";
2-
import { useRenderData } from "../../hooks/index.js";
3-
import {
4-
Cell as LGCell,
5-
HeaderCell as LGHeaderCell,
6-
HeaderRow,
7-
Row as LGRow,
8-
Table,
9-
TableBody,
10-
TableHead,
11-
} from "@leafygreen-ui/table";
12-
import { tableStyles } from "./ListDatabases.styles.js";
1+
import { type ReactElement } from "react";
2+
import { useDarkMode, useRenderData } from "../../hooks/index.js";
3+
import { Cell, HeaderCell, HeaderRow, Row, Table, TableBody, TableHead } from "@leafygreen-ui/table";
4+
import { Body } from "@leafygreen-ui/typography";
135
import type { ListDatabasesOutput } from "../../../tools/mongodb/metadata/listDatabases.js";
6+
import { AmountTextStyles, getContainerStyles } from "./ListDatabases.styles.js";
147

15-
const HeaderCell = LGHeaderCell as React.FC<React.ComponentPropsWithoutRef<"th">>;
16-
const Cell = LGCell as React.FC<React.ComponentPropsWithoutRef<"td">>;
17-
const Row = LGRow as React.FC<React.ComponentPropsWithoutRef<"tr">>;
8+
export type Database = ListDatabasesOutput["databases"][number];
9+
10+
interface ListDatabasesProps {
11+
databases?: Database[];
12+
darkMode?: boolean;
13+
}
1814

1915
function formatBytes(bytes: number): string {
2016
if (bytes === 0) return "0 Bytes";
@@ -26,37 +22,49 @@ function formatBytes(bytes: number): string {
2622
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + " " + sizes[i];
2723
}
2824

29-
export const ListDatabases = (): React.ReactElement | null => {
30-
const { data, isLoading, error } = useRenderData<ListDatabasesOutput>();
25+
export const ListDatabases = ({
26+
databases: propDatabases,
27+
darkMode: darkModeProp,
28+
}: ListDatabasesProps): ReactElement | null => {
29+
const darkMode = useDarkMode(darkModeProp);
30+
const { data: hookData, isLoading, error } = useRenderData<ListDatabasesOutput>();
31+
const databases = propDatabases ?? hookData?.databases;
3132

32-
if (isLoading) {
33-
return <div>Loading...</div>;
34-
}
33+
if (!propDatabases) {
34+
if (isLoading) {
35+
return <div>Loading...</div>;
36+
}
3537

36-
if (error) {
37-
return <div>Error: {error}</div>;
38+
if (error) {
39+
return <div>Error: {error}</div>;
40+
}
3841
}
3942

40-
if (!data) {
43+
if (!databases) {
4144
return null;
4245
}
4346

4447
return (
45-
<Table className={tableStyles}>
46-
<TableHead>
47-
<HeaderRow>
48-
<HeaderCell>DB Name</HeaderCell>
49-
<HeaderCell>DB Size</HeaderCell>
50-
</HeaderRow>
51-
</TableHead>
52-
<TableBody>
53-
{data.databases.map((db) => (
54-
<Row key={db.name}>
55-
<Cell>{db.name}</Cell>
56-
<Cell>{formatBytes(db.size)}</Cell>
57-
</Row>
58-
))}
59-
</TableBody>
60-
</Table>
48+
<div className={getContainerStyles(darkMode)}>
49+
<Body className={AmountTextStyles} darkMode={darkMode}>
50+
Your cluster has <strong>{databases.length} databases</strong>:
51+
</Body>
52+
<Table darkMode={darkMode}>
53+
<TableHead>
54+
<HeaderRow>
55+
<HeaderCell>Database</HeaderCell>
56+
<HeaderCell>Size</HeaderCell>
57+
</HeaderRow>
58+
</TableHead>
59+
<TableBody>
60+
{databases.map((db) => (
61+
<Row key={db.name}>
62+
<Cell>{db.name}</Cell>
63+
<Cell>{formatBytes(db.size)}</Cell>
64+
</Row>
65+
))}
66+
</TableBody>
67+
</Table>
68+
</div>
6169
);
6270
};

src/ui/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
export { useDarkMode } from "./useDarkMode.js";
12
export { useRenderData } from "./useRenderData.js";
23
export { useHostCommunication } from "./useHostCommunication.js";

src/ui/hooks/useDarkMode.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { useSyncExternalStore } from "react";
2+
3+
function subscribeToPrefersColorScheme(callback: () => void): () => void {
4+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
5+
mediaQuery.addEventListener("change", callback);
6+
return () => mediaQuery.removeEventListener("change", callback);
7+
}
8+
9+
function getPrefersDarkMode(): boolean {
10+
return window.matchMedia("(prefers-color-scheme: dark)").matches;
11+
}
12+
13+
export function useDarkMode(override?: boolean): boolean {
14+
const prefersDarkMode = useSyncExternalStore(subscribeToPrefersColorScheme, getPrefersDarkMode);
15+
return override ?? prefersDarkMode;
16+
}

src/ui/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
export { UIRegistry } from "./registry/index.js";
2+
export { ListDatabases } from "./components/ListDatabases/index.js";
3+
export type { Database } from "./components/ListDatabases/ListDatabases.js";

0 commit comments

Comments
 (0)