diff --git a/frontend/src/KnowledgeGraph.css b/frontend/src/KnowledgeGraph.css index 7f2ef7f76..e22536fc8 100644 --- a/frontend/src/KnowledgeGraph.css +++ b/frontend/src/KnowledgeGraph.css @@ -20,9 +20,9 @@ .knowledge-graph-controls button { align-items: center; background: var(--surface); - border: 1px solid var(--line-strong); + border: 1px solid var(--border); border-radius: 0.45rem; - color: var(--ink); + color: var(--text-h); display: inline-flex; font: inherit; justify-content: center; @@ -44,7 +44,7 @@ } .knowledge-graph-controls span { - color: var(--muted); + color: var(--text-muted); font-variant-numeric: tabular-nums; min-width: 3.25rem; text-align: center; @@ -55,14 +55,14 @@ } .knowledge-graph-description { - color: var(--muted); + color: var(--text-muted); margin: 0.2rem 0 0; } .knowledge-graph-viewport { cursor: grab; overflow: hidden; - border: 1px solid var(--line); + border: 1px solid var(--border); border-radius: 0.75rem; background: var(--surface); touch-action: none; @@ -78,19 +78,19 @@ } .knowledge-graph-arrow { - fill: var(--muted); + fill: var(--text-muted); } .knowledge-graph-edge path { fill: none; marker-end: inherit; - stroke: var(--line-strong); + stroke: var(--border); stroke-width: 1.3; } .knowledge-graph-node rect { fill: var(--surface); - stroke: var(--line-strong); + stroke: var(--border); stroke-width: 1.5; } @@ -101,8 +101,8 @@ } .knowledge-graph-node.evidence rect { - fill: color-mix(in srgb, var(--warning) 10%, var(--surface)); - stroke: var(--warning); + fill: color-mix(in srgb, var(--color-accent-orange) 10%, var(--surface)); + stroke: var(--color-accent-orange); stroke-dasharray: 4 3; } @@ -122,18 +122,18 @@ } .knowledge-graph-node-label { - fill: var(--ink); + fill: var(--text-h); font-size: 12px; font-weight: 650; } .knowledge-graph-node-type { - fill: var(--muted); + fill: var(--text-muted); font-size: 9px; } .knowledge-graph-evidence { - border-top: 1px solid var(--line); + border-top: 1px solid var(--border); padding-top: 0.7rem; } diff --git a/frontend/src/KnowledgeGraph.css.test.ts b/frontend/src/KnowledgeGraph.css.test.ts new file mode 100644 index 000000000..88185c59e --- /dev/null +++ b/frontend/src/KnowledgeGraph.css.test.ts @@ -0,0 +1,15 @@ +/// +import { readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, expect, it } from "vitest"; + +const here = dirname(fileURLToPath(import.meta.url)); +const css = readFileSync(join(here, "KnowledgeGraph.css"), "utf-8"); + +describe("Knowledge Graph CSS contract", () => { + it("uses only defined shared tokens for graph colors", () => { + expect(css).not.toMatch(/var\(\s*--(?:ink|muted|line|line-strong|warning)\b/); + expect(css).toContain("var(--color-accent-orange)"); + }); +}); diff --git a/frontend/src/KnowledgeGraph.stories.tsx b/frontend/src/KnowledgeGraph.stories.tsx new file mode 100644 index 000000000..420fef7ee --- /dev/null +++ b/frontend/src/KnowledgeGraph.stories.tsx @@ -0,0 +1,82 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { KnowledgeGraphView } from "./KnowledgeGraph"; +import type { KnowledgeGraph } from "./api"; + +const mixedStateGraph: KnowledgeGraph = { + post_id: "post-1", + nodes: [ + { + id: "post-1", + node_type_code: "node_post", + node_id: "post-1", + label: "Case introduction meeting", + is_focus: true, + }, + { + id: "team-1", + node_type_code: "node_team", + node_id: "team-1", + label: "Case design team", + ontology_label: "Team", + is_focus: false, + is_evidence_text_node: false, + }, + { + id: "organization-1", + node_type_code: "node_corporate_entity", + node_id: "organization-1", + label: "Case main contractor", + ontology_label: "Organization", + is_focus: false, + is_evidence_text_node: true, + }, + { + id: "project-1", + node_type_code: "node_project", + node_id: "project-1", + label: "Case grid project", + ontology_label: "Project", + is_focus: false, + is_evidence_text_node: true, + }, + ], + edges: [ + { + source: "post-1", + target: "team-1", + edge_type_code: "edge_mention", + ontology_label: "Mentioned in post", + confidence: 0.9, + evidence_post_ids: ["post-1"], + }, + { + source: "organization-1", + target: "project-1", + edge_type_code: "edge_responsible_for", + ontology_label: "Responsible for", + confidence: 0.82, + evidence_text: "Main contract evidence", + evidence_post_ids: ["post-1"], + }, + ], +}; + +const meta = { + title: "Evidence/KnowledgeGraph", + component: KnowledgeGraphView, + parameters: { layout: "padded" }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const MixedNodeStates: Story = { + args: { graph: mixedStateGraph, onSelectPost: () => undefined }, +}; + +export const EmptyState: Story = { + args: { + graph: { post_id: "post-1", nodes: [], edges: [] }, + onSelectPost: () => undefined, + }, +};