diff --git a/js/packages/ui/src/components/schema/SchemaView.tsx b/js/packages/ui/src/components/schema/SchemaView.tsx index 97b886f63..166ebbdb2 100644 --- a/js/packages/ui/src/components/schema/SchemaView.tsx +++ b/js/packages/ui/src/components/schema/SchemaView.tsx @@ -24,6 +24,7 @@ import { useLineageViewContext, useRecceServerFlag, } from "../../contexts"; +import { useThemeColors } from "../../hooks"; import { useInlineProfileDistribution } from "../../hooks/useInlineProfileDistribution"; import { trackColumnLevelLineage } from "../../lib/api/track"; import type { @@ -36,12 +37,41 @@ import { ScreenshotDataGrid, } from "../../primitives"; import { ProfileDistributionUnsupportedBanner } from "../data/ProfileDistributionUnsupportedBanner"; +import { TreatmentChip } from "../lineage/TreatmentChip"; +import { pickGraphBadge, pickTitleChip } from "../lineage/wholeModelTreatment"; import { createDataGridFromData } from "../ui/dataGrid"; import type { SchemaDistributionData } from "../ui/dataGrid/schemaCells"; import { getColumnChangeStatus } from "./getColumnChangeStatus"; import { selectInlineProfileScope } from "./selectInlineProfileScope"; export function SchemaLegend() { + const { isDark } = useThemeColors(); + const { data: serverFlags } = useRecceServerFlag(); + const newCllExperience = serverFlags?.new_cll_experience ?? false; + + // Reuse the same resolution + token palette that the lineage graph and + // NodeView header use, so the legend chips match the in-product badges. + // Gated on the same flag that controls whether the badges render at all. + const wholeModelChip = pickTitleChip( + { + newCllExperience, + isWholeModelChanged: false, + isWholeModelImpacted: true, + isImpacted: true, + }, + isDark, + ); + const additiveBadge = pickGraphBadge( + { + newCllExperience, + isWholeModelChanged: false, + isWholeModelImpacted: false, + isImpacted: true, + changeCategory: "non_breaking", + }, + isDark, + ); + return ( {" "} changed + {wholeModelChip && ( + + + ALL + {" "} + whole-model — every row in this model can be affected + + )} + {additiveBadge && ( + + + {additiveBadge.text} + {" "} + additive — adds a column; existing rows unchanged + + )} ); } diff --git a/js/packages/ui/src/components/schema/__tests__/SchemaView.test.tsx b/js/packages/ui/src/components/schema/__tests__/SchemaView.test.tsx index f5046b8c2..b07f7427b 100644 --- a/js/packages/ui/src/components/schema/__tests__/SchemaView.test.tsx +++ b/js/packages/ui/src/components/schema/__tests__/SchemaView.test.tsx @@ -17,7 +17,7 @@ import userEvent from "@testing-library/user-event"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { NodeData } from "../../../api"; import { theme } from "../../../theme"; -import { SchemaView } from "../SchemaView"; +import { SchemaLegend, SchemaView } from "../SchemaView"; const { flags, distribution, lineageViewContext } = vi.hoisted(() => ({ flags: { @@ -126,3 +126,39 @@ describe("SchemaView inline-profile wiring", () => { expect(screen.getByRole("button", BUTTON)).toBeInTheDocument(); }); }); + +const wrapLegend = () => ( + + + + +); + +describe("SchemaLegend", () => { + it("always shows the added / removed / changed entries", () => { + render(wrapLegend()); + expect(screen.getByText("added")).toBeInTheDocument(); + expect(screen.getByText("removed")).toBeInTheDocument(); + expect(screen.getByText("changed")).toBeInTheDocument(); + }); + + it("documents the whole-model and additive badges when new_cll_experience is on", () => { + flags.current = { new_cll_experience: true }; + render(wrapLegend()); + expect(screen.getByLabelText("whole-model")).toHaveTextContent("ALL"); + expect(screen.getByLabelText("additive change")).toHaveTextContent("ADD"); + expect( + screen.getByText(/every row in this model can be affected/), + ).toBeInTheDocument(); + expect( + screen.getByText(/adds a column; existing rows unchanged/), + ).toBeInTheDocument(); + }); + + it("hides the whole-model and additive badges when new_cll_experience is off", () => { + flags.current = { new_cll_experience: false }; + render(wrapLegend()); + expect(screen.queryByLabelText("whole-model")).not.toBeInTheDocument(); + expect(screen.queryByLabelText("additive change")).not.toBeInTheDocument(); + }); +});