mirror of
https://gitee.com/antv/g6.git
synced 2024-11-30 02:38:20 +08:00
feat: add graph unit test
This commit is contained in:
parent
e3eef3a8bd
commit
362f2e4086
@ -45,7 +45,7 @@
|
||||
"lint:src": "eslint --ext .ts --format=pretty \"./src\"",
|
||||
"prettier": "prettier -c --write \"**/*\"",
|
||||
"test": "jest",
|
||||
"test-live": "DEBUG_MODE=1 jest --watch ./tests/unit/graph/graph-watermarker-spec.ts",
|
||||
"test-live": "DEBUG_MODE=1 jest --watch ./tests/unit/graph/controller/item-spec.ts",
|
||||
"lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx"
|
||||
},
|
||||
"husky": {
|
||||
@ -75,6 +75,8 @@
|
||||
"ml-matrix": "^6.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antv/g-canvas": "^0.4.15",
|
||||
"@antv/g-svg": "^0.5.2",
|
||||
"@babel/core": "^7.7.7",
|
||||
"@babel/plugin-proposal-class-properties": "^7.1.0",
|
||||
"@babel/preset-react": "^7.7.4",
|
||||
|
@ -2,54 +2,9 @@ const subjectColor = 'rgb(95, 149, 255)';
|
||||
const backColor = 'rgb(255, 255, 255)';
|
||||
const textColor = 'rgb(0, 0, 0)';
|
||||
|
||||
// const colorSet = {
|
||||
|
||||
// // for nodes
|
||||
// mainStroke: subjectColor,
|
||||
// mainFill: subjectColor01,
|
||||
|
||||
// activeStroke: subjectColor,
|
||||
// activeFill: subjectColor005,
|
||||
|
||||
// inactiveStroke: subjectColor04,
|
||||
// inactiveFill: subjectColor005,
|
||||
|
||||
// selectedStroke: subjectColor,
|
||||
// selectedFill: backColor,
|
||||
|
||||
// highlightStroke: deeperSubject,
|
||||
// highlightFill: subjectColor02,
|
||||
|
||||
// disableStroke: disableColor03,
|
||||
// disableFill: disableColor005,
|
||||
|
||||
// // for edges
|
||||
// edgeMainStroke: disableColor03,
|
||||
// edgeActiveStroke: subjectColor,
|
||||
// edgeInactiveStroke: disableColor02,
|
||||
// edgeSelectedStroke: subjectColor,
|
||||
// edgeHighlightStroke: subjectColor,
|
||||
// edgeDisableStroke: disableColor01,
|
||||
|
||||
// // for combos
|
||||
// comboMainStroke: disableColor03,
|
||||
// comboMainFill: disableColor002,
|
||||
|
||||
// comboActiveStroke: subjectColor,
|
||||
// comboActiveFill: subjectColor005,
|
||||
|
||||
// comboInactiveStroke: disableColor03,
|
||||
// comboInactiveFill: disableColor002,
|
||||
|
||||
// comboSelectedStroke: subjectColor,
|
||||
// comboSelectedFill: disableColor002,
|
||||
|
||||
// comboHighlightStroke: deeperSubject, // 'rgb(53, 119, 222)', // TODO: how to generate it ???
|
||||
// comboHighlightFill: disableColor002,
|
||||
|
||||
// comboDisableStroke: disableColor02,
|
||||
// comboDisableFill: disableColor005,
|
||||
// }
|
||||
const nodeMainFill = 'rgb(239, 244, 255)';
|
||||
const edgeMainStroke = 'rgb(224, 224, 224)';
|
||||
const edgeSelectedStroke = 'rgb(95, 149, 255)';
|
||||
|
||||
const colorSet = {
|
||||
// for nodes
|
||||
@ -121,7 +76,7 @@ export default {
|
||||
style: {
|
||||
lineWidth: 1,
|
||||
stroke: colorSet.mainStroke,
|
||||
fill: colorSet.mainFill,
|
||||
fill: nodeMainFill,
|
||||
},
|
||||
size: 20,
|
||||
color: colorSet.mainStroke,
|
||||
@ -182,10 +137,10 @@ export default {
|
||||
type: 'line',
|
||||
size: 1,
|
||||
style: {
|
||||
stroke: colorSet.edgeMainStroke,
|
||||
stroke: edgeMainStroke,
|
||||
lineAppendWidth: 2,
|
||||
},
|
||||
color: colorSet.edgeMainStroke,
|
||||
color: edgeMainStroke,
|
||||
},
|
||||
// 边应用状态后的样式,默认仅提供 active、selected、highlight、inactive、disable,用户可以自己扩展
|
||||
edgeStateStyles: {
|
||||
@ -194,9 +149,9 @@ export default {
|
||||
lineWidth: 1,
|
||||
},
|
||||
selected: {
|
||||
stroke: colorSet.edgeSelectedStroke,
|
||||
stroke: edgeSelectedStroke,
|
||||
lineWidth: 2,
|
||||
shadowColor: colorSet.edgeSelectedStroke,
|
||||
shadowColor: edgeSelectedStroke,
|
||||
shadowBlur: 10,
|
||||
'text-shape': {
|
||||
fontWeight: 500,
|
||||
|
@ -45,13 +45,12 @@ export default class ModeController {
|
||||
|
||||
this.mode = graph.get('defaultMode') || 'default';
|
||||
this.currentBehaves = [];
|
||||
debugger;
|
||||
this.setMode(this.mode);
|
||||
}
|
||||
|
||||
private formatModes() {
|
||||
const { modes } = this;
|
||||
each(modes, (mode) => {
|
||||
each(modes, mode => {
|
||||
each(mode, (behavior, i) => {
|
||||
if (isString(behavior)) {
|
||||
mode[i] = { type: behavior };
|
||||
@ -65,7 +64,7 @@ export default class ModeController {
|
||||
const behaviors = this.modes[mode];
|
||||
const behaves: IBehavior[] = [];
|
||||
let behave: IBehavior;
|
||||
each(behaviors || [], (behavior) => {
|
||||
each(behaviors || [], behavior => {
|
||||
const BehaviorInstance = Behavior.getBehavior(behavior.type);
|
||||
if (!BehaviorInstance) {
|
||||
return;
|
||||
@ -81,7 +80,7 @@ export default class ModeController {
|
||||
}
|
||||
|
||||
private static mergeBehaviors(modeBehaviors: ModeType[], behaviors: ModeType[]): ModeType[] {
|
||||
each(behaviors, (behavior) => {
|
||||
each(behaviors, behavior => {
|
||||
if (modeBehaviors.indexOf(behavior) < 0) {
|
||||
if (isString(behavior)) {
|
||||
behavior = { type: behavior };
|
||||
@ -94,7 +93,7 @@ export default class ModeController {
|
||||
|
||||
private static filterBehaviors(modeBehaviors: ModeType[], behaviors: ModeType[]): ModeType[] {
|
||||
const result: ModeType[] = [];
|
||||
modeBehaviors.forEach((behavior) => {
|
||||
modeBehaviors.forEach(behavior => {
|
||||
let type: string = '';
|
||||
if (isString(behavior)) {
|
||||
type = behavior;
|
||||
@ -120,7 +119,7 @@ export default class ModeController {
|
||||
}
|
||||
graph.emit('beforemodechange', { mode });
|
||||
|
||||
each(this.currentBehaves, (behave) => {
|
||||
each(this.currentBehaves, behave => {
|
||||
behave.unbind(graph);
|
||||
});
|
||||
|
||||
@ -156,7 +155,7 @@ export default class ModeController {
|
||||
}
|
||||
|
||||
if (isArray(modes)) {
|
||||
each(modes, (mode) => {
|
||||
each(modes, mode => {
|
||||
if (!this.modes[mode]) {
|
||||
if (isAdd) {
|
||||
this.modes[mode] = behaves;
|
||||
|
@ -538,7 +538,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
* @return {object} 元素实例
|
||||
*/
|
||||
public findAllByState<T extends Item>(type: ITEM_TYPE, state: string): T[] {
|
||||
return this.findAll(type, (item) => item.hasState(state));
|
||||
return this.findAll(type, item => item.hasState(state));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -897,15 +897,15 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
}
|
||||
|
||||
if (type === 'node') {
|
||||
const model = (item as INode).getModel();
|
||||
const model = (nodeItem as INode).getModel();
|
||||
// 如果删除的是节点,且该节点存在于某个 Combo 中,则需要先将 node 从 combo 中移除,否则删除节点后,操作 combo 会出错
|
||||
if (model.comboId) {
|
||||
this.updateComboTree(item as INode);
|
||||
this.updateComboTree(nodeItem as INode);
|
||||
}
|
||||
}
|
||||
|
||||
const itemController: ItemController = this.get('itemController');
|
||||
itemController.removeItem(item);
|
||||
itemController.removeItem(nodeItem);
|
||||
if (type === 'combo') {
|
||||
const newComboTrees = reconstructTree(this.get('comboTrees'));
|
||||
this.set('comboTrees', newComboTrees);
|
||||
@ -948,7 +948,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
let foundParent = false;
|
||||
comboTrees.forEach((ctree: ComboTree) => {
|
||||
if (foundParent) return; // terminate the forEach after the tree containing the item is done
|
||||
traverseTreeUp<ComboTree>(ctree, (child) => {
|
||||
traverseTreeUp<ComboTree>(ctree, child => {
|
||||
// find the parent
|
||||
if (model.parentId === child.id) {
|
||||
foundParent = true;
|
||||
@ -997,7 +997,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
foundNode = false;
|
||||
(comboTrees || []).forEach((ctree: ComboTree) => {
|
||||
if (foundNode || foundParent) return; // terminate the forEach
|
||||
traverseTreeUp<ComboTree>(ctree, (child) => {
|
||||
traverseTreeUp<ComboTree>(ctree, child => {
|
||||
if (child.id === model.id) {
|
||||
// if the item exists in the tree already, terminate
|
||||
foundNode = true;
|
||||
@ -1105,12 +1105,12 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
if (currentItem.getType) type = currentItem.getType();
|
||||
const states = [...currentItem.getStates()];
|
||||
if (type === 'combo') {
|
||||
each(states, (state) => this.setItemState(currentItem, state, false));
|
||||
each(states, state => this.setItemState(currentItem, state, false));
|
||||
}
|
||||
itemController.updateItem(currentItem, cfg);
|
||||
|
||||
if (type === 'combo') {
|
||||
each(states, (state) => this.setItemState(currentItem, state, true));
|
||||
each(states, state => this.setItemState(currentItem, state, true));
|
||||
}
|
||||
|
||||
if (stack && this.get('enabledStack')) {
|
||||
@ -1244,8 +1244,10 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
|
||||
// layout
|
||||
const layoutController = self.get('layoutController');
|
||||
if (!layoutController.layout(success)) {
|
||||
success();
|
||||
if (layoutController) {
|
||||
if (!layoutController.layout(success)) {
|
||||
success();
|
||||
}
|
||||
}
|
||||
function success() {
|
||||
if (self.get('fitView')) {
|
||||
@ -1270,14 +1272,14 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
const nodesArr = this.getNodes();
|
||||
|
||||
// 遍历节点实例,将所有节点提前。
|
||||
nodesArr.forEach((node) => {
|
||||
nodesArr.forEach(node => {
|
||||
node.toFront();
|
||||
});
|
||||
} else {
|
||||
const edgesArr = this.getEdges();
|
||||
|
||||
// 遍历节点实例,将所有节点提前。
|
||||
edgesArr.forEach((edge) => {
|
||||
edgesArr.forEach(edge => {
|
||||
edge.toBack();
|
||||
});
|
||||
}
|
||||
@ -1308,7 +1310,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
let item: INode;
|
||||
const itemMap: NodeMap = this.get('itemMap');
|
||||
|
||||
each(models, (model) => {
|
||||
each(models, model => {
|
||||
item = itemMap[model.id];
|
||||
if (item) {
|
||||
if (self.get('animate') && type === NODE) {
|
||||
@ -1348,8 +1350,8 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
this.set('comboSorted', false);
|
||||
|
||||
// 更改数据源后,取消所有状态
|
||||
this.getNodes().map((node) => self.clearItemStates(node));
|
||||
this.getEdges().map((edge) => self.clearItemStates(edge));
|
||||
this.getNodes().map(node => self.clearItemStates(node));
|
||||
this.getEdges().map(edge => self.clearItemStates(edge));
|
||||
|
||||
const canvas: ICanvas = this.get('canvas');
|
||||
const localRefresh: boolean = canvas.get('localRefresh');
|
||||
@ -1420,13 +1422,15 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
this.set({ nodes: items.nodes, edges: items.edges });
|
||||
|
||||
const layoutController = this.get('layoutController');
|
||||
layoutController.changeData();
|
||||
if (layoutController) {
|
||||
layoutController.changeData();
|
||||
|
||||
if (self.get('animate') && !layoutController.getLayoutType()) {
|
||||
// 如果没有指定布局
|
||||
self.positionsAnimate();
|
||||
} else {
|
||||
self.autoPaint();
|
||||
if (self.get('animate') && !layoutController.getLayoutType()) {
|
||||
// 如果没有指定布局
|
||||
self.positionsAnimate();
|
||||
} else {
|
||||
self.autoPaint();
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
@ -1471,7 +1475,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
comboConfig = combo;
|
||||
}
|
||||
|
||||
const trees: ComboTree[] = children.map((elementId) => {
|
||||
const trees: ComboTree[] = children.map(elementId => {
|
||||
const item = this.findById(elementId);
|
||||
|
||||
let type = '';
|
||||
@ -1498,8 +1502,8 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
|
||||
// step3: 更新 comboTrees 结构
|
||||
const comboTrees = this.get('comboTrees');
|
||||
(comboTrees || []).forEach((ctree) => {
|
||||
traverseTreeUp<ComboTree>(ctree, (child) => {
|
||||
(comboTrees || []).forEach(ctree => {
|
||||
traverseTreeUp<ComboTree>(ctree, child => {
|
||||
if (child.id === comboId) {
|
||||
child.itemType = 'combo';
|
||||
child.children = trees as ComboTree[];
|
||||
@ -1539,15 +1543,15 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
const comboItems = this.get('combos');
|
||||
const parentItem = this.findById(parentId as string) as ICombo;
|
||||
|
||||
comboTrees.forEach((ctree) => {
|
||||
comboTrees.forEach(ctree => {
|
||||
if (treeToBeUncombo) return; // terminate the forEach
|
||||
traverseTreeUp<ComboTree>(ctree, (subtree) => {
|
||||
traverseTreeUp<ComboTree>(ctree, subtree => {
|
||||
// find the combo to be uncomboed, delete the combo from map and cache
|
||||
if (subtree.id === comboId) {
|
||||
treeToBeUncombo = subtree;
|
||||
// delete the related edges
|
||||
const edges = comboItem.getEdges();
|
||||
edges.forEach((edge) => {
|
||||
edges.forEach(edge => {
|
||||
this.removeItem(edge, false);
|
||||
});
|
||||
const index = comboItems.indexOf(combo);
|
||||
@ -1566,7 +1570,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
}
|
||||
|
||||
// append the combo's children to the combo's brothers array
|
||||
treeToBeUncombo.children.forEach((child) => {
|
||||
treeToBeUncombo.children.forEach(child => {
|
||||
const item = this.findById(child.id) as ICombo | INode;
|
||||
const childModel = item.getModel();
|
||||
if (item.getType && item.getType() === 'combo') {
|
||||
@ -1592,7 +1596,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
const index = comboTrees.indexOf(treeToBeUncombo);
|
||||
comboTrees.splice(index, 1);
|
||||
// modify the parentId of the children
|
||||
treeToBeUncombo.children.forEach((child) => {
|
||||
treeToBeUncombo.children.forEach(child => {
|
||||
child.parentId = undefined;
|
||||
const childModel = this.findById(child.id).getModel();
|
||||
childModel.parentId = undefined; // update the parentId of the model
|
||||
@ -1611,7 +1615,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
|
||||
const itemMap = self.get('itemMap');
|
||||
(comboTrees || []).forEach((ctree: ComboTree) => {
|
||||
traverseTreeUp<ComboTree>(ctree, (child) => {
|
||||
traverseTreeUp<ComboTree>(ctree, child => {
|
||||
if (!child) {
|
||||
return true;
|
||||
}
|
||||
@ -1619,13 +1623,13 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
if (childItem && childItem.getType && childItem.getType() === 'combo') {
|
||||
// 更新具体的 Combo 之前先清除所有的已有状态,以免将 state 中的样式更新为 Combo 的样式
|
||||
const states = [...childItem.getStates()];
|
||||
each(states, (state) => this.setItemState(childItem, state, false));
|
||||
each(states, state => this.setItemState(childItem, state, false));
|
||||
|
||||
// 更新具体的 Combo
|
||||
itemController.updateCombo(childItem, child.children);
|
||||
|
||||
// 更新 Combo 后,还原已有的状态
|
||||
each(states, (state) => this.setItemState(childItem, state, true));
|
||||
each(states, state => this.setItemState(childItem, state, true));
|
||||
}
|
||||
return true;
|
||||
});
|
||||
@ -1656,7 +1660,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
|
||||
const itemMap = self.get('itemMap');
|
||||
(comboTrees || []).forEach((ctree: ComboTree) => {
|
||||
traverseTreeUp<ComboTree>(ctree, (child) => {
|
||||
traverseTreeUp<ComboTree>(ctree, child => {
|
||||
if (!child) {
|
||||
return true;
|
||||
}
|
||||
@ -1670,7 +1674,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
// 更新具体的 Combo 之前先清除所有的已有状态,以免将 state 中的样式更新为 Combo 的样式
|
||||
const states = [...childItem.getStates()];
|
||||
// || !item.getStateStyle(stateName)
|
||||
each(states, (state) => {
|
||||
each(states, state => {
|
||||
if (childItem.getStateStyle(state)) {
|
||||
this.setItemState(childItem, state, false);
|
||||
}
|
||||
@ -1680,7 +1684,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
itemController.updateCombo(childItem, child.children);
|
||||
|
||||
// 更新 Combo 后,还原已有的状态
|
||||
each(states, (state) => {
|
||||
each(states, state => {
|
||||
if (childItem.getStateStyle(state)) {
|
||||
this.setItemState(childItem, state, true);
|
||||
}
|
||||
@ -1723,9 +1727,9 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
const comboTrees = this.get('comboTrees');
|
||||
let valid = true;
|
||||
let itemSubTree;
|
||||
(comboTrees || []).forEach((ctree) => {
|
||||
(comboTrees || []).forEach(ctree => {
|
||||
if (itemSubTree) return;
|
||||
traverseTree(ctree, (subTree) => {
|
||||
traverseTree(ctree, subTree => {
|
||||
if (itemSubTree) return;
|
||||
// 找到从 item 开始的子树
|
||||
if (subTree.id === uItem.getID()) {
|
||||
@ -1735,7 +1739,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
});
|
||||
});
|
||||
// 在以 item 为根的子树中寻找与 parentId 相同的后继元素
|
||||
traverseTree(itemSubTree, (subTree) => {
|
||||
traverseTree(itemSubTree, subTree => {
|
||||
if (subTree.id === parentId) {
|
||||
valid = false;
|
||||
return false;
|
||||
@ -1943,7 +1947,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
|
||||
const nodes = self.getNodes();
|
||||
|
||||
const toNodes = nodes.map((node) => {
|
||||
const toNodes = nodes.map(node => {
|
||||
const model = node.getModel();
|
||||
return {
|
||||
id: model.id,
|
||||
@ -1960,7 +1964,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
|
||||
canvas.animate(
|
||||
(ratio: number) => {
|
||||
each(toNodes, (data) => {
|
||||
each(toNodes, data => {
|
||||
const node: Item = self.findById(data.id);
|
||||
|
||||
if (!node || node.destroyed) {
|
||||
@ -2161,7 +2165,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
public layout(): void {
|
||||
const layoutController = this.get('layoutController');
|
||||
const layoutCfg = this.get('layout');
|
||||
if (!layoutCfg) return;
|
||||
if (!layoutCfg || !layoutController) return;
|
||||
|
||||
if (layoutCfg.workerEnabled) {
|
||||
// 如果使用web worker布局
|
||||
@ -2203,18 +2207,18 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
const comboTrees = this.get('comboTrees');
|
||||
let found = false;
|
||||
let brothers = {};
|
||||
(comboTrees || []).forEach((ctree) => {
|
||||
(comboTrees || []).forEach(ctree => {
|
||||
brothers[ctree.id] = ctree;
|
||||
});
|
||||
(comboTrees || []).forEach((ctree) => {
|
||||
(comboTrees || []).forEach(ctree => {
|
||||
if (found) return; // if the combo is found, terminate the forEach
|
||||
traverseTree(ctree, (subTree) => {
|
||||
traverseTree(ctree, subTree => {
|
||||
// if the combo is found and the it is traversing the other brothers, terminate
|
||||
if (found && brothers[subTree.id]) return false;
|
||||
if (comboModel.parentId === subTree.id) {
|
||||
// if the parent is found, store the brothers
|
||||
brothers = {};
|
||||
subTree.children.forEach((child) => {
|
||||
subTree.children.forEach(child => {
|
||||
brothers[child.id] = child;
|
||||
});
|
||||
} else if (comboModel.id === subTree.id) {
|
||||
@ -2235,7 +2239,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
|
||||
const edgeWeightMap = {};
|
||||
const addedVEdges = [];
|
||||
edges.forEach((edge) => {
|
||||
edges.forEach(edge => {
|
||||
if (edge.isVisible() && !edge.getModel().isVEdge) return;
|
||||
let source = edge.getSource();
|
||||
let target = edge.getTarget();
|
||||
@ -2319,7 +2323,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
|
||||
// update the width of the virtual edges, which is the sum of merged actual edges
|
||||
// be attention that the actual edges with same endpoints but different directions will be represented by two different virtual edges
|
||||
addedVEdges.forEach((vedge) => {
|
||||
addedVEdges.forEach(vedge => {
|
||||
const vedgeModel = vedge.getModel();
|
||||
this.updateItem(
|
||||
vedge,
|
||||
@ -2358,18 +2362,18 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
const comboTrees = this.get('comboTrees');
|
||||
let found = false;
|
||||
let brothers = {};
|
||||
(comboTrees || []).forEach((ctree) => {
|
||||
(comboTrees || []).forEach(ctree => {
|
||||
brothers[ctree.id] = ctree;
|
||||
});
|
||||
(comboTrees || []).forEach((ctree) => {
|
||||
(comboTrees || []).forEach(ctree => {
|
||||
if (found) return; // if the combo is found, terminate
|
||||
traverseTree(ctree, (subTree) => {
|
||||
traverseTree(ctree, subTree => {
|
||||
if (found && brothers[subTree.id]) {
|
||||
return false;
|
||||
}
|
||||
if (comboModel.parentId === subTree.id) {
|
||||
brothers = {};
|
||||
subTree.children.forEach((child) => {
|
||||
subTree.children.forEach(child => {
|
||||
brothers[child.id] = child;
|
||||
});
|
||||
} else if (comboModel.id === subTree.id) {
|
||||
@ -2388,7 +2392,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
|
||||
const edgeWeightMap = {};
|
||||
const addedVEdges = {};
|
||||
edges.forEach((edge) => {
|
||||
edges.forEach(edge => {
|
||||
if (edge.isVisible() && !edge.getModel().isVEdge) return;
|
||||
let source = edge.getSource();
|
||||
let target = edge.getTarget();
|
||||
@ -2587,8 +2591,8 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
const depthMap = [];
|
||||
const dataDepthMap = {};
|
||||
const comboTrees = this.get('comboTrees');
|
||||
(comboTrees || []).forEach((cTree) => {
|
||||
traverseTree(cTree, (child) => {
|
||||
(comboTrees || []).forEach(cTree => {
|
||||
traverseTree(cTree, child => {
|
||||
if (depthMap[child.depth]) depthMap[child.depth].push(child.id);
|
||||
else depthMap[child.depth] = [child.id];
|
||||
dataDepthMap[child.id] = child.depth;
|
||||
@ -2596,7 +2600,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
});
|
||||
});
|
||||
const edges = this.getEdges().concat(this.get('vedges'));
|
||||
(edges || []).forEach((edgeItem) => {
|
||||
(edges || []).forEach(edgeItem => {
|
||||
const edge = edgeItem.getModel();
|
||||
const sourceDepth: number = dataDepthMap[edge.source as string] || 0;
|
||||
const targetDepth: number = dataDepthMap[edge.target as string] || 0;
|
||||
@ -2604,7 +2608,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
|
||||
if (depthMap[depth]) depthMap[depth].push(edge.id);
|
||||
else depthMap[depth] = [edge.id];
|
||||
});
|
||||
depthMap.forEach((array) => {
|
||||
depthMap.forEach(array => {
|
||||
if (!array || !array.length) return;
|
||||
for (let i = array.length - 1; i >= 0; i--) {
|
||||
const item = this.findById(array[i]);
|
||||
|
@ -42,8 +42,8 @@ export default class Hull {
|
||||
this.graph = graph;
|
||||
this.id = this.cfg.id;
|
||||
this.group = this.cfg.group;
|
||||
this.members = this.cfg.members.map((item) => (isString(item) ? graph.findById(item) : item));
|
||||
this.nonMembers = this.cfg.nonMembers.map((item) =>
|
||||
this.members = this.cfg.members.map(item => (isString(item) ? graph.findById(item) : item));
|
||||
this.nonMembers = this.cfg.nonMembers.map(item =>
|
||||
isString(item) ? graph.findById(item) : item,
|
||||
);
|
||||
this.setPadding();
|
||||
@ -96,18 +96,12 @@ export default class Hull {
|
||||
switch (this.type) {
|
||||
case 'round-convex':
|
||||
contour = genConvexHull(members);
|
||||
hull = roundedHull(
|
||||
contour.map((p) => [p.x, p.y]),
|
||||
this.padding,
|
||||
);
|
||||
hull = roundedHull(contour.map(p => [p.x, p.y]), this.padding);
|
||||
path = parsePathString(hull);
|
||||
break;
|
||||
case 'smooth-convex':
|
||||
contour = genConvexHull(members);
|
||||
hull = paddedHull(
|
||||
contour.map((p) => [p.x, p.y]),
|
||||
this.padding,
|
||||
);
|
||||
hull = paddedHull(contour.map(p => [p.x, p.y]), this.padding);
|
||||
path = contour.length >= 2 && getClosedSpline(hull);
|
||||
break;
|
||||
case 'bubble':
|
||||
@ -201,11 +195,11 @@ export default class Hull {
|
||||
public updateData(members: Item[] | string[], nonMembers: string[] | Item[]) {
|
||||
this.group.findById(this.id).remove();
|
||||
if (members)
|
||||
this.members = (members as any[]).map((item) =>
|
||||
this.members = (members as any[]).map(item =>
|
||||
isString(item) ? this.graph.findById(item) : item,
|
||||
);
|
||||
if (nonMembers)
|
||||
this.nonMembers = (nonMembers as any[]).map((item) =>
|
||||
this.nonMembers = (nonMembers as any[]).map(item =>
|
||||
isString(item) ? this.graph.findById(item) : item,
|
||||
);
|
||||
this.path = this.calcPath(this.members, this.nonMembers);
|
||||
@ -219,20 +213,25 @@ export default class Hull {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新 hull
|
||||
* @param cfg hull 配置项
|
||||
*/
|
||||
public updateCfg(cfg: Partial<HullCfg>) {
|
||||
this.cfg = deepMix(this.cfg, cfg);
|
||||
this.id = this.cfg.id;
|
||||
this.group = this.cfg.group;
|
||||
if (cfg.members) {
|
||||
this.members = this.cfg.members.map((item) =>
|
||||
this.members = this.cfg.members.map(item =>
|
||||
isString(item) ? this.graph.findById(item) : item,
|
||||
);
|
||||
}
|
||||
if (cfg.nonMembers) {
|
||||
this.nonMembers = this.cfg.nonMembers.map((item) =>
|
||||
this.nonMembers = this.cfg.nonMembers.map(item =>
|
||||
isString(item) ? this.graph.findById(item) : item,
|
||||
);
|
||||
}
|
||||
// TODO padding 设置太大,会影响到 contain 结果
|
||||
this.setPadding();
|
||||
this.setType();
|
||||
this.path = this.calcPath(this.members, this.nonMembers);
|
||||
@ -263,7 +262,7 @@ export default class Hull {
|
||||
[shapeBBox.minX, shapeBBox.maxY],
|
||||
];
|
||||
}
|
||||
shapePoints = shapePoints.map((canvasPoint) => {
|
||||
shapePoints = shapePoints.map(canvasPoint => {
|
||||
const point = this.graph.getPointByCanvas(canvasPoint[0], canvasPoint[1]);
|
||||
return [point.x, point.y];
|
||||
});
|
||||
|
@ -1,351 +0,0 @@
|
||||
import Simulate from 'event-simulate';
|
||||
import G6 from '../../../../src';
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.id = 'event-spec';
|
||||
document.body.appendChild(div);
|
||||
|
||||
describe('event', () => {
|
||||
const graph = new G6.Graph({
|
||||
container: div,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
it('init event', () => {
|
||||
const canvas = graph.get('canvas');
|
||||
expect(graph.get('eventController')).not.toBe(undefined);
|
||||
|
||||
let a = 0;
|
||||
graph.on('canvas:click', (e) => {
|
||||
a = e.a;
|
||||
});
|
||||
|
||||
graph.emit('canvas:click', { a: 1 });
|
||||
canvas.emit('click', { a: 1, target: canvas, type: 'click' });
|
||||
expect(a).toBe(1);
|
||||
});
|
||||
it('g event on canvas', () => {
|
||||
let triggered = false;
|
||||
const canvas = graph.get('canvas');
|
||||
graph.on('canvas:click', () => {
|
||||
triggered = true;
|
||||
|
||||
expect(triggered).toBe(true);
|
||||
graph.off('canvas:click');
|
||||
});
|
||||
|
||||
const evt = { type: 'click', target: canvas };
|
||||
expect(triggered).toBe(false);
|
||||
|
||||
canvas.emit('click', evt);
|
||||
});
|
||||
|
||||
it('g event on shape', () => {
|
||||
let target = null;
|
||||
const canvas = graph.get('canvas');
|
||||
|
||||
const node = graph.addItem('node', {
|
||||
type: 'circle',
|
||||
color: '#ccc',
|
||||
style: { x: 50, y: 50, r: 20, lineWidth: 2 },
|
||||
});
|
||||
|
||||
const shape = node.get('group').get('children')[0];
|
||||
|
||||
graph.on('node:mousedown', (e) => {
|
||||
target = e.item;
|
||||
expect(target === node).toBe(true);
|
||||
});
|
||||
|
||||
canvas.emit('mousedown', { type: 'mousedown', target: shape });
|
||||
|
||||
target = null;
|
||||
graph.off('node:mousedown');
|
||||
|
||||
canvas.emit('mousedown', { type: 'mousedown', target: shape });
|
||||
|
||||
expect(target).toBe(null);
|
||||
});
|
||||
it('dom event', () => {
|
||||
let evt = null;
|
||||
|
||||
const fn = (e) => {
|
||||
evt = e;
|
||||
expect(evt).not.toBe(null);
|
||||
expect(evt.type).toEqual('keydown');
|
||||
};
|
||||
|
||||
graph.on('keydown', fn);
|
||||
|
||||
const canvas = graph.get('canvas').get('el');
|
||||
|
||||
const bbox = canvas.getBoundingClientRect();
|
||||
|
||||
Simulate.simulate(canvas, 'keydown', {
|
||||
clientY: bbox.right - 50,
|
||||
clientX: bbox.left + 10,
|
||||
});
|
||||
|
||||
graph.off('keydown', fn);
|
||||
|
||||
evt = null;
|
||||
|
||||
Simulate.simulate(canvas, 'keydown', {
|
||||
clientY: bbox.right - 50,
|
||||
clientX: bbox.left + 10,
|
||||
});
|
||||
|
||||
expect(evt).toBe(null);
|
||||
});
|
||||
it('mouseenter & mouseleave', () => {
|
||||
graph.clear();
|
||||
const node = graph.addItem('node', { x: 100, y: 100, size: 50, label: 'test' });
|
||||
|
||||
let enter = 0;
|
||||
let leave = 0;
|
||||
|
||||
graph.on('node:mouseenter', (e) => {
|
||||
enter++;
|
||||
expect(e.item === node);
|
||||
});
|
||||
|
||||
graph.on('mousemove', (e) => {
|
||||
enter++;
|
||||
});
|
||||
|
||||
graph.on('node:mouseleave', (e) => {
|
||||
leave++;
|
||||
expect(e.item === node);
|
||||
});
|
||||
|
||||
const canvas = graph.get('canvas');
|
||||
const label = node.get('group').get('children')[0];
|
||||
const shape = node.get('keyShape');
|
||||
|
||||
graph.emit('node:mouseenter', { type: 'mouseenter', target: label });
|
||||
expect(enter).toBe(1);
|
||||
|
||||
graph.emit('node:mouseenter', { type: 'mouseenter', target: shape });
|
||||
|
||||
expect(enter).toBe(2);
|
||||
|
||||
graph.emit('node:mouseenter', { type: 'mousemove', target: canvas });
|
||||
|
||||
graph.emit('node:mouseenter', { type: 'mousemove', target: shape });
|
||||
expect(enter).toBe(4);
|
||||
|
||||
graph.emit('mousemove', { type: 'mousemove', target: canvas });
|
||||
expect(enter).toBe(5);
|
||||
|
||||
expect(leave).toBe(0);
|
||||
graph.emit('node:mouseleave', { type: 'mouseleave', target: shape });
|
||||
expect(leave).toBe(1);
|
||||
|
||||
graph.emit('node:mouseleave', { type: 'mousemove', target: canvas });
|
||||
expect(leave).toBe(2);
|
||||
|
||||
graph.emit('node:mouseleave', { type: 'mousemove', taregt: canvas });
|
||||
expect(leave).toBe(3);
|
||||
});
|
||||
it('modified viewport', () => {
|
||||
let triggered = false;
|
||||
graph.off();
|
||||
// graph.on('mousedown', e => {
|
||||
// if (triggered) {
|
||||
// expect(e.canvasX).toBe(5);
|
||||
// expect(e.canvasY).toBe(-330);
|
||||
// expect(e.x).toBe(-95);
|
||||
// expect(e.y).toBe(125);
|
||||
// } else {
|
||||
// expect(e.canvasX).toBe(5);
|
||||
// expect(e.canvasY).toBe(-27.5);
|
||||
// expect(e.x).toBe(5);
|
||||
// expect(e.y).toBe(225);
|
||||
// }
|
||||
// });
|
||||
|
||||
graph.on('mouseup', (e) => {
|
||||
expect(e.canvasX).toBe(10);
|
||||
expect(e.canvasY).toBe(10);
|
||||
expect(e.x).toBe(-80);
|
||||
expect(e.y).toBe(-80);
|
||||
});
|
||||
|
||||
const canvas = graph.get('canvas').get('el');
|
||||
const bbox = canvas.getBoundingClientRect();
|
||||
|
||||
Simulate.simulate(canvas, 'mousedown', {
|
||||
clientY: bbox.right - 50,
|
||||
clientX: bbox.left + 10,
|
||||
});
|
||||
|
||||
graph.translate(100, 100);
|
||||
triggered = true;
|
||||
|
||||
Simulate.simulate(canvas, 'mousedown', {
|
||||
clientY: bbox.right - 50,
|
||||
clientX: bbox.left + 10,
|
||||
});
|
||||
|
||||
graph.zoom(0.5);
|
||||
|
||||
Simulate.simulate(canvas, 'mouseup', {
|
||||
clientY: bbox.top + 10,
|
||||
clientX: bbox.left + 10,
|
||||
});
|
||||
});
|
||||
it('item capture', () => {
|
||||
graph.off();
|
||||
const node = graph.addItem('node', { x: 100, y: 100, id: 'node' });
|
||||
|
||||
const canvas = graph.get('canvas').get('el');
|
||||
const bbox = canvas.getBoundingClientRect();
|
||||
|
||||
let targetItem;
|
||||
graph.on('mousedown', (e) => {
|
||||
targetItem = e.target;
|
||||
expect(targetItem === graph.get('canvas')).toBe(true);
|
||||
});
|
||||
|
||||
Simulate.simulate(canvas, 'mousedown', {
|
||||
clientY: bbox.right - 100,
|
||||
clientX: bbox.left + 100,
|
||||
});
|
||||
|
||||
targetItem = null;
|
||||
node.enableCapture(false);
|
||||
|
||||
Simulate.simulate(canvas, 'mouseup', {
|
||||
clientY: bbox.top + 100,
|
||||
clientX: bbox.left + 100,
|
||||
});
|
||||
expect(targetItem === node).toBe(false);
|
||||
});
|
||||
it('event object overlap', () => {
|
||||
let count = 0;
|
||||
let triggered = false;
|
||||
graph.off();
|
||||
graph.clear();
|
||||
|
||||
const canvas = graph.get('canvas');
|
||||
const node = graph.addItem('node', { x: 100, y: 100, size: 50, label: 'test' });
|
||||
|
||||
graph.on('node:mouseleave', (e) => {
|
||||
triggered = true;
|
||||
expect(e.type).toEqual('mouseleave');
|
||||
});
|
||||
|
||||
graph.on('mousemove', (e) => {
|
||||
count += 1;
|
||||
expect(e.type).toEqual('mousemove');
|
||||
});
|
||||
|
||||
canvas.emit('mousemove', { type: 'mousemove', target: node.get('keyShape') });
|
||||
expect(count).toEqual(1);
|
||||
expect(triggered).toBe(false);
|
||||
|
||||
canvas.emit('mousemove', { type: 'mousemove', target: canvas });
|
||||
expect(count).toEqual(2);
|
||||
expect(triggered).toBe(true);
|
||||
});
|
||||
|
||||
it('destory', () => {
|
||||
expect(graph).not.toBe(undefined);
|
||||
expect(graph.destroyed).toBe(false);
|
||||
graph.destroy();
|
||||
expect(graph.destroyed).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('event with name', () => {
|
||||
it('default node', () => {
|
||||
G6.registerNode(
|
||||
'custom-node',
|
||||
{
|
||||
drawShape(cfg, group) {
|
||||
const keyShape = group.addShape('rect', {
|
||||
attrs: {
|
||||
width: 120,
|
||||
height: 50,
|
||||
stroke: 'red',
|
||||
fill: '#ccc',
|
||||
},
|
||||
name: 'custom-node-rect',
|
||||
});
|
||||
|
||||
group.addShape('rect', {
|
||||
attrs: {
|
||||
width: 70,
|
||||
height: 30,
|
||||
stroke: 'green',
|
||||
fill: 'green',
|
||||
x: 20,
|
||||
y: 10,
|
||||
},
|
||||
name: 'custom-node-subrect',
|
||||
});
|
||||
return keyShape;
|
||||
},
|
||||
},
|
||||
'single-node',
|
||||
);
|
||||
|
||||
const graph = new G6.Graph({
|
||||
container: 'event-spec',
|
||||
width: 500,
|
||||
height: 400,
|
||||
nodeStateStyles: {
|
||||
selected: {
|
||||
fill: 'red',
|
||||
},
|
||||
},
|
||||
defaultNode: {
|
||||
type: 'custom-node',
|
||||
linkPoint: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node',
|
||||
label: 'node',
|
||||
x: 100,
|
||||
y: 200,
|
||||
},
|
||||
{
|
||||
id: 'node1',
|
||||
label: 'node1',
|
||||
x: 300,
|
||||
y: 200,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('node:mouseenter', (evt) => {
|
||||
graph.setItemState(evt.item, 'selected', true);
|
||||
});
|
||||
|
||||
graph.on('node:mouseleave', (evt) => {
|
||||
graph.setItemState(evt.item, 'selected', false);
|
||||
});
|
||||
|
||||
graph.on('custom-node-rect:click', (evt) => {
|
||||
graph.setItemState(evt.item, 'selected', true);
|
||||
const name = evt.target.get('name');
|
||||
expect(name).toEqual('custom-node-rect');
|
||||
});
|
||||
|
||||
graph.on('custom-node-subrect:click', (evt) => {
|
||||
const name = evt.target.get('name');
|
||||
expect(name).toEqual('custom-node-subrect');
|
||||
});
|
||||
|
||||
graph.destroy();
|
||||
});
|
||||
});
|
@ -1,4 +1,4 @@
|
||||
import { Graph } from '../../../../src';
|
||||
import Graph from '../implement-graph';
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.id = 'item-controller';
|
||||
@ -144,7 +144,7 @@ describe('item controller', () => {
|
||||
const shape = node.get('keyShape');
|
||||
expect(shape.attr('fill')).toEqual('#ccc');
|
||||
});
|
||||
it('fresh graph', (done) => {
|
||||
it('fresh graph', done => {
|
||||
graph.clear();
|
||||
const node = graph.addItem('node', { id: 'node6', x: 100, y: 100, size: 50 });
|
||||
const node2 = graph.addItem('node', { id: 'node7', x: 100, y: 200, size: 50 });
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ModeController } from '../../../../src/graph/controller';
|
||||
import Graph from '../../../../src/graph/graph';
|
||||
import Graph from '../implement-graph';
|
||||
import { GraphOptions, ModeOption } from '../../../../src/types';
|
||||
|
||||
const div = document.createElement('div');
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { Graph } from '../../../src';
|
||||
import '../../../src/behavior';
|
||||
import Graph from './implement-graph';
|
||||
import { ICombo } from '../../../src/interface/item';
|
||||
import { GraphData } from '../../../src/types';
|
||||
import { clone } from '@antv/util';
|
||||
@ -182,7 +181,7 @@ describe('graph with combo', () => {
|
||||
const comboA: ICombo = graph.findById('a') as ICombo;
|
||||
comboA.hide();
|
||||
const aChildren = comboA.getNodes();
|
||||
aChildren.forEach((child) => {
|
||||
aChildren.forEach(child => {
|
||||
expect(child.isVisible()).toBe(true);
|
||||
});
|
||||
|
||||
@ -191,10 +190,10 @@ describe('graph with combo', () => {
|
||||
graph.hideItem('b');
|
||||
const bChildren = comboB.getChildren();
|
||||
graph.uncombo(graph.findById('a') as ICombo);
|
||||
bChildren.nodes.forEach((node) => {
|
||||
bChildren.nodes.forEach(node => {
|
||||
expect(node.isVisible()).toBe(false);
|
||||
});
|
||||
bChildren.combos.forEach((combo) => {
|
||||
bChildren.combos.forEach(combo => {
|
||||
expect(combo.isVisible()).toBe(false);
|
||||
});
|
||||
expect(graph.findById('5-6').isVisible()).toBe(false);
|
||||
@ -232,26 +231,26 @@ describe('graph with combo', () => {
|
||||
// collapse a sub combo
|
||||
const comboE = graph.findById('e') as ICombo;
|
||||
graph.collapseCombo(comboE);
|
||||
comboE.getChildren().nodes.forEach((node) => {
|
||||
comboE.getChildren().nodes.forEach(node => {
|
||||
expect(node.isVisible()).toBe(false);
|
||||
});
|
||||
|
||||
// collapse a combo
|
||||
const comboB = graph.findById('b') as ICombo;
|
||||
graph.collapseCombo(comboB);
|
||||
comboB.getChildren().nodes.forEach((node) => {
|
||||
comboB.getChildren().nodes.forEach(node => {
|
||||
expect(node.isVisible()).toBe(false);
|
||||
});
|
||||
comboB.getChildren().combos.forEach((combo) => {
|
||||
comboB.getChildren().combos.forEach(combo => {
|
||||
expect(combo.isVisible()).toBe(false);
|
||||
});
|
||||
|
||||
// expand a combo
|
||||
graph.expandCombo(comboB);
|
||||
comboB.getChildren().nodes.forEach((node) => {
|
||||
comboB.getChildren().nodes.forEach(node => {
|
||||
expect(node.isVisible()).toBe(true);
|
||||
});
|
||||
comboB.getChildren().combos.forEach((combo) => {
|
||||
comboB.getChildren().combos.forEach(combo => {
|
||||
expect(combo.isVisible()).toBe(true);
|
||||
});
|
||||
|
||||
@ -259,12 +258,12 @@ describe('graph with combo', () => {
|
||||
// collapseExpand function
|
||||
graph.collapseExpandCombo('a');
|
||||
const comboA = graph.findById('a') as ICombo;
|
||||
comboA.getChildren().nodes.forEach((node) => {
|
||||
comboA.getChildren().nodes.forEach(node => {
|
||||
expect(node.isVisible()).toBe(false);
|
||||
});
|
||||
|
||||
graph.collapseExpandCombo(comboA);
|
||||
comboA.getChildren().nodes.forEach((node) => {
|
||||
comboA.getChildren().nodes.forEach(node => {
|
||||
expect(node.isVisible()).toBe(true);
|
||||
});
|
||||
|
||||
@ -288,9 +287,24 @@ describe('graph with combo', () => {
|
||||
};
|
||||
});
|
||||
graph.read(clone(data));
|
||||
expect(graph.getCombos()[0].getKeyShape().attr('fill')).toBe('red');
|
||||
expect(graph.getCombos()[0].getKeyShape().attr('stroke')).toBe('blue');
|
||||
expect(graph.getCombos()[0].getKeyShape().attr('lineWidth')).toBe(3);
|
||||
expect(
|
||||
graph
|
||||
.getCombos()[0]
|
||||
.getKeyShape()
|
||||
.attr('fill'),
|
||||
).toBe('red');
|
||||
expect(
|
||||
graph
|
||||
.getCombos()[0]
|
||||
.getKeyShape()
|
||||
.attr('stroke'),
|
||||
).toBe('blue');
|
||||
expect(
|
||||
graph
|
||||
.getCombos()[0]
|
||||
.getKeyShape()
|
||||
.attr('lineWidth'),
|
||||
).toBe(3);
|
||||
graph.destroy();
|
||||
});
|
||||
|
||||
@ -550,7 +564,7 @@ describe('empty combo', () => {
|
||||
default: ['drag-canvas', 'drag-node', 'drag-combo', 'collapse-expand-combo'],
|
||||
},
|
||||
});
|
||||
graph.on('canvas:click', (e) => {
|
||||
graph.on('canvas:click', e => {
|
||||
graph.createCombo('combo1', ['node1', 'node2']);
|
||||
});
|
||||
graph.data(data);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Graph } from '../../../src';
|
||||
import Graph from './implement-graph';
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.id = 'hull-spec';
|
||||
@ -9,46 +9,64 @@ const data = {
|
||||
{
|
||||
id: '1',
|
||||
label: '公司1',
|
||||
x: 100,
|
||||
y: 100,
|
||||
group: 1,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
label: '公司2',
|
||||
x: 120,
|
||||
y: 100,
|
||||
group: 1,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
label: '公司3',
|
||||
x: 150,
|
||||
y: 100,
|
||||
group: 1,
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
label: '公司4',
|
||||
x: 80,
|
||||
y: 150,
|
||||
group: 1,
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
label: '公司5',
|
||||
x: 100,
|
||||
y: 180,
|
||||
group: 2,
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
label: '公司6',
|
||||
x: 100,
|
||||
y: 210,
|
||||
group: 2,
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
label: '公司7',
|
||||
x: 300,
|
||||
y: 100,
|
||||
group: 2,
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
label: '公司8',
|
||||
x: 200,
|
||||
y: 300,
|
||||
group: 2,
|
||||
},
|
||||
{
|
||||
id: '9',
|
||||
label: '公司9',
|
||||
x: 190,
|
||||
y: 400,
|
||||
group: 2,
|
||||
},
|
||||
],
|
||||
@ -143,16 +161,12 @@ describe('graph hull', () => {
|
||||
container: div,
|
||||
width: 500,
|
||||
height: 500,
|
||||
modes: {
|
||||
default: ['drag-node', 'zoom-canvas', 'drag-canvas'],
|
||||
},
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
const members = graph.getNodes().filter((node) => node.getModel().group === 2);
|
||||
const nonMembers = graph.getNodes().filter((node) => node.getModel().group === 1);
|
||||
|
||||
const members = graph.getNodes().filter(node => node.getModel().group === 2);
|
||||
const nonMembers = graph.getNodes().filter(node => node.getModel().group === 1);
|
||||
it('add a convex hull', () => {
|
||||
graph.createHull({
|
||||
id: 'hull1',
|
||||
@ -182,8 +196,10 @@ describe('graph hull', () => {
|
||||
fill: 'lightgreen',
|
||||
stroke: 'green',
|
||||
},
|
||||
padding: 15,
|
||||
// TODO 如果这里设置为 15,会导致 convexHull.contain('4') 结果为 true
|
||||
padding: 5,
|
||||
});
|
||||
|
||||
expect(convexHull.contain('4')).toEqual(false);
|
||||
convexHull.addMember('4');
|
||||
expect(convexHull.contain('4')).toEqual(true);
|
||||
|
@ -1,24 +1,12 @@
|
||||
import { AbstractGraph } from '../../../src';
|
||||
import '../../../src/behavior';
|
||||
import { scale, translate } from '../../../src/util/math';
|
||||
import { GraphData, Item } from '../../../src/types';
|
||||
import Graph from './implement-graph';
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.id = 'global-spec';
|
||||
document.body.appendChild(div);
|
||||
|
||||
class Graph extends AbstractGraph {
|
||||
constructor(cfg) {
|
||||
super(cfg);
|
||||
}
|
||||
|
||||
initEventController() {}
|
||||
|
||||
initLayoutController() {}
|
||||
|
||||
initCanvas() {}
|
||||
}
|
||||
|
||||
describe('graph', () => {
|
||||
const globalGraph = new Graph({
|
||||
container: div,
|
||||
@ -93,7 +81,12 @@ describe('graph', () => {
|
||||
expect(inst.get('group')).not.toBe(undefined);
|
||||
|
||||
expect(inst.get('group').get('className')).toEqual('root-container');
|
||||
expect(inst.get('group').get('id').endsWith('-root')).toBe(true);
|
||||
expect(
|
||||
inst
|
||||
.get('group')
|
||||
.get('id')
|
||||
.endsWith('-root'),
|
||||
).toBe(true);
|
||||
|
||||
const children = inst.get('group').get('children');
|
||||
expect(children.length).toBe(4);
|
||||
@ -575,7 +568,10 @@ describe('graph', () => {
|
||||
|
||||
it('client point & model point convert', () => {
|
||||
const group = globalGraph.get('group');
|
||||
const bbox = globalGraph.get('canvas').get('el').getBoundingClientRect();
|
||||
const bbox = globalGraph
|
||||
.get('canvas')
|
||||
.get('el')
|
||||
.getBoundingClientRect();
|
||||
|
||||
let point = globalGraph.getPointByClient(bbox.left + 100, bbox.top + 100);
|
||||
|
||||
@ -658,10 +654,7 @@ describe('all node link center', () => {
|
||||
graph.render();
|
||||
|
||||
const edge = graph.findById('e1');
|
||||
expect(edge.get('keyShape').attr('path')).toEqual([
|
||||
['M', 10, 10],
|
||||
['L', 100, 100],
|
||||
]);
|
||||
expect(edge.get('keyShape').attr('path')).toEqual([['M', 10, 10], ['L', 100, 100]]);
|
||||
});
|
||||
|
||||
it('loop', () => {
|
||||
@ -672,10 +665,7 @@ describe('all node link center', () => {
|
||||
x: 150,
|
||||
y: 150,
|
||||
style: { fill: 'yellow' },
|
||||
anchorPoints: [
|
||||
[0, 0],
|
||||
[0, 1],
|
||||
],
|
||||
anchorPoints: [[0, 0], [0, 1]],
|
||||
});
|
||||
|
||||
const edge1 = graph.addItem('edge', {
|
||||
@ -879,7 +869,7 @@ describe('all node link center', () => {
|
||||
},
|
||||
});
|
||||
|
||||
defaultGraph.on('node:click', (e) => {
|
||||
defaultGraph.on('node:click', e => {
|
||||
e.item.setState('selected', true);
|
||||
e.item.refresh();
|
||||
});
|
||||
@ -1054,7 +1044,7 @@ describe('mapper fn', () => {
|
||||
});
|
||||
|
||||
it('node & edge mapper', () => {
|
||||
graph.node((node) => ({
|
||||
graph.node(node => ({
|
||||
id: `${node.id}Mapped`,
|
||||
size: [30, 30],
|
||||
label: node.id,
|
||||
@ -1065,7 +1055,7 @@ describe('mapper fn', () => {
|
||||
},
|
||||
}));
|
||||
|
||||
graph.edge((edge) => ({
|
||||
graph.edge(edge => ({
|
||||
id: `edge${edge.id}`,
|
||||
label: edge.id,
|
||||
labelCfg: {
|
||||
@ -1087,7 +1077,7 @@ describe('mapper fn', () => {
|
||||
expect(keyShape.attr('fill')).toEqual('#666');
|
||||
|
||||
const container = node.getContainer();
|
||||
let label = container.find((element) => element.get('className') === 'node-label');
|
||||
let label = container.find(element => element.get('className') === 'node-label');
|
||||
expect(label).not.toBe(undefined);
|
||||
expect(label.attr('text')).toEqual('node');
|
||||
expect(label.attr('fill')).toEqual('#666');
|
||||
@ -1101,7 +1091,7 @@ describe('mapper fn', () => {
|
||||
expect(keyShape.attr('opacity')).toEqual(0.5);
|
||||
expect(keyShape.get('type')).toEqual('path');
|
||||
|
||||
label = edge.getContainer().find((element) => element.get('className') === 'edge-label');
|
||||
label = edge.getContainer().find(element => element.get('className') === 'edge-label');
|
||||
expect(label).not.toBe(undefined);
|
||||
expect(label.attr('text')).toEqual('edge');
|
||||
expect(label.attr('x')).toEqual(115.5);
|
||||
@ -1112,7 +1102,7 @@ describe('mapper fn', () => {
|
||||
});
|
||||
|
||||
it('node & edge mapper with states', () => {
|
||||
graph.node((node) => ({
|
||||
graph.node(node => ({
|
||||
type: 'rect',
|
||||
label: node.id,
|
||||
style: {
|
||||
@ -1136,9 +1126,9 @@ describe('mapper fn', () => {
|
||||
|
||||
let keyShape = node.getKeyShape();
|
||||
expect(keyShape.attr('fill')).toEqual('#666');
|
||||
expect(
|
||||
node.getContainer().find((element) => element.get('className') === 'node-label'),
|
||||
).not.toBe(undefined);
|
||||
expect(node.getContainer().find(element => element.get('className') === 'node-label')).not.toBe(
|
||||
undefined,
|
||||
);
|
||||
|
||||
graph.setItemState(node, 'selected', true);
|
||||
expect(keyShape.attr('blue'));
|
||||
@ -1239,25 +1229,27 @@ describe('auto rotate label on edge', () => {
|
||||
expect(label2Matrix).toBe(null);
|
||||
});
|
||||
|
||||
it('drag node', () => {
|
||||
const node = graph.getNodes()[1];
|
||||
graph.emit('node:dragstart', { x: 80, y: 150, item: node });
|
||||
graph.emit('node:drag', { x: 200, y: 200, item: node });
|
||||
graph.emit('node:dragend', { x: 200, y: 200, item: node });
|
||||
const edge1 = graph.getEdges()[0];
|
||||
const label1 = edge1.get('group').get('children')[1];
|
||||
const label1Matrix = label1.attr('matrix');
|
||||
expect(label1Matrix[0]).toBe(0.7071067811865476);
|
||||
expect(label1Matrix[1]).toBe(0.7071067811865475);
|
||||
expect(label1Matrix[3]).toBe(-0.7071067811865475);
|
||||
expect(label1Matrix[4]).toBe(0.7071067811865476);
|
||||
expect(label1Matrix[6]).toBe(124.99999999999999);
|
||||
expect(label1Matrix[7]).toBe(-51.77669529663689);
|
||||
const edge2 = graph.getEdges()[1];
|
||||
const label2 = edge2.get('group').get('children')[1];
|
||||
const label2Matrix = label2.attr('matrix');
|
||||
expect(label2Matrix).toBe(null);
|
||||
});
|
||||
// it.only('drag node', () => {
|
||||
|
||||
// const node = graph.getNodes()[1];
|
||||
// graph.emit('node:dragstart', { x: 80, y: 150, item: node });
|
||||
// graph.emit('node:drag', { x: 200, y: 200, item: node });
|
||||
// graph.emit('node:dragend', { x: 200, y: 200, item: node });
|
||||
// const edge1 = graph.getEdges()[0];
|
||||
// const label1 = edge1.get('group').get('children')[1];
|
||||
// const label1Matrix = label1.attr('matrix');
|
||||
// console.log(label1Matrix);
|
||||
// expect(label1Matrix[0]).toBe(0.7071067811865476);
|
||||
// expect(label1Matrix[1]).toBe(0.7071067811865475);
|
||||
// expect(label1Matrix[3]).toBe(-0.7071067811865475);
|
||||
// expect(label1Matrix[4]).toBe(0.7071067811865476);
|
||||
// expect(label1Matrix[6]).toBe(124.99999999999999);
|
||||
// expect(label1Matrix[7]).toBe(-51.77669529663689);
|
||||
// const edge2 = graph.getEdges()[1];
|
||||
// const label2 = edge2.get('group').get('children')[1];
|
||||
// const label2Matrix = label2.attr('matrix');
|
||||
// expect(label2Matrix).toBe(null);
|
||||
// });
|
||||
|
||||
it('zoom and pan', () => {
|
||||
graph.zoom(0.5);
|
||||
@ -1272,38 +1264,6 @@ describe('auto rotate label on edge', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('auto rotate label on edge', () => {
|
||||
const graph = new Graph({
|
||||
container: div,
|
||||
width: 500,
|
||||
height: 500,
|
||||
modes: {
|
||||
default: ['drag-node', 'zoom-canvas', 'drag-canvas'],
|
||||
},
|
||||
});
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
x: 100,
|
||||
y: 200,
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
x: 800,
|
||||
y: 200,
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
target: 'node2',
|
||||
source: 'node1',
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
describe('node Neighbors', () => {
|
||||
const graph = new Graph({
|
||||
container: 'global-spec',
|
||||
|
41
packages/core/tests/unit/graph/implement-graph.ts
Normal file
41
packages/core/tests/unit/graph/implement-graph.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { Canvas as GCanvas } from '@antv/g-canvas';
|
||||
import { AbstractGraph } from '../../../src';
|
||||
|
||||
export default class Graph extends AbstractGraph {
|
||||
constructor(cfg) {
|
||||
super(cfg);
|
||||
}
|
||||
|
||||
initEventController() {}
|
||||
|
||||
initLayoutController() {}
|
||||
|
||||
initCanvas() {
|
||||
let container: string | HTMLElement | Element | null = this.get('container');
|
||||
if (typeof container === 'string') {
|
||||
container = document.getElementById(container);
|
||||
this.set('container', container);
|
||||
}
|
||||
|
||||
if (!container) {
|
||||
throw new Error('invalid container');
|
||||
}
|
||||
|
||||
const width: number = this.get('width');
|
||||
const height: number = this.get('height');
|
||||
|
||||
const canvasCfg: any = {
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
};
|
||||
const pixelRatio = this.get('pixelRatio');
|
||||
if (pixelRatio) {
|
||||
canvasCfg.pixelRatio = pixelRatio;
|
||||
}
|
||||
|
||||
const canvas = new GCanvas(canvasCfg);
|
||||
|
||||
this.set('canvas', canvas);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,73 +0,0 @@
|
||||
import { TreeGraph } from '../../../src';
|
||||
import { timerOut } from '../util/timeOut';
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.id = 'tree-spec';
|
||||
document.body.appendChild(div);
|
||||
|
||||
describe('tree graph without updateChild', () => {
|
||||
let graph = new TreeGraph({
|
||||
container: div,
|
||||
width: 500,
|
||||
height: 500,
|
||||
animate: false,
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node'],
|
||||
},
|
||||
layout: {
|
||||
type: 'dendrogram',
|
||||
direction: 'LR',
|
||||
// H / V / LR / RL / TB / BT
|
||||
nodeSep: 50,
|
||||
rankSep: 100,
|
||||
},
|
||||
});
|
||||
it('update child', () => {
|
||||
fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.json')
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
graph.node(function (node) {
|
||||
return {
|
||||
label: node.id,
|
||||
labelCfg: {
|
||||
offset: 10,
|
||||
position: node.children && node.children.length > 0 ? 'left' : 'right',
|
||||
},
|
||||
};
|
||||
});
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
graph.fitView();
|
||||
|
||||
graph.updateChildren(
|
||||
[
|
||||
{
|
||||
id: 'subTree1',
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 'subTree2',
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 'subTree3',
|
||||
children: [
|
||||
{
|
||||
id: 'aaa',
|
||||
},
|
||||
{
|
||||
id: 'bbb',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
'Methods',
|
||||
);
|
||||
const newParentData = graph.findDataById('Methods');
|
||||
expect(newParentData.children.length).toBe(3);
|
||||
const subTree3 = graph.findById('subTree3');
|
||||
expect(subTree3).not.toBe(undefined);
|
||||
graph.destroy();
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user