@moxa/graph 3.0.0-beta.1 → 3.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +602 -114
  2. package/assets/icon-sprite.svg +1 -1
  3. package/components/edge-label/index.d.ts +4 -4
  4. package/components/edge-label/index.d.ts.map +1 -1
  5. package/components/edge-polyline/index.d.ts.map +1 -1
  6. package/components/node-device/models/index.d.ts +1 -0
  7. package/components/node-device/models/index.d.ts.map +1 -1
  8. package/components/node-label/index.d.ts +1 -1
  9. package/components/node-label/index.d.ts.map +1 -1
  10. package/components/shared/transforms/edge-transform.d.ts.map +1 -1
  11. package/components/shared/transforms/node-transform.d.ts.map +1 -1
  12. package/components/shared/utils/node-utils/config.d.ts +27 -0
  13. package/components/shared/utils/node-utils/config.d.ts.map +1 -0
  14. package/components/shared/utils/node-utils/icon-style.d.ts.map +1 -1
  15. package/components/shared/utils/node-utils/index.d.ts +5 -5
  16. package/components/shared/utils/node-utils/index.d.ts.map +1 -1
  17. package/components/shared/utils/node-utils/offset.d.ts +2 -6
  18. package/components/shared/utils/node-utils/offset.d.ts.map +1 -1
  19. package/components/shared/utils/node-utils/text-style.d.ts.map +1 -1
  20. package/components/shared/utils/node-utils/title-style.d.ts.map +1 -1
  21. package/core/model/index.d.ts +1 -0
  22. package/core/model/index.d.ts.map +1 -1
  23. package/core/model/label.model.d.ts +2 -0
  24. package/core/model/label.model.d.ts.map +1 -1
  25. package/core/model/plugin.model.d.ts +1 -1
  26. package/core/model/text.model.d.ts +5 -0
  27. package/core/model/text.model.d.ts.map +1 -0
  28. package/index.cjs +57 -57
  29. package/index.js +3643 -3580
  30. package/layouts/grid/index.d.ts.map +1 -1
  31. package/package.json +1 -1
  32. package/plugins/context-menu/index.d.ts +11 -1
  33. package/plugins/context-menu/index.d.ts.map +1 -1
  34. package/plugins/minimap/index.d.ts +3 -2
  35. package/plugins/minimap/index.d.ts.map +1 -1
  36. package/shared/transforms/plugin-transform.d.ts +1 -1
package/README.md CHANGED
@@ -4,6 +4,10 @@
4
4
 
