fix(FN-3797): restore org chart connector endpoints for wide subtrees
- Compute first/last child subtree leaf counts in AgentsView and expose them as CSS variables on org-chart child groups - Update org chart connector offset math to anchor horizontal bars to first/last child subtree centers instead of fixed node half-widths - Keep defaults tokenized for single-child cases and preserve vertical-layout connector behavior - Extend dashboard CSS and component tests to assert the new connector offset variables and connector rule coverage Fusion-Task-Id: FN-3797
This commit is contained in:
@@ -84,6 +84,12 @@ describe("Agent CSS classes", () => {
|
||||
expect(orgChartSection).toContain("--org-chart-connector-gap: var(--space-sm)");
|
||||
expect(orgChartSection).toContain("--org-chart-sibling-gap: var(--space-xl)");
|
||||
expect(orgChartSection).toContain("--org-chart-children-offset: calc(var(--space-lg) + var(--space-sm))");
|
||||
expect(orgChartSection).toContain("--org-chart-first-child-leaves: 1");
|
||||
expect(orgChartSection).toContain("--org-chart-last-child-leaves: 1");
|
||||
expect(orgChartSection).toContain("--org-chart-first-child-center-offset");
|
||||
expect(orgChartSection).toContain("--org-chart-last-child-center-offset");
|
||||
expect(orgChartSection).toContain("left: var(--org-chart-first-child-center-offset)");
|
||||
expect(orgChartSection).toContain("right: var(--org-chart-last-child-center-offset)");
|
||||
expect(orgChartSection).toContain("min-height: var(--org-chart-node-width)");
|
||||
expect(orgChartSection).toContain("touch-action: pan-x pan-y");
|
||||
expect(orgChartSection).toContain("overflow: auto");
|
||||
|
||||
@@ -853,6 +853,8 @@
|
||||
--org-chart-connector-gap: var(--space-sm);
|
||||
--org-chart-children-offset: calc(var(--space-lg) + var(--space-sm));
|
||||
--org-chart-root-gap: var(--space-xl);
|
||||
--org-chart-first-child-leaves: 1;
|
||||
--org-chart-last-child-leaves: 1;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
@@ -968,6 +970,20 @@
|
||||
}
|
||||
|
||||
.org-chart-children {
|
||||
--org-chart-first-child-leaves-number: var(--org-chart-first-child-leaves, 1);
|
||||
--org-chart-last-child-leaves-number: var(--org-chart-last-child-leaves, 1);
|
||||
--org-chart-first-child-center-offset: calc(
|
||||
(
|
||||
var(--org-chart-first-child-leaves-number) * var(--org-chart-node-width) +
|
||||
(var(--org-chart-first-child-leaves-number) - 1) * var(--org-chart-sibling-gap)
|
||||
) / 2
|
||||
);
|
||||
--org-chart-last-child-center-offset: calc(
|
||||
(
|
||||
var(--org-chart-last-child-leaves-number) * var(--org-chart-node-width) +
|
||||
(var(--org-chart-last-child-leaves-number) - 1) * var(--org-chart-sibling-gap)
|
||||
) / 2
|
||||
);
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
@@ -982,8 +998,8 @@
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: calc(var(--org-chart-node-width) / 2);
|
||||
right: calc(var(--org-chart-node-width) / 2);
|
||||
left: var(--org-chart-first-child-center-offset);
|
||||
right: var(--org-chart-last-child-center-offset);
|
||||
height: 1px;
|
||||
background: var(--border);
|
||||
}
|
||||
|
||||
@@ -125,6 +125,12 @@ function OrgChartNode({
|
||||
const stateNodeClass = getStateCardClass("org-chart-node-card", agent.state);
|
||||
const subtreeLeafCount = getOrgChartLeafCount(node);
|
||||
const nodeStyle = { "--org-chart-subtree-leaves": String(subtreeLeafCount) } as CSSProperties;
|
||||
const firstChildLeafCount = children.length > 0 ? getOrgChartLeafCount(children[0]) : 1;
|
||||
const lastChildLeafCount = children.length > 0 ? getOrgChartLeafCount(children[children.length - 1]) : 1;
|
||||
const childrenStyle = {
|
||||
"--org-chart-first-child-leaves": String(firstChildLeafCount),
|
||||
"--org-chart-last-child-leaves": String(lastChildLeafCount),
|
||||
} as CSSProperties;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -162,7 +168,7 @@ function OrgChartNode({
|
||||
</div>
|
||||
</div>
|
||||
{children.length > 0 && (
|
||||
<div className="org-chart-children" role="group" aria-label={`${agent.name} employees`}>
|
||||
<div className="org-chart-children" style={childrenStyle} role="group" aria-label={`${agent.name} employees`}>
|
||||
{children.map((child) => (
|
||||
<OrgChartNode
|
||||
key={child.agent.id}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { render, screen, fireEvent, waitFor, within } from "@testing-library/react";
|
||||
import { loadAllAppCss } from "../../test/cssFixture";
|
||||
import { AgentsView } from "../AgentsView";
|
||||
import * as apiModule from "../../api";
|
||||
import type { Agent, AgentState, AgentCapability, OrgTreeNode } from "../../api";
|
||||
@@ -1335,9 +1336,20 @@ describe("AgentsView", () => {
|
||||
expect(leafNode.style.getPropertyValue("--org-chart-subtree-leaves")).toBe("1");
|
||||
expect(rootChildren).toBeTruthy();
|
||||
expect(rootChildren.className).toContain("org-chart-children");
|
||||
expect(rootChildren.style.getPropertyValue("--org-chart-first-child-leaves")).toBe("1");
|
||||
expect(rootChildren.style.getPropertyValue("--org-chart-last-child-leaves")).toBe("1");
|
||||
expect(container.querySelectorAll(".org-chart-node--has-children").length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("uses tokenized connector edge offsets for org chart child bars", () => {
|
||||
const css = loadAllAppCss();
|
||||
expect(css).toContain("--org-chart-first-child-center-offset");
|
||||
expect(css).toContain("--org-chart-last-child-center-offset");
|
||||
expect(css).toContain("left: var(--org-chart-first-child-center-offset)");
|
||||
expect(css).toContain("right: var(--org-chart-last-child-center-offset)");
|
||||
expect(css).toContain(".org-chart-children > .org-chart-node::before");
|
||||
});
|
||||
|
||||
it("switches org chart to vertical layout mode when estimated width exceeds viewport", async () => {
|
||||
const clientWidthSpy = vi.spyOn(window.HTMLElement.prototype, "clientWidth", "get").mockReturnValue(320);
|
||||
mockFetchOrgTree.mockResolvedValue(orgTree);
|
||||
|
||||
@@ -300,6 +300,13 @@ describe("agents-view mobile CSS", () => {
|
||||
expect(extractRuleBlock(mobileMediaBlock, ".agent-org-chart")).toContain("--org-chart-node-width: calc(var(--space-2xl) * 5)");
|
||||
expect(extractRuleBlock(mobileMediaBlock, ".agent-org-chart")).toContain("--org-chart-sibling-gap: var(--space-sm)");
|
||||
expect(extractRuleBlock(mobileMediaBlock, ".agent-org-chart")).toContain("--org-chart-children-offset: var(--space-lg)");
|
||||
const childrenConnectorBlock = extractRuleBlock(cssContent, ".org-chart-children::before");
|
||||
expect(childrenConnectorBlock).toContain("left: var(--org-chart-first-child-center-offset)");
|
||||
expect(childrenConnectorBlock).toContain("right: var(--org-chart-last-child-center-offset)");
|
||||
expect(childrenConnectorBlock).not.toContain("rgba(");
|
||||
const verticalConnectorBlock = extractRuleBlock(cssContent, ".agent-org-chart--vertical .org-chart-children::before");
|
||||
expect(verticalConnectorBlock).toContain("left: var(--space-sm)");
|
||||
expect(verticalConnectorBlock).not.toContain("rgba(");
|
||||
expect(extractRuleBlock(mobileMediaBlock, ".org-chart-node-card")).toContain("padding: var(--space-sm)");
|
||||
expect(extractRuleBlock(mobileMediaBlock, ".org-chart-node__badge")).toContain("font-size: calc(var(--space-sm) + var(--space-xs) * 0.625)");
|
||||
expect(extractRuleBlock(mobileMediaBlock, ".agent-org-chart-shell")).toContain("overflow: hidden");
|
||||
|
||||
Reference in New Issue
Block a user