Skip to content

Commit

Permalink
core(packages):initial packages
Browse files Browse the repository at this point in the history
  • Loading branch information
pomelo-nwu committed Nov 15, 2019
1 parent b402aa3 commit fe510c3
Show file tree
Hide file tree
Showing 259 changed files with 25,209 additions and 165 deletions.
7 changes: 7 additions & 0 deletions packages/graphin-components/.fatherrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default {
esm: {
type: 'rollup',
},
lessInRollupMode: {},
injectCSS: true,
};
43 changes: 43 additions & 0 deletions packages/graphin-components/__mock__/g6.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import EventEmitter from 'eventemitter3';

let canvasSize = { width: 1000, height: 1000 };

export const setCanvasSize = size => {
canvasSize = size;
};

export const getMockGraph = () => {
let mockGraph: any = new EventEmitter();

mockGraph.get = key => {
return mockGraph[key];
};
mockGraph.canvas = {
get: key => {
return mockGraph.canvas[key];
},
width: canvasSize.width,
height: canvasSize.height,
};
mockGraph.getCanvasByPoint = (x, y) => {
return {
x,
y,
};
};
return mockGraph;
};

export const getMockG6Event = (BBox = { x: 0, y: 0 }) => {
return {
preventDefault: () => {},
item: {
getBBox: () => {
return {
maxY: BBox.y,
minX: BBox.x,
};
},
},
};
};
1 change: 1 addition & 0 deletions packages/graphin-components/__mock__/styleMock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = {};
14 changes: 14 additions & 0 deletions packages/graphin-components/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
preset: 'ts-jest',
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
testRegex: '/__tests__/.*\\.test.(ts|tsx)$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
setupFiles: ['jest-canvas-mock'],
collectCoverage: true,
collectCoverageFrom: ['src/**/*.{ts,tsx}'],
moduleNameMapper: {
'\\.(css|less|sass|scss)$': '<rootDir>/__mock__/styleMock.ts',
},
};
33 changes: 33 additions & 0 deletions packages/graphin-components/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@antv/graphin-components",
"version": "0.1.0",
"description": "Components for graphin.",
"main": "./dist/index.esm",
"types": "./dist/src/index.d",
"scripts": {
"start": "father build --watch",
"build": "father build",
"test": "jest"
},
"author": "",
"license": "ISC",
"peerDependencies": {
"react": "^16.11.0",
"react-dom": "^16.11.0",
"antd": "^3.23.5",
"@antv/graphin": "^1.0.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^4.2.0",
"@testing-library/react": "^9.3.0",
"father": "^2.23.1",
"jest": "^24.9.0",
"ts-jest": "^24.1.0",
"jest-canvas-mock": "^2.1.2",
"typescript": "^3.6.4",
"eventemitter3": "^4.0.0"
},
"dependencies": {
"@antv/g6": "^3.1.5"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React, { ReactElement } from 'react';
import { Menu, Icon } from 'antd';
import { Graph } from '@antv/g6';

const MenuItem = Menu.Item;

const IconFont = Icon.createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/font_1187963_50nfpuasd7b.js',
});

export interface MenuItemType {
key: string;
visible?: boolean;
/** antd icon type */
iconType?: string;
title?: string;
width?: number;
height?: number;
onClick?: (props: ContainerProps) => void;
/** antd icon type */
useCustomIcon?: boolean;
/** user defined render function */
render?: (props: ContainerProps) => ReactElement;
}

interface ContainerProps {
graph: Graph;
menu: MenuItemType[];
onClose: () => void;
}

const Container: React.FC<ContainerProps> = props => {
const { menu, onClose } = props;

const onClickMenuItem = (item: MenuItemType) => {
onClose();
// call user defined menu item onClick callback
if (item.onClick) item.onClick(props);
};

const menuItems = menu
.filter(item => !(item.visible === false)) // item.visible 不传时默认可见
.map(item => {
// render icon
const iconProps = {
type: item.iconType,
style: { fontSize: '12px' },
};
const icon = item.useCustomIcon === false ? <Icon {...iconProps} /> : <IconFont {...iconProps} />;

return (
<MenuItem key={item.key} className={item.key} onClick={() => onClickMenuItem(item)}>
{item.render ? (
item.render(props)
) : (
<>
{icon}
{item.title}
</>
)}
</MenuItem>
);
});

return (
<div className="graphin-context-menu">
<Menu>{menuItems}</Menu>
</div>
);
};

export default Container;
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import React from 'react';
import { render, act, fireEvent, cleanup } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { getMockG6Event, getMockGraph } from '../../../../__mock__/g6';
import { ContextMenu, isEqual, ContextMenuProps } from '../index';
import getPosition from '../getPosition';

const mockGraph = getMockGraph();
const mockG6Event = getMockG6Event();

