diff --git a/frontend/src/expr/JsonTree.test.tsx b/frontend/src/expr/JsonTree.test.tsx
index 267d123..b0aec2c 100644
--- a/frontend/src/expr/JsonTree.test.tsx
+++ b/frontend/src/expr/JsonTree.test.tsx
@@ -54,6 +54,13 @@ describe("JsonTree", () => {
expect(onPick).not.toHaveBeenCalled();
});
+ it("branches still expand when pickable is false (an un-pickable tree stays browsable)", async () => {
+ render();
+ expect(screen.queryByText("b")).not.toBeInTheDocument();
+ await userEvent.click(screen.getByText("a"));
+ expect(screen.getByText("b")).toBeInTheDocument();
+ });
+
it("renders empty state for empty object", () => {
render();
expect(screen.getByText(/no data/i)).toBeInTheDocument();
diff --git a/frontend/src/expr/JsonTree.tsx b/frontend/src/expr/JsonTree.tsx
index 937bfae..9e0f43b 100644
--- a/frontend/src/expr/JsonTree.tsx
+++ b/frontend/src/expr/JsonTree.tsx
@@ -54,11 +54,12 @@ function Node({ label, value, path, depth, pickable, onPick }: NodeProps) {
function BranchOrLeaf({ label, value, path, depth, pickable, onPick }: NodeProps) {
const branch = isBranch(value);
const [open, setOpen] = useState(false);
- const clickable = pickable;
+ // Branches always expand/collapse (so an un-pickable tree can still be
+ // browsed — e.g. the Output column in the node editor dialog). Only leaf
+ // clicks are gated by `pickable`.
const pickHandler = () => {
- if (!pickable) return;
- if (branch) setOpen((o) => !o);
- else onPick?.(path);
+ if (branch) { setOpen((o) => !o); return; }
+ if (pickable) onPick?.(path);
};
return (
@@ -67,8 +68,8 @@ function BranchOrLeaf({ label, value, path, depth, pickable, onPick }: NodeProps
onClick={pickHandler}
className={
"flex items-center gap-1 w-full text-left py-0.5 px-1 rounded " +
- (pickable ? "hover:bg-slate-800/80 " : "opacity-50 cursor-default ") +
- (clickable ? "" : "cursor-default")
+ "hover:bg-slate-800/80 " +
+ (!pickable && !branch ? "opacity-50 cursor-default" : "")
}
>
{branch ? (open ? : ) : }