diff --git a/packages/core/__tests__/view/mixins/CellMixin.test.ts b/packages/core/__tests__/view/mixins/CellMixin.test.ts index f7528f0ee3..24b20a0771 100644 --- a/packages/core/__tests__/view/mixins/CellMixin.test.ts +++ b/packages/core/__tests__/view/mixins/CellMixin.test.ts @@ -256,3 +256,48 @@ describe('isCellRotatable', () => { ).toBeFalsy(); }); }); + +describe('isValidAncestor', () => { + function configureParentChild(parent: Cell, child: Cell) { + child.setParent(parent); + parent.children.push(child); + } + + test('Parent is the direct parent of the Cell, recurse: false', () => { + const parent = new Cell(); + const cell = new Cell(); + cell.setParent(parent); + expect(createGraphWithoutPlugins().isValidAncestor(cell, parent)).toBeTruthy(); + }); + + test('Cell is direct child of parent but does not declare it as parent, and recurse: false', () => { + const cell = new Cell(); + const parent = new Cell(); + parent.children.push(cell); + expect(createGraphWithoutPlugins().isValidAncestor(cell, parent)).toBeFalsy(); + }); + + test('Cell is direct child of parent, recurse: true', () => { + const cell = new Cell(); + const intermediateParent = new Cell(); + configureParentChild(intermediateParent, cell); + const parent = new Cell(); + configureParentChild(parent, intermediateParent); + expect(createGraphWithoutPlugins().isValidAncestor(cell, parent, true)).toBeTruthy(); + }); + + test.each([true, false])( + 'Cell does not match parent, recurse: %s', + (recurse: boolean) => { + expect( + createGraphWithoutPlugins().isValidAncestor(new Cell(), new Cell(), recurse) + ).toBeFalsy(); + } + ); + + test.each([true, false])('null Cell, recurse: %s', (recurse: boolean) => { + expect( + createGraphWithoutPlugins().isValidAncestor(null, new Cell(), recurse) + ).toBeFalsy(); + }); +}); diff --git a/packages/core/src/view/mixins/CellsMixin.ts b/packages/core/src/view/mixins/CellsMixin.ts index 3cfd9f8f1a..6c497b79e8 100644 --- a/packages/core/src/view/mixins/CellsMixin.ts +++ b/packages/core/src/view/mixins/CellsMixin.ts @@ -1750,7 +1750,7 @@ export const CellsMixin: PartialType = { }, isValidAncestor(cell, parent, recurse = false) { - return recurse ? parent.isAncestor(cell) : cell.getParent() === parent; + return recurse ? parent.isAncestor(cell) : cell?.getParent() === parent; }, /***************************************************************************** diff --git a/packages/core/src/view/mixins/CellsMixin.type.ts b/packages/core/src/view/mixins/CellsMixin.type.ts index 2b579cf5b5..503d1d70c5 100644 --- a/packages/core/src/view/mixins/CellsMixin.type.ts +++ b/packages/core/src/view/mixins/CellsMixin.type.ts @@ -720,7 +720,7 @@ declare module '../Graph' { * @param parent {@link Cell} the possible parent cell * @param recurse boolean whether to recurse the child ancestors. Default is `false`. */ - isValidAncestor: (cell: Cell, parent: Cell, recurse: boolean) => boolean; + isValidAncestor: (cell: Cell | null, parent: Cell, recurse?: boolean) => boolean; /** * Returns `true` if the given cell may not be moved, sized, bended, disconnected, edited or selected. diff --git a/packages/core/src/view/mixins/EdgeMixin.ts b/packages/core/src/view/mixins/EdgeMixin.ts index 5ca289a4c5..d6fa6bd7c4 100644 --- a/packages/core/src/view/mixins/EdgeMixin.ts +++ b/packages/core/src/view/mixins/EdgeMixin.ts @@ -341,10 +341,10 @@ export const EdgeMixin: PartialType = { (source !== target && ((incoming && target === cell && - (!parent || this.isValidAncestor(source, parent, recurse))) || + (!parent || this.isValidAncestor(source, parent, recurse))) || (outgoing && source === cell && - (!parent || this.isValidAncestor(target, parent, recurse))))) + (!parent || this.isValidAncestor(target, parent, recurse))))) ) { result.push(edges[i]); }