interface PropType {
graph: any; // eslint-disable-line
options: ContextMenuProps['options'];
render: ContextMenuProps['render'];
}

describe('<ContextMenu />', () => {
afterEach(() => {
cleanup();
jest.resetModules();
});

it('rendenrs menu when trigger contextmenu event', () => {
const mockCallback = jest.fn(() => {});

// TODO icon 测试
const { asFragment, queryByText, rerender, getByText, unmount } = render(
<ContextMenu
graph={mockGraph}
options={[
{
key: 'select',
title: 'Select',
iconType: 'icon',
visible: true,
onClick: mockCallback,
},
{
key: 'invert-select',
title: 'Invert Select',
iconType: 'icon',
visible: false,
onClick: () => {},
},
{
key: 'delete',
title: 'Delete',
visible: true,
useCustomIcon: true,
render: () => <div>Delete</div>,
},
]}
/>,
);
expect(asFragment().children.length).toEqual(0);
act(() => {
mockGraph.emit('node:contextmenu', mockG6Event);
});

// 菜单是否正确显示
expect(asFragment().children.length).not.toEqual(0);
expect(queryByText(/Select/)).toBeTruthy();
expect(queryByText(/Invert Select/)).toBeFalsy();
expect(queryByText(/Delete/)).toBeTruthy();

act(() => {
fireEvent.click(getByText(/Select/), {});
fireEvent.click(getByText(/Delete/), {});
});

expect(mockCallback).toBeCalled(); // 菜单点击 callback 是否被调用
expect(asFragment().children.length).toEqual(0); // 点击菜单 item 后,菜单是否隐藏

// render function invalid
rerender(
<ContextMenu
graph={mockGraph}
// eslint-disable-next-line
render={(): any => {
return 1;
}}
/>,
);
act(() => {
mockGraph.emit('node:contextmenu', mockG6Event);
});
act(() => {
mockGraph.emit('canvas:click', mockG6Event); // reset
});

// 测试自定义菜单
rerender(
<ContextMenu
graph={mockGraph}
render={() => {
return (
<ul>
<li>User Defined Item 1</li>
<li>User Defined Item 2</li>
<li>User Defined Item 3</li>
</ul>
);
}}
/>,
);

expect(asFragment().children.length).toEqual(0);

act(() => {
mockGraph.emit('node:contextmenu', mockG6Event);
});
// 菜单是否正确显示
expect(asFragment().children.length).not.toEqual(0);
expect(queryByText(/User Defined Item 1/)).toBeTruthy();

// Menu should hide when click canvas
act(() => {
mockGraph.emit('canvas:click', mockG6Event);
});
expect(asFragment().children.length).toEqual(0);

// Contextmenu event should not be active when ctrl keydown
act(() => {
mockGraph.emit('keydown', { keyCode: 10 });
mockGraph.emit('keydown', { keyCode: 17 });
mockGraph.emit('keydown', { which: 17 });
});
act(() => {
mockGraph.emit('node:contextmenu', mockG6Event);
});
expect(asFragment().children.length).toEqual(0);

// Contextmenu event should be active when keyup
act(() => {
mockGraph.emit('keyup', {});
});
act(() => {
mockGraph.emit('node:contextmenu', mockG6Event);
});
expect(asFragment().children.length).not.toEqual(0);
});

it('should rerender when render prop change', () => {
const options = [
{
key: 'select',
text: 'Select',
icon: 'icon',
visible: true,
onClick: () => {},
},
];
const renderFunc = () => <div>foo</div>;

const prevProps: PropType = { graph: {}, options, render: renderFunc };
const optionsChangeProps: PropType = { graph: {}, options, render: renderFunc };
const renderChangeProps: PropType = { graph: {}, options, render: renderFunc };

expect(isEqual(prevProps, optionsChangeProps)).toBeTruthy();
expect(isEqual(prevProps, renderChangeProps)).toBeTruthy();

optionsChangeProps.options = [
{
key: 'select',
title: 'Select',
iconType: 'icon',
visible: false,
onClick: () => {},
},
];
expect(isEqual(prevProps, optionsChangeProps)).toBeFalsy();

renderChangeProps.render = () => <div>bar</div>;
expect(isEqual(prevProps, renderChangeProps)).toBeFalsy();
});

it('should calculate right position', () => {
let g6Event = getMockG6Event({ x: 100, y: 100 }) as any; // eslint-disable-line
expect(getPosition(mockGraph, g6Event, { width: 250, height: 300 })).toEqual({ x: 100, y: 100 });

g6Event = getMockG6Event({ x: 900, y: 900 }) as any; // eslint-disable-line
expect(getPosition(mockGraph, g6Event, { width: 250, height: 300 })).toEqual({ x: 650, y: 600 });
});
});
Loading

0 comments on commit fe510c3

Please sign in to comment.