feat(org-chart): implement expandable layout and multi-company navigation
Integrate P5-1B through P5-4 shared Core layout, collapse, zoom/pan/fit, company roots and keyboard accessibility. Include React/HTML examples, virtual 20/100/300 employee fixtures, focused checks and the additive data contract.
This commit is contained in:
1 parent
a92913d6bc
commit
064517ec46
30 files changed
+7532
-183
No files matched your search
@@ -1,29 +1,31 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { expandedPreviewRoots } from './expanded-layout.js';
|
||||
import { createOrgChart } from './index.js';
|
||||
import { engineTree } from './render.js';
|
||||
import type { RenderContext } from './render.js';
|
||||
import type { OrganizationTreeNode } from '../data/index.js';
|
||||
|
||||
const node = (id: string, parentId: string | null, type: 'root' | 'office' = 'office', children: OrganizationTreeNode[] = []): OrganizationTreeNode => ({
|
||||
organization: { id, parentId, name: id, type, leaderId: null, displayOrder: 0 }, children,
|
||||
const node = (id: string, parentId: string | null, children: OrganizationTreeNode[] = []): OrganizationTreeNode => ({
|
||||
organization: { id, parentId, name: id, type: parentId ? 'office' : 'root', leaderId: null, displayOrder: 0 }, children,
|
||||
});
|
||||
|
||||
test('initial preview keeps actual first-depth ancestry and defers descendants without mutating hierarchy', () => {
|
||||
const grandchild = node('depth-2', 'depth-1');
|
||||
const child = node('depth-1', 'root', 'office', [grandchild]);
|
||||
const roots = [node('root', null, 'root', [child])];
|
||||
test('Auto engine projection preserves root, nested ancestry, sibling order and depths without mutation', () => {
|
||||
const roots = [node('root', null, [node('first', 'root', [node('cell', 'first', [node('team', 'cell')])]), node('second', 'root')])];
|
||||
const before = structuredClone(roots);
|
||||
const projected = expandedPreviewRoots(roots);
|
||||
assert.deepEqual(projected.map(item => item.organization.id), ['depth-1']);
|
||||
assert.equal(projected[0]!.organization.parentId, 'root');
|
||||
assert.deepEqual(projected[0]!.children, []);
|
||||
const context = { orderedOrganizations: [], readOnly: true } as unknown as RenderContext;
|
||||
const result = engineTree(roots, 'test', context);
|
||||
assert.equal(result[0]?.depth, 0);
|
||||
assert.equal(result[0]?.children?.[0]?.children?.[0]?.children?.[0]?.depth, 3);
|
||||
assert.deepEqual(context.orderedOrganizations.map(item => item.id), ['root', 'first', 'cell', 'team', 'second']);
|
||||
assert.deepEqual(result[0]?.children?.map(item => item.organizationIndex), [1, 4]);
|
||||
assert.deepEqual(roots, before);
|
||||
});
|
||||
|
||||
test('independent roots and normalized order stay independent in the initial preview', () => {
|
||||
const roots = [node('first', null), node('root', null, 'root', [node('second', 'root'), node('third', 'root')])];
|
||||
assert.deepEqual(expandedPreviewRoots(roots).map(item => item.organization.id), ['first', 'second', 'third']);
|
||||
assert.deepEqual(expandedPreviewRoots([]), []);
|
||||
test('independent roots remain independent and empty hierarchy stays empty', () => {
|
||||
const context = { orderedOrganizations: [], readOnly: true } as unknown as RenderContext;
|
||||
const roots = [node('first', null), node('second', null)];
|
||||
assert.equal(engineTree(roots, 'test', context).length, 2);
|
||||
assert.deepEqual(engineTree([], 'empty', context), []);
|
||||
});
|
||||
|
||||
const options = { dataSource: { load: async () => ({}) }, stylesheetUrls: { engine: '/engine.css', core: '/style.css' } };
|
||||
|
||||
Reference in new issue
Block a user