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();
+ });
+});