From 8956d9853a0a5c4e4a5cf1bd134bae4a96d1e7d6 Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 21 Jun 2024 21:10:34 +0800 Subject: [PATCH] fix: fix issue about contextmenu (#5913) * fix: fix issue about contextmenu * test: update test case --- .../g6/__tests__/demos/plugin-contextmenu.ts | 3 ++ .../unit/plugins/contextmenu.spec.ts | 45 ++++++++++++++++--- packages/g6/src/plugins/contextmenu/index.ts | 32 ++++++------- 3 files changed, 58 insertions(+), 22 deletions(-) diff --git a/packages/g6/__tests__/demos/plugin-contextmenu.ts b/packages/g6/__tests__/demos/plugin-contextmenu.ts index bbc2032dfbe..11f108bd2da 100644 --- a/packages/g6/__tests__/demos/plugin-contextmenu.ts +++ b/packages/g6/__tests__/demos/plugin-contextmenu.ts @@ -7,10 +7,13 @@ export const pluginContextmenu: TestCase = async (context) => { autoResize: true, data, layout: { type: 'd3-force' }, + behaviors: ['drag-canvas'], plugins: [ { + key: 'contextmenu', type: 'contextmenu', trigger: 'contextmenu', + className: 'custom-class-name', getItems: () => { return [ { name: '展开一度关系', value: 'spread' }, diff --git a/packages/g6/__tests__/unit/plugins/contextmenu.spec.ts b/packages/g6/__tests__/unit/plugins/contextmenu.spec.ts index c104a33ca1a..fa025565b52 100644 --- a/packages/g6/__tests__/unit/plugins/contextmenu.spec.ts +++ b/packages/g6/__tests__/unit/plugins/contextmenu.spec.ts @@ -1,20 +1,53 @@ import { pluginContextmenu } from '@/__tests__/demos'; -import { createDemoGraph } from '@@/utils'; +import type { Contextmenu } from '@/src'; +import { NodeEvent } from '@/src'; +import { createDemoGraph, sleep } from '@@/utils'; describe('plugin contextmenu', () => { it('contextmenu', async () => { const graph = await createDemoGraph(pluginContextmenu); + const onClick = jest.fn(); + graph.updatePlugin({ key: 'contextmenu', onClick }); + const container = graph.getCanvas().getContainer()!; const el = container.querySelector('.g6-contextmenu') as HTMLDivElement; - expect(graph.getPlugins().length).toBe(1); - // @ts-expect-error ignore - expect(graph.getPlugins()[0].trigger).toBe('contextmenu'); + const $dom = container.querySelector('.g6-contextmenu'); + expect($dom).toBeTruthy(); + expect($dom?.classList.contains('custom-class-name')).toBeTruthy(); expect(el.querySelector('.g6-contextmenu-li')).toBeFalsy(); - await graph.destroy(); - expect(container.querySelector('.g6-contextmenu')).toBeFalsy(); + const emit = () => { + graph.emit(NodeEvent.CONTEXT_MENU, { + target: { id: '1' }, + targetType: 'node', + client: { + x: 100, + y: 100, + }, + }); + }; + + emit(); + + await sleep(100); + expect(el.querySelector('.g6-contextmenu-ul')).toBeTruthy(); + expect(el.querySelectorAll('.g6-contextmenu-li').length).toBe(2); + + const instance = graph.getPluginInstance('contextmenu'); + + // @ts-expect-error private method + instance.onMenuItemClick({ target: el.querySelector('.g6-contextmenu-li') }); + expect(onClick).toHaveBeenCalledTimes(1); + expect(container.querySelector('.g6-contextmenu')!.style.display).toBe('none'); + + emit(); + + await sleep(100); + expect(container.querySelector('.g6-contextmenu')!.style.display).toBe('block'); + document.body.click(); + expect(container.querySelector('.g6-contextmenu')!.style.display).toBe('none'); }); }); diff --git a/packages/g6/src/plugins/contextmenu/index.ts b/packages/g6/src/plugins/contextmenu/index.ts index eac48429c4e..8e3dee67168 100644 --- a/packages/g6/src/plugins/contextmenu/index.ts +++ b/packages/g6/src/plugins/contextmenu/index.ts @@ -13,9 +13,9 @@ import { CONTEXTMENU_CSS, getContentFromItems } from './util'; */ export interface ContextmenuOptions extends BasePluginOptions { /** - * 给菜单的 DOM 追加的 classname,便于自定义样式。默认是包含 `g6-contextmenu` + * 给菜单的 DOM 追加的类名 * - * The classname appended to the menu DOM for custom styles. The default is `g6-contextmenu` + * The class name appended to the menu DOM for custom styles * @defaultValue 'g6-contextmenu' */ className?: string; @@ -42,7 +42,7 @@ export interface ContextmenuOptions extends BasePluginOptions { * * The callback method triggered when the menu is clicked */ - onClick?: (v: string, target: HTMLElement) => void; + onClick?: (value: string, target: HTMLElement) => void; /** * 返回菜单的项目列表,支持 `Promise` 类型的返回值。是 `getContent` 的快捷配置 * @@ -88,18 +88,24 @@ export class Contextmenu extends BasePlugin { enable: () => true, }; - private $element: HTMLElement = createPluginContainer('contextmenu', false); + private $element!: HTMLElement; constructor(context: RuntimeContext, options: ContextmenuOptions) { super(context, Object.assign({}, Contextmenu.defaultOptions, options)); + this.initElement(); + this.update(options); + } + + private initElement() { + this.$element = createPluginContainer('contextmenu', false); + const { className } = this.options; + if (className) this.$element.classList.add(className); + const $container = this.context.canvas.getContainer(); $container!.appendChild(this.$element); - // 设置样式 insertDOM('g6-contextmenu-css', 'style', {}, CONTEXTMENU_CSS, document.head); - - this.update(options); } /** @@ -210,16 +216,10 @@ export class Contextmenu extends BasePlugin { const { onClick } = this.options; if (event.target instanceof HTMLElement) { if (event.target.className.includes('g6-contextmenu-li')) { - const v = event.target.getAttribute('value') as string; - onClick && onClick(v, event.target); - - this.hide(); - } - - // 点击其他地方,隐藏菜单 - if (!this.context.graph.getCanvas().getContainer()!.contains(event.target)) { - this.hide(); + const value = event.target.getAttribute('value') as string; + onClick?.(value, event.target); } } + this.hide(); }; }