mirror of
https://github.com/n8n-io/n8n.git
synced 2026-09-19 09:51:59 +08:00
fix(editor): Open sticky note color picker from context menu (#31917)
This commit is contained in:
+15
-1
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { useContextMenu } from '../composables/useContextMenu';
|
||||
import { type ContextMenuAction } from '../composables/useContextMenuItems';
|
||||
import { isFocusHandoffAction, type ContextMenuAction } from '../composables/useContextMenuItems';
|
||||
import { useStyles } from '@/app/composables/useStyles';
|
||||
import { nextTick, ref, watch } from 'vue';
|
||||
import {
|
||||
@@ -41,10 +41,23 @@ watch(
|
||||
{ flush: 'post' },
|
||||
);
|
||||
|
||||
// When a selected action hands off to another floating layer (e.g. the sticky
|
||||
// color popover), we must stop Reka from restoring focus as the menu closes —
|
||||
// otherwise the restore lands outside the freshly-opened layer and dismisses it.
|
||||
let suppressRestoreFocus = false;
|
||||
|
||||
function onActionSelect(item: ContextMenuAction) {
|
||||
suppressRestoreFocus = isFocusHandoffAction(item);
|
||||
emit('action', item, contextMenu.targetNodeIds.value);
|
||||
}
|
||||
|
||||
function onCloseAutoFocus(event: Event) {
|
||||
if (suppressRestoreFocus) {
|
||||
event.preventDefault();
|
||||
}
|
||||
suppressRestoreFocus = false;
|
||||
}
|
||||
|
||||
function onOpenChange(open: boolean) {
|
||||
if (!open) {
|
||||
contextMenu.close();
|
||||
@@ -71,6 +84,7 @@ function onOpenChange(open: boolean) {
|
||||
:class="$style.content"
|
||||
data-test-id="context-menu"
|
||||
:style="{ zIndex: APP_Z_INDEXES.CONTEXT_MENU }"
|
||||
@close-auto-focus="onCloseAutoFocus"
|
||||
>
|
||||
<template v-for="item in actions" :key="item.id">
|
||||
<ContextMenuSeparator v-if="item.divided" :class="$style.separator" />
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { isFocusHandoffAction } from './useContextMenuItems';
|
||||
|
||||
describe('isFocusHandoffAction', () => {
|
||||
it('returns true for actions that open another focus-taking layer', () => {
|
||||
// `change_color` opens the sticky color popover; the context menu must not
|
||||
// restore focus on close or it would dismiss the just-opened popover.
|
||||
expect(isFocusHandoffAction('change_color')).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false for regular actions that do not hand off focus', () => {
|
||||
expect(isFocusHandoffAction('delete')).toBe(false);
|
||||
expect(isFocusHandoffAction('duplicate')).toBe(false);
|
||||
expect(isFocusHandoffAction('rename')).toBe(false);
|
||||
expect(isFocusHandoffAction('open')).toBe(false);
|
||||
});
|
||||
});
|
||||
+12
@@ -41,6 +41,18 @@ export type ContextMenuAction =
|
||||
| 'extract_sub_workflow'
|
||||
| 'focus_ai_on_selected';
|
||||
|
||||
/**
|
||||
* Actions that, once selected, hand off to another floating layer (e.g. a
|
||||
* popover) which then takes focus. For these the context menu must not restore
|
||||
* focus on close — otherwise the restore lands outside the freshly-opened layer
|
||||
* and immediately dismisses it.
|
||||
*/
|
||||
const FOCUS_HANDOFF_ACTIONS = new Set<ContextMenuAction>(['change_color']);
|
||||
|
||||
export function isFocusHandoffAction(action: ContextMenuAction): boolean {
|
||||
return FOCUS_HANDOFF_ACTIONS.has(action);
|
||||
}
|
||||
|
||||
type Item = ActionDropdownItem<ContextMenuAction>;
|
||||
|
||||
export function useContextMenuItems(targetNodeIds: ComputedRef<string[]>): ComputedRef<Item[]> {
|
||||
|
||||
@@ -43,4 +43,16 @@ export class StickyComponent extends BasePage {
|
||||
getDefaultStickyGuideLink(): Locator {
|
||||
return this.getStickies().first().getByRole('link', { name: 'Guide' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the color picker for a sticky via its right-click context menu.
|
||||
*/
|
||||
async openColorPickerFromContextMenu(sticky: Locator): Promise<void> {
|
||||
await sticky.click({ button: 'right' });
|
||||
await this.page.getByTestId('context-menu-item-change_color').click();
|
||||
}
|
||||
|
||||
getColorOptions(): Locator {
|
||||
return this.page.getByTestId('color');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,5 +31,17 @@ test.describe(
|
||||
const guideLink = n8n.canvas.sticky.getDefaultStickyGuideLink();
|
||||
await expect(guideLink).toHaveAttribute('href');
|
||||
});
|
||||
|
||||
test('opens the color picker from the right-click context menu', async ({ n8n }) => {
|
||||
await n8n.start.fromBlankCanvas();
|
||||
await n8n.canvas.sticky.addSticky();
|
||||
|
||||
const sticky = n8n.canvas.sticky.getStickies().first();
|
||||
await n8n.canvas.sticky.openColorPickerFromContextMenu(sticky);
|
||||
|
||||
// The popover is opened as the context menu closes; it must survive the
|
||||
// menu's focus restoration rather than being dismissed immediately.
|
||||
await expect(n8n.canvas.sticky.getColorOptions().first()).toBeVisible();
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user