5
5
  - [Install](#install)
6
6
  - [Basic Usage](#basic-usage)
7
+ - [Components](#components)
8
+ - [Behaviors](#behaviors)
9
+ - [Layouts](#layouts)
10
+ - [Plugins](#plugins)
7
11
  - [Graph Control](#graph-control)
8
12
  - [Event Handling](#event-handling)
9
13
  - [Developer Guidelines](#developer-guidelines)
@@ -41,45 +45,34 @@ pnpm add @moxa/graph
41
45
  import { Graph } from '@moxa/graph';
42
46
 
43
47
  const graph = new Graph({
44
- renderer: 'svg',
45
48
  container: 'container',
46
49
  width: 500,
47
50
  height: 500,
51
+ renderer: 'canvas', // 'canvas', 'svg', or 'webgl'
48
52
  data: {
49
53
  nodes: [
50
54
  {
51
55
  id: 'node1',
52
- config: {
53
- type: 'circle-node',
54
- point: { x: 100, y: 50 },
56
+ data: {
57
+ type: 'node-device',
58
+ x: 100,
59
+ y: 50,
55
60
  },
56
61
  },
57
62
  {
58
63
  id: 'node2',
59
- config: {
60
- type: 'circle-node',
61
- point: { x: 100, y: 250 },
64
+ data: {
65
+ type: 'node-device',
66
+ x: 100,
67
+ y: 250,
62
68
  },
63
69
  },
64
70
  {
65
71
  id: 'node3',
66
- config: {
67
- type: 'circle-node',
68
- point: { x: 300, y: 50 },
69
- },
70
- },
71
- {
72
- id: 'node4',
73
- config: {
74
- type: 'circle-node',
75
- point: { x: 300, y: 250 },
76
- },
77
- },
78
- {
79
- id: 'node5',
80
- config: {
81
- type: 'circle-node',
82
- point: { x: 200, y: 150 },
72
+ data: {
73
+ type: 'node-device',
74
+ x: 300,
75
+ y: 50,
83
76
  },
84
77
  },
85
78
  ],
@@ -88,91 +81,228 @@ const graph = new Graph({
88
81
  id: 'edge1',
89
82
  source: 'node1',
90
83
  target: 'node2',
84
+ data: {
85
+ type: 'edge-line',
86
+ },
91
87
  },
92
88
  {
93
89
  id: 'edge2',
94
- source: 'node1',
95
- target: 'node5',
96
- },
97
- {
98
- id: 'edge3',
99
- source: 'node5',
90
+ source: 'node2',
100
91
  target: 'node3',
101
- },
102
- {
103
- id: 'edge4',
104
- source: 'node3',
105
- target: 'node4',
92
+ data: {
93
+ type: 'edge-quadratic',
94
+ },
106
95
  },
107
96
  ],
108
97
  },
109
98
  });
99
+
100
+ // Render the graph
101
+ graph.render();
110
102
  ```
111
103
 
104
+ ## Components
105
+
106
+ ### Node Components
107
+ - **node-device**: Device node with icon and label support
108
+ - **node-icon**: Icon-only node component
109
+ - **node-label**: Label-only node component
110
+
111
+ ### Edge Components
112
+ - **edge-line**: Straight line edges
113
+ - **edge-polyline**: Multi-segment polyline edges
114
+ - **edge-quadratic**: Curved quadratic edges
115
+ - **edge-arrow**: Arrow markers for edges
116
+ - **edge-label**: Labels for edges
117
+
118
+ ### Group Components
119
+ - **group-device**: Device group with expand/collapse functionality
120
+
121
+ ## Behaviors
122
+
123
+ Interactive behaviors for user interaction:
124
+
125
+ - **brush-select**: Rectangle selection
126
+ - **click-select**: Click to select elements
127
+ - **collapse-expand**: Group collapse/expand
128
+ - **create-edge**: Interactive edge creation
129
+ - **drag-canvas**: Canvas dragging
130
+ - **drag-element**: Element dragging
131
+ - **fix-element-size**: Fixed element sizing
132
+ - **focus-element**: Element focusing
133
+ - **hover-activate**: Hover activation
134
+ - **scroll-canvas**: Canvas scrolling
135
+ - **select-all**: Select all elements
136
+ - **zoom-canvas**: Canvas zooming
137
+
138
+ ## Layouts
139
+
140
+ Automatic layout algorithms:
141
+
142
+ - **align**: Alignment layout
143
+ - **force**: Force-directed layout
144
+ - **grid**: Grid layout
145
+ - **ring**: Circular ring layout
146
+ - **tree**: Hierarchical tree layout
147
+
148
+ ## Plugins
149
+
150
+ Extensible plugin system:
151
+
152
+ - **context-menu**: Right-click context menu
153
+ - **element-toolbar**: Element-specific toolbar
154
+ - **fixed-toolbar**: Fixed position toolbar
155
+ - **graph-background**: Customizable graph background
156
+ - **history**: Undo/redo functionality
157
+ - **minimap**: Mini navigation map
158
+ - **snapline**: Element alignment guides
159
+ - **tooltip**: Element tooltips
160
+
112
161
  ## Graph Control
113
162
 
114
163
  > You can control the presentation and behavior of the graph by calling the `Graph` methods
115
164
 
116
165
  ```typescript
117
- // Update Graph Data
118
- graph.updateNode({ ... });
119
- graph.updateEdge({ ... });
120
- graph.updateGroup({ ... });
166
+ // Data Management
167
+ graph.addNode({ id: 'node1', data: { type: 'node-device' } });
168
+ graph.updateNode('node1', { data: { x: 100, y: 100 } });
169
+ graph.removeNode('node1');
170
+
171
+ graph.addEdge({ id: 'edge1', source: 'node1', target: 'node2' });
172
+ graph.updateEdge('edge1', { data: { type: 'edge-line' } });
173
+ graph.removeEdge('edge1');
174
+
175
+ graph.addGroup({ id: 'group1', data: { type: 'group-device' } });
176
+ graph.updateGroup('group1', { data: { collapsed: true } });
177
+ graph.removeGroup('group1');
121
178
 
122
179
  // View Control
123
- graph.zoom();
180
+ graph.zoom(1.5);
181
+ graph.zoomTo(2.0, { x: 100, y: 100 });
124
182
  graph.fitView();
125
- graph.focusItem();
183
+ graph.fitCenter();
184
+ graph.focusItem('node1');
185
+
186
+ // Layout
187
+ graph.setLayout({ type: 'force', options: {} });
188
+ graph.layout();
126
189
 
127
- // Change Behaviors
128
- graph.changeBehavior([ ... ], true);
190
+ // Behaviors
191
+ graph.setBehavior(['drag-canvas', 'zoom-canvas']);
129
192
 
130
- // Change layouts
131
- graph.changeLayout();
193
+ // Plugins
194
+ graph.setPlugin('minimap', { size: [200, 150] });
132
195
 
133
- // Change Theme
134
- graph.changeTheme();
196
+ // Themes
197
+ graph.setTheme('dark');
198
+ graph.setThemeTokens({ primaryColor: '#1890ff' });
135
199
 
136
- // Plugin Management
137
- graph.addPlugin({ ... });
138
- graph.updatePlugin({ ... });
139
- graph.removePlugin({ ... });
200
+ // State Management
201
+ graph.setElementState('node1', 'selected', true);
202
+ graph.clearElementState('node1', 'selected');
140
203
  ```
141
204
 
142
205
  ## Event Handling
143
206
 
144
207
  ```typescript
145
208
  // Listen Events
146
- graph.events.nodeClick.once((e) => { ... });
147
- graph.events.beginCreateEdge.on((e) => { ... });
148
- graph.events.groupExpanded.off((e) => { ... });
209
+ graph.on('node:click', (event) => {
210
+ console.log('Node clicked:', event.itemId);
211
+ });
212
+
213
+ graph.on('edge:click', (event) => {
214
+ console.log('Edge clicked:', event.itemId);
215
+ });
216
+
217
+ graph.on('canvas:click', (event) => {
218
+ console.log('Canvas clicked:', event.canvas);
219
+ });
220
+
221
+ // Remove event listeners
222
+ graph.off('node:click', listener);
223
+
224
+ // One-time event listeners
225
+ graph.once('afterrender', () => {
226
+ console.log('Graph rendered');
227
+ });
149
228
  ```
150
229
 
151
230
  ## Developer Guidelines
152
231
 
153
232
  This section provides guidelines for developers who want to contribute to or extend the Moxa Graph library.
154
233
 
155
- ### Project Structure
234
+ ### Project Structure & Folder Responsibilities
156
235
 
157
236
  ```
158
237
  libs/graph/
159
- ├── src/
160
- │ ├── lib/ # Core functionality
161
- ├── behavior/ # Graph behaviors and interactions
162
- ├── edge/ # Edge definitions and renderers
163
- ├── graph/ # Main graph implementation
164
- ├── group/ # Node grouping functionality
165
- ├── layout/ # Layout algorithms
166
- ├── model/ # Data models and types
167
- ├── node/ # Node definitions and renderers
168
- │ ├── plugin/ # Plugin system
169
- │ │ ├── themes/ # Theming and styling
170
- │ │ └── utils/ # Utility functions
171
- ├── assets/ # Static assets
172
- │ ├── styles/ # Global styles
173
- │ └── stories/ # Storybook examples
238
+ ├── src/ # Source code
239
+ │ ├── core/ # 🔧 Core functionality (Graph API, models, utilities)
240
+ │ ├── components/ # 🎨 Visual components (nodes, edges, groups)
241
+ │ ├── behaviors/ # 🖱️ Interactive behaviors (12 behaviors)
242
+ │ ├── layouts/ # 📐 Layout algorithms (5 layouts)
243
+ │ ├── plugins/ # 🔌 Plugin system (8 plugins)
244
+ │ ├── shared/ # 🛠️ Shared utilities and types
245
+ │ ├── assets/ # 📦 Static assets (icons, images)
246
+ │ ├── styles/ # 🎨 Global styles
247
+ └── stories/ # 📚 API documentation
248
+ ├── tests/ # 🧪 Test utilities
249
+ ├── e2e/ # 🎭 End-to-end testing
250
+ ├── .storybook/ # 📖 Storybook configuration
251
+ └── package.json # 📦 Package configuration
174
252
  ```
175
253
 
254
+ #### Detailed Folder Responsibilities
255
+
256
+ ##### Core (`src/core/`)
257
+ **Purpose**: Essential graph functionality and APIs
258
+ - **`graph/`**: Main Graph class implementing the public API, lifecycle management, and core operations
259
+ - **`model/`**: TypeScript type definitions, interfaces, and data structures used throughout the library
260
+ - **`utils/`**: Core utility functions for graph manipulation, theme management, and element operations
261
+
262
+ ##### Components (`src/components/`)
263
+ **Purpose**: Visual rendering components for graph elements
264
+ - **Node Components**: Different node types (device, icon, label) with specialized rendering logic
265
+ - **Edge Components**: Various edge types (line, polyline, quadratic) with arrow and label support
266
+ - **Group Components**: Container components for grouping and organizing elements
267
+ - **Shared**: Common utilities and transformations used across components
268
+
269
+ ##### Behaviors (`src/behaviors/`)
270
+ **Purpose**: User interaction handling and graph manipulation
271
+ - Each behavior handles specific user interactions (clicking, dragging, selecting)
272
+ - Behaviors are modular and can be enabled/disabled independently
273
+ - Includes both mouse and keyboard interaction handling
274
+
275
+ ##### Layouts (`src/layouts/`)
276
+ **Purpose**: Automatic positioning algorithms for graph elements
277
+ - **Force**: Physics-based layout for natural node arrangements
278
+ - **Tree**: Hierarchical layouts for tree-structured data
279
+ - **Grid**: Regular grid arrangements for systematic positioning
280
+ - **Ring**: Circular arrangements for cyclical data
281
+ - **Align**: Tools for manual element alignment
282
+
283
+ ##### Plugins (`src/plugins/`)
284
+ **Purpose**: Extensible features that enhance graph functionality
285
+ - Each plugin is self-contained and optional
286
+ - Plugins can add UI elements (toolbars, tooltips, minimap)
287
+ - Includes data management features (history, context menus)
288
+
289
+ ##### Shared (`src/shared/`)
290
+ **Purpose**: Common utilities and infrastructure
291
+ - **Types**: Shared TypeScript definitions used across modules
292
+ - **Utils**: Generic utility functions and shared components
293
+ - **Transforms**: Data transformation pipeline for processing graph configurations
294
+ - **Constants**: Application-wide constants and configuration values
295
+
296
+ ##### Testing Infrastructure
297
+ - **`tests/`**: Reusable test utilities and helper functions
298
+ - **`e2e/`**: End-to-end visual regression testing with Playwright
299
+ - **Each component includes its own test suite with visual snapshots
300
+
301
+ ##### Documentation & Development
302
+ - **`.storybook/`**: Interactive documentation and component playground
303
+ - **`stories/`**: Written documentation and API guides
304
+ - **Each component includes comprehensive Storybook stories for demonstration and testing
305
+
176
306
  ### Development Workflow
177
307
 
178
308
  1. **Setup Development Environment**
@@ -194,92 +324,443 @@ libs/graph/
194
324
  # Build the library
195
325
  pnpm build:graph
196
326
 
197
- # Run tests
327
+ # Run unit tests
198
328
  pnpm test:graph
329
+
330
+ # Run e2e tests
331
+ pnpm e2e:graph
332
+
333
+ # Start Storybook for development
334
+ pnpm storybook:graph
199
335
  ```
200
336
 
201
337
  ### Extending the Library
202
338
 
203
- #### Creating Custom Nodes
339
+ #### Creating Custom Components
204
340
 
205
- Custom nodes can be created by extending the base node classes:
341
+ Custom components are created using the G6 5.x API and registered with specific types:
206
342
 
207
343
  ```typescript
208
- import { BaseNode, NodeConfig } from '@moxa/graph';
344
+ import { ExtensionController } from '@antv/g6';
209
345
 
210
- interface CustomNodeConfig extends NodeConfig {
211
- customProperty: string;
212
- }
346
+ // Custom Node Component
347
+ const customNode = (context) => {
348
+ const { model, theme } = context;
349
+ const { data } = model;
213
350
 
214
- class CustomNode extends BaseNode<CustomNodeConfig> {
215
- render() {
216
- // Implement custom rendering logic
217
- const shape = this.createShape('circle', {
351
+ return {
352
+ circle: {
218
353
  r: 20,
219
- fill: this.config.customProperty === 'special' ? 'red' : 'blue',
220
- });
354
+ fill: data.customProperty === 'special' ? 'red' : 'blue',
355
+ stroke: theme.nodeStroke,
356
+ lineWidth: 2,
357
+ },
358
+ };
359
+ };
221
360
 
222
- return shape;
223
- }
224
- }
361
+ // Register the custom component
362
+ ExtensionController.register('node', 'custom-node', customNode);
225
363
 
226
- // Register custom node
227
- graph.registerNode('custom-node', CustomNode);
364
+ // Use in graph data
365
+ const nodeData = {
366
+ id: 'node1',
367
+ data: {
368
+ type: 'custom-node',
369
+ customProperty: 'special',
370
+ },
371
+ };
228
372
  ```
229
373
 
230
374
  #### Creating Custom Layouts
231
375
 
232
376
  ```typescript
233
- import { BaseLayout, LayoutConfig } from '@moxa/graph';
234
-
235
- class CustomLayout extends BaseLayout {
236
- layout() {
237
- // Implement custom layout algorithm
238
- this.nodes.forEach((node, index) => {
239
- // Position nodes in a circle
240
- const angle = (index / this.nodes.length) * Math.PI * 2;
241
- const radius = 200;
242
-
243
- node.updatePosition({
244
- x: Math.cos(angle) * radius + this.width / 2,
245
- y: Math.sin(angle) * radius + this.height / 2,
377
+ import { ExtensionController } from '@antv/g6';
378
+
379
+ // Custom Layout Implementation
380
+ const customLayout = (graph, options) => {
381
+ return {
382
+ id: 'custom-layout',
383
+ async execute() {
384
+ const { nodes } = graph.getData();
385
+ const { radius = 200 } = options;
386
+
387
+ nodes.forEach((node, index) => {
388
+ const angle = (index / nodes.length) * Math.PI * 2;
389
+ node.data.x = Math.cos(angle) * radius;
390
+ node.data.y = Math.sin(angle) * radius;
246
391
  });
247
- });
248
- }
249
- }
392
+
393
+ return { nodes, edges: graph.getData().edges };
394
+ },
395
+ };
396
+ };
397
+
398
+ // Register the layout
399
+ ExtensionController.register('layout', 'custom-layout', customLayout);
250
400
 
251
401
  // Apply custom layout
252
- graph.changeLayout('custom', {
253
- /* layout options */
402
+ graph.setLayout({
403
+ type: 'custom-layout',
404
+ options: { radius: 300 },
254
405
  });
255
406
  ```
256
407
 
257
408
  #### Creating Plugins
258
409
 
259
410
  ```typescript
260
- import { BasePlugin, PluginConfig } from '@moxa/graph';
411
+ import { BasePlugin } from '@antv/g6';
261
412
 
262
413
  class CustomPlugin extends BasePlugin {
414
+ constructor(options) {
415
+ super(options);
416
+ }
417
+
263
418
  init() {
264
419
  // Setup plugin
265
- this.graph.events.nodeClick.on(this.handleNodeClick);
420
+ this.graph.on('node:click', this.handleNodeClick);
266
421
  }
267
422
 
268
- handleNodeClick = (e) => {
423
+ handleNodeClick = (event) => {
269
424
  // Implement custom behavior
270
- console.log('Node clicked:', e.node.id);
425
+ console.log('Node clicked:', event.itemId);
271
426
  };
272
427
 
273
428
  destroy() {
274
429
  // Clean up
275
- this.graph.events.nodeClick.off(this.handleNodeClick);
430
+ this.graph.off('node:click', this.handleNodeClick);
431
+ super.destroy();
276
432
  }
277
433
  }
278
434
 
279
435
  // Add plugin to graph
280
- graph.addPlugin('custom', new CustomPlugin());
436
+ graph.setPlugin('custom-plugin', new CustomPlugin({
437
+ // plugin options
438
+ }));
439
+ ```
440
+
441
+ ## Development Workflow
442
+
443
+ This section describes the complete development workflow for contributing to the Moxa Graph library, including component implementation, Storybook documentation, and visual testing.
444
+
445
+ ### Directory Structure & Responsibilities
446
+
447
+ Each component follows a standardized directory structure:
448
+
449
+ ```
450
+ {component-name}/
451
+ ├── index.ts # Component export
452
+ ├── models/ # TypeScript type definitions
453
+ │ └── index.ts
454
+ ├── utils/ # Utility functions
455
+ │ └── index.ts
456
+ ├── stories/ # Storybook documentation
457
+ │ ├── {story-name}.stories.ts # Storybook configuration
458
+ │ ├── {story-name}.component.ts # Angular wrapper component
459
+ │ └── data/ # Test data definitions
460
+ │ └── {story-name}-data.ts
461
+ ├── tests/ # Visual regression tests
462
+ │ ├── {test-name}.spec.ts
463
+ │ └── __snapshots__/ # Visual test snapshots
464
+ │ └── {test-name}.png
465
+ └── transforms/ # Data transformation helpers (optional)
466
+ └── index.ts
467
+ ```
468
+
469
+ #### Directory Responsibilities
470
+
471
+ - **`index.ts`**: Main component export, registers the component with G6
472
+ - **`models/`**: TypeScript interfaces and type definitions for component configuration
473
+ - **`utils/`**: Helper functions for styling, positioning, state management
474
+ - **`stories/`**: Complete Storybook documentation with interactive examples
475
+ - **`data/`**: Reusable test data configurations for different scenarios
476
+ - **`tests/`**: Playwright visual regression tests ensuring UI consistency
477
+ - **`transforms/`**: Data transformation utilities (for complex components)
478
+
479
+ ### Development Process
480
+
481
+ #### 1. Component Implementation
482
+
483
+ Start by implementing the core component logic:
484
+
485
+ ```typescript
486
+ // components/my-component/index.ts
487
+ import { ExtensionController } from '@antv/g6';
488
+
489
+ const myComponent = (context) => {
490
+ const { model, theme } = context;
491
+ const { data } = model;
492
+
493
+ return {
494
+ // Component rendering logic
495
+ rect: {
496
+ width: data.width || 100,
497
+ height: data.height || 50,
498
+ fill: theme.primaryColor,
499
+ },
500
+ };
501
+ };
502
+
503
+ // Register component
504
+ ExtensionController.register('node', 'my-component', myComponent);
505
+
506
+ export { myComponent };
507
+ ```
508
+
509
+ #### 2. Type Definitions
510
+
511
+ Define TypeScript interfaces in `models/`:
512
+
513
+ ```typescript
514
+ // components/my-component/models/index.ts
515
+ export interface MyComponentConfig {
516
+ width?: number;
517
+ height?: number;
518
+ customProperty?: string;
519
+ }
520
+
521
+ export interface MyComponentData extends NodeData {
522
+ type: 'my-component';
523
+ config: MyComponentConfig;
524
+ }
525
+ ```
526
+
527
+ #### 3. Storybook Documentation
528
+
529
+ Create comprehensive Storybook stories:
530
+
531
+ ```typescript
532
+ // components/my-component/stories/my-component.stories.ts
533
+ import { Meta, StoryObj } from '@storybook/angular';
534
+ import { MY_COMPONENT_DATA } from './data/my-component-data';
535
+ import { MyComponentComponent } from './my-component.component';
536
+
537
+ export default {
538
+ title: 'Components/My Component',
539
+ component: MyComponentComponent,
540
+ parameters: {
541
+ docs: {
542
+ description: {
543
+ component: `
544
+ # My Component
545
+
546
+ This component demonstrates custom node rendering capabilities.
547
+
548
+ ## Features
549
+ - Configurable dimensions
550
+ - Theme integration
551
+ - State management
552
+
553
+ ## Usage
554
+ \`\`\`typescript
555
+ const graph = new Graph({
556
+ container: 'graph',
557
+ data: MY_COMPONENT_DATA,
558
+ });
559
+ \`\`\`
560
+ `,
561
+ },
562
+ },
563
+ },
564
+ args: {
565
+ graphData: MY_COMPONENT_DATA,
566
+ },
567
+ } as Meta;
568
+
569
+ type Story = StoryObj<MyComponentComponent>;
570
+
571
+ export const Basic: Story = {};
572
+ export const Advanced: Story = {
573
+ args: {
574
+ graphData: ADVANCED_MY_COMPONENT_DATA,
575
+ },
576
+ };
577
+ ```
578
+
579
+ #### 4. Angular Wrapper Component
580
+
581
+ Create an Angular component for Storybook:
582
+
583
+ ```typescript
584
+ // components/my-component/stories/my-component.component.ts
585
+ import { Component, input, computed } from '@angular/core';
586
+ import { Graph, GraphConfig, GraphData } from '@moxa/graph';
587
+ import { StoryWrapperComponent, JsonViewerComponent } from '@shared/utils/components';
588
+
589
+ @Component({
590
+ selector: 'moxa-vizion-my-component',
591
+ imports: [JsonViewerComponent, StoryWrapperComponent],
592
+ template: `
593
+ <story-wrapper>
594
+ <json-viewer [data]="data()">
595
+ <div container [id]="id"></div>
596
+ </json-viewer>
597
+ </story-wrapper>
598
+ `,
599
+ })
600
+ export class MyComponentComponent {
601
+ graph!: Graph;
602
+ id: string = Math.random().toString(36).substring(2);
603
+
604
+ graphData = input<GraphData>();
605
+
606
+ data = computed<GraphConfig>(() => ({
607
+ container: this.id,
608
+ data: this.graphData()!,
609
+ }));
610
+
611
+ ngAfterViewInit(): void {
612
+ this.graph = new Graph(this.data());
613
+ this.graph.render();
614
+ }
615
+
616
+ ngOnDestroy(): void {
617
+ this.graph.destroy();
618
+ }
619
+ }
281
620
  ```
282
621
 
622
+ #### 5. Test Data
623
+
624
+ Define comprehensive test data:
625
+
626
+ ```typescript
627
+ // components/my-component/stories/data/my-component-data.ts
628
+ import { GraphData } from '@moxa/graph';
629
+
630
+ export const MY_COMPONENT_DATA: GraphData = {
631
+ nodes: [
632
+ {
633
+ id: 'node1',
634
+ data: {
635
+ type: 'my-component',
636
+ x: 100,
637
+ y: 100,
638
+ width: 120,
639
+ height: 60,
640
+ },
641
+ },
642
+ // More test scenarios...
643
+ ],
644
+ };
645
+ ```
646
+
647
+ #### 6. Visual Testing
648
+
649
+ Implement Playwright visual regression tests:
650
+
651
+ ```typescript
652
+ // components/my-component/stories/tests/my-component-basic.spec.ts
653
+ import { test } from '@playwright/test';
654
+ import {
655
+ getGraphContainer,
656
+ getStorybookUrl,
657
+ verifySnapshot,
658
+ } from '@tests/helpers';
659
+
660
+ const STORY_ID = 'components-my-component--basic';
661
+ const SNAPSHOT_NAME = 'my-component-basic.png';
662
+
663
+ test.describe('My Component Visual Tests', () => {
664
+ test.beforeEach(async ({ page }) => {
665
+ await page.setViewportSize({ width: 600, height: 500 });
666
+ });
667
+
668
+ test('should display basic component correctly', async ({
669
+ page,
670
+ baseURL,
671
+ }) => {
672
+ await page.goto(getStorybookUrl(baseURL, STORY_ID));
673
+ await page.waitForSelector('div.container', { timeout: 10000 });
674
+
675
+ const graphContainer = getGraphContainer(page);
676
+ await page.waitForTimeout(1000); // Wait for graph rendering
677
+
678
+ await verifySnapshot(graphContainer, SNAPSHOT_NAME);
679
+ });
680
+ });
681
+ ```
682
+
683
+ ### Testing Strategy
684
+
685
+ #### Visual Regression Testing
686
+
687
+ The library uses Playwright for comprehensive visual regression testing:
688
+
689
+ 1. **Snapshot Generation**: First test run generates baseline snapshots
690
+ 2. **Automatic Comparison**: Subsequent runs compare against baselines
691
+ 3. **Pixel-Perfect Accuracy**: Detects even minor visual changes
692
+ 4. **Cross-Browser Support**: Ensures consistency across different browsers
693
+
694
+ #### Test Configuration
695
+
696
+ Playwright is configured for optimal visual testing:
697
+
698
+ ```typescript
699
+ // playwright.config.ts highlights
700
+ export default defineConfig({
701
+ testMatch: ['**/e2e/**/*.spec.ts', '**/src/**/tests/*.spec.ts'],
702
+ snapshotPathTemplate: `{testFileDir}/__snapshots__/{arg}{ext}`,
703
+ use: {
704
+ viewport: { width: 1280, height: 720 },
705
+ deviceScaleFactor: 1, // Fixed DPR for consistency
706
+ // Disable animations for consistent screenshots
707
+ launchOptions: {
708
+ args: ['--disable-gpu', '--disable-web-security'],
709
+ },
710
+ },
711
+ expect: {
712
+ toMatchSnapshot: {
713
+ maxDiffPixelRatio: 0.01,
714
+ threshold: 0.1,
715
+ },
716
+ },
717
+ });
718
+ ```
719
+
720
+ ### Development Commands
721
+
722
+ ```bash
723
+ # Start Storybook for development
724
+ pnpm storybook:graph
725
+
726
+ # Run all tests
727
+ pnpm test:graph
728
+
729
+ # Run visual regression tests
730
+ pnpm e2e:graph
731
+
732
+ # Update visual snapshots (when intentional changes are made)
733
+ pnpm e2e:graph --update-snapshots
734
+
735
+ # Build the library
736
+ pnpm build:graph
737
+
738
+ # Generate documentation
739
+ pnpm docs:graph
740
+ ```
741
+
742
+ ### Quality Assurance
743
+
744
+ #### Pre-commit Checklist
745
+
746
+ Before submitting code, ensure:
747
+
748
+ 1. ✅ **Component Registration**: Component is properly registered with G6
749
+ 2. ✅ **Type Safety**: All TypeScript interfaces are defined
750
+ 3. ✅ **Storybook Stories**: Complete documentation with examples
751
+ 4. ✅ **Test Data**: Comprehensive test scenarios
752
+ 5. ✅ **Visual Tests**: All visual regression tests pass
753
+ 6. ✅ **Documentation**: Component behavior is clearly documented
754
+ 7. ✅ **Performance**: Component renders efficiently for large datasets
755
+
756
+ #### Code Review Process
757
+
758
+ 1. **Functionality Review**: Verify component logic and API design
759
+ 2. **Documentation Review**: Ensure Storybook stories are comprehensive
760
+ 3. **Visual Review**: Check all visual test snapshots for accuracy
761
+ 4. **Performance Review**: Validate rendering performance
762
+ 5. **Integration Review**: Confirm component works with existing ecosystem
763
+
283
764
  ### Best Practices
284
765
 
285
766
  1. **Performance Considerations**
@@ -293,10 +774,17 @@ graph.addPlugin('custom', new CustomPlugin());
293
774
  - Support keyboard navigation for critical operations
294
775
 
295
776
  3. **Testing**
296
- - Write unit tests for custom components
297
- - Create visual regression tests for custom node renders
777
+ - Write comprehensive visual regression tests for all component states
778
+ - Create multiple test scenarios covering edge cases
779
+ - Maintain up-to-date snapshots when making intentional visual changes
298
780
  - Test across different browsers and device sizes
299
781
 
782
+ 4. **Documentation**
783
+ - Provide clear usage examples in Storybook
784
+ - Document all configuration options
785
+ - Include interactive demos showing component capabilities
786
+ - Explain integration patterns with other components
787
+
300
788
  ### Troubleshooting
301
789
 
302
790
  Common issues and their solutions: