fix(editor): Remove inconsistent scrim on node creator open (#27086)

This commit is contained in:
Charlie Kolb
2026-03-17 11:03:41 +00:00
committed by GitHub
parent 608c6497ae
commit bc76e620fb
10 changed files with 27 additions and 62 deletions
@@ -688,7 +688,6 @@ export type NodeCreatorOpenSource =
export interface INodeCreatorState {
itemsFilter: string;
showScrim: boolean;
rootViewHistory: NodeFilterType[];
selectedView: NodeFilterType;
openSource: NodeCreatorOpenSource;
@@ -3910,7 +3910,6 @@ describe('useCanvasOperations', () => {
const resetStateSpy = vi.spyOn(workflowState, 'resetState');
nodeCreatorStore.setNodeCreatorState = vi.fn();
nodeCreatorStore.setShowScrim = vi.fn();
workflowsStore.removeTestWebhook = vi.fn();
workflowsStore.resetWorkflow = vi.fn();
workflowsStore.resetState = vi.fn();
@@ -3947,7 +3946,6 @@ describe('useCanvasOperations', () => {
expect(nodeCreatorStore.setNodeCreatorState).toHaveBeenCalledWith({
createNodeActive: false,
});
expect(nodeCreatorStore.setShowScrim).toHaveBeenCalledWith(false);
expect(workflowsStore.removeTestWebhook).toHaveBeenCalledWith('workflow-id');
expect(workflowsStore.resetWorkflow).toHaveBeenCalled();
expect(resetStateSpy).toHaveBeenCalled();
@@ -3966,7 +3964,6 @@ describe('useCanvasOperations', () => {
const workflowsStore = mockedStore(useWorkflowsStore);
nodeCreatorStore.setNodeCreatorState = vi.fn();
nodeCreatorStore.setShowScrim = vi.fn();
workflowsStore.removeTestWebhook = vi.fn();
workflowsStore.executionWaitingForWebhook = false;
@@ -2273,7 +2273,6 @@ export function useCanvasOperations() {
function resetWorkspace() {
// Reset node creator
nodeCreatorStore.setNodeCreatorState({ createNodeActive: false });
nodeCreatorStore.setShowScrim(false);
// Make sure that if there is a waiting test-webhook, it gets removed
if (workflowsStore.executionWaitingForWebhook) {
@@ -1,7 +1,6 @@
<script setup lang="ts">
import { computed } from 'vue';
import type { XYPosition } from '@/Interface';
import { useNodeCreatorStore } from '@/features/shared/nodeCreator/nodeCreator.store';
import { useI18n } from '@n8n/i18n';
import { N8nIcon, N8nTooltip } from '@n8n/design-system';
@@ -14,7 +13,6 @@ const i18n = useI18n();
const props = defineProps<Props>();
const nodeCreatorStore = useNodeCreatorStore();
const containerCssVars = computed(() => ({
'--trigger-placeholder--margin-left': `${props.position[0]}px`,
'--trigger-placeholder--margin-top': `${props.position[1]}px`,
@@ -28,12 +26,7 @@ const containerCssVars = computed(() => ({
:style="containerCssVars"
data-test-id="canvas-add-button"
>
<N8nTooltip
placement="top"
:visible="showTooltip"
:disabled="nodeCreatorStore.showScrim"
:show-after="700"
>
<N8nTooltip placement="top" :visible="showTooltip" :show-after="700">
<button :class="$style.button" data-test-id="canvas-plus-button" @click="$emit('click')">
<N8nIcon icon="plus" size="large" />
</button>
@@ -964,7 +964,7 @@ function onClickReplaceNode(nodeId: string) {
if (!nodeType) return;
nodeCreatorReplaceTargetId.value = nodeId;
nodeCreatorStore.oppeningContext = 'replacement';
nodeCreatorStore.openingContext = 'replacement';
if (isTriggerNode(nodeType)) {
nodeCreatorStore.openNodeCreatorForTriggerNodes(NODE_CREATOR_OPEN_SOURCES.REPLACE_NODE_ACTION);
} else {
@@ -40,7 +40,7 @@ const uiStore = useUIStore();
const bannersStore = useBannersStore();
const chatPanelStore = useChatPanelStore();
const { setShowScrim, setActions, setMergeNodes } = useNodeCreatorStore();
const { setActions, setMergeNodes } = useNodeCreatorStore();
const { generateMergedNodesAndActions } = useActionsGenerator();
const state = reactive({
@@ -48,8 +48,6 @@ const state = reactive({
mousedownInsideEvent: null as MouseEvent | null,
});
const showScrim = computed(() => useNodeCreatorStore().showScrim);
const viewStacksLength = computed(() => useViewStacks().viewStacks.length);
const nodeCreatorInlineStyle = computed(() => {
@@ -117,7 +115,6 @@ watch(
() => props.active,
(isActive) => {
if (!isActive) {
setShowScrim(false);
resetViewStacks();
}
},
@@ -127,7 +124,6 @@ watch(
watch(viewStacksLength, (value) => {
if (value === 0) {
emit('closeNodeCreator');
setShowScrim(false);
}
});
@@ -166,12 +162,7 @@ onClickOutside(
<template>
<div>
<aside
:class="{
[$style.nodeCreatorScrim]: true,
[$style.active]: showScrim,
}"
/>
<aside :class="$style.nodeCreatorScrim" />
<N8nIconButton
variant="subtle"
v-if="active"
@@ -273,8 +273,8 @@ describe('NodesListPanel', () => {
});
});
describe('oppeningContext', () => {
it('should have null oppeningContext by default', () => {
describe('openingContext', () => {
it('should have null openingContext by default', () => {
getWrapperComponent(() => {
const nodeCreatorStore = useNodeCreatorStore();
const { setMergeNodes } = nodeCreatorStore;
@@ -287,12 +287,12 @@ describe('NodesListPanel', () => {
}),
]);
expect(nodeCreatorStore.oppeningContext).toBeNull();
expect(nodeCreatorStore.openingContext).toBeNull();
return {};
});
});
it('should maintain oppeningContext when set to "replacement"', async () => {
it('should maintain openingContext when set to "replacement"', async () => {
getWrapperComponent(() => {
const nodeCreatorStore = useNodeCreatorStore();
const { setMergeNodes } = nodeCreatorStore;
@@ -306,15 +306,15 @@ describe('NodesListPanel', () => {
]);
// Simulate setting context when replacing a node
nodeCreatorStore.oppeningContext = 'replacement';
expect(nodeCreatorStore.oppeningContext).toBe('replacement');
nodeCreatorStore.openingContext = 'replacement';
expect(nodeCreatorStore.openingContext).toBe('replacement');
return {};
});
await nextTick();
});
it('should allow resetting oppeningContext back to null', async () => {
it('should allow resetting openingContext back to null', async () => {
getWrapperComponent(() => {
const nodeCreatorStore = useNodeCreatorStore();
const { setMergeNodes } = nodeCreatorStore;
@@ -328,19 +328,19 @@ describe('NodesListPanel', () => {
]);
// Set context
nodeCreatorStore.oppeningContext = 'replacement';
expect(nodeCreatorStore.oppeningContext).toBe('replacement');
nodeCreatorStore.openingContext = 'replacement';
expect(nodeCreatorStore.openingContext).toBe('replacement');
// Reset context (simulating node creator close)
nodeCreatorStore.oppeningContext = null;
expect(nodeCreatorStore.oppeningContext).toBeNull();
nodeCreatorStore.openingContext = null;
expect(nodeCreatorStore.openingContext).toBeNull();
return {};
});
await nextTick();
});
it('should preserve oppeningContext during node search when in replacement mode', async () => {
it('should preserve openingContext during node search when in replacement mode', async () => {
const mockedNodes = [...Array(10).keys()].map(
(n) =>
mockSimplifiedNodeType({
@@ -370,7 +370,7 @@ describe('NodesListPanel', () => {
const { setMergeNodes, setSelectedView } = nodeCreatorStore;
// Set replacement context
nodeCreatorStore.oppeningContext = 'replacement';
nodeCreatorStore.openingContext = 'replacement';
watch(
() => props.nodeTypes,
@@ -405,7 +405,7 @@ describe('NodesListPanel', () => {
await nextTick();
const nodeCreatorStore = useNodeCreatorStore();
expect(nodeCreatorStore.oppeningContext).toBe('replacement');
expect(nodeCreatorStore.openingContext).toBe('replacement');
// Perform search
await fireEvent.input(screen.getByTestId('node-creator-search-bar'), {
@@ -414,7 +414,7 @@ describe('NodesListPanel', () => {
await nextTick();
// Context should still be 'replacement' after search
expect(nodeCreatorStore.oppeningContext).toBe('replacement');
expect(nodeCreatorStore.openingContext).toBe('replacement');
});
});
});
@@ -83,7 +83,7 @@ const isCommunityNodeActionsMode = computed(() => {
});
const viewStackTitle = computed(() => {
if (nodeCreatorStore.oppeningContext === 'replacement') {
if (nodeCreatorStore.openingContext === 'replacement') {
return i18n.baseText('nodeCreator.replaceNode.title');
}
return activeViewStack.value.title;
@@ -122,12 +122,12 @@ function onSearch(value: string) {
}
function onTransitionEnd() {
cleanupOppeningContext();
cleanupopeningContext();
void setActiveItemIndex(getDefaultActiveIndex());
}
function cleanupOppeningContext() {
nodeCreatorStore.oppeningContext = null;
function cleanupopeningContext() {
nodeCreatorStore.openingContext = null;
}
onMounted(() => {
@@ -136,7 +136,7 @@ onMounted(() => {
});
onUnmounted(() => {
cleanupOppeningContext();
cleanupopeningContext();
detachKeydownEvent();
});
@@ -64,12 +64,11 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, () => {
const mergedNodes = ref<SimplifiedNodeType[]>([]);
const actions = ref<ActionsRecord<typeof mergedNodes.value>>({});
const showScrim = ref(false);
const openSource = ref<NodeCreatorOpenSource>('');
const isCreateNodeActive = ref<boolean>(false);
const oppeningContext = ref<null | 'replacement'>(null);
const openingContext = ref<null | 'replacement'>(null);
const nodePanelSessionId = ref<string>('');
@@ -89,10 +88,6 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, () => {
actions.value = nodes;
}
function setShowScrim(isVisible: boolean) {
showScrim.value = isVisible;
}
function setSelectedView(view: NodeFilterType) {
selectedView.value = view;
}
@@ -266,7 +261,6 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, () => {
function openNodeCreatorForTriggerNodes(source: NodeCreatorOpenSource) {
ndvStore.unsetActiveNodeName();
setSelectedView(TRIGGER_NODE_CREATOR_VIEW);
setShowScrim(true);
setNodeCreatorState({
source,
createNodeActive: true,
@@ -277,7 +271,6 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, () => {
function openNodeCreatorForRegularNodes(source: NodeCreatorOpenSource) {
ndvStore.unsetActiveNodeName();
setSelectedView(REGULAR_NODE_CREATOR_VIEW);
setShowScrim(true);
setNodeCreatorState({
source,
createNodeActive: true,
@@ -465,14 +458,12 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, () => {
return {
isCreateNodeActive,
oppeningContext,
openingContext,
openSource,
selectedView,
showScrim,
mergedNodes,
actions,
allNodeCreatorNodes,
setShowScrim,
setSelectedView,
setOpenSource,
setActions,
@@ -60,12 +60,7 @@ async function onClickTemplatesLink() {
</script>
<template>
<div ref="container" :class="$style.addNodes" data-test-id="canvas-add-button">
<N8nTooltip
placement="top"
:visible="isTooltipVisible"
:disabled="nodeCreatorStore.showScrim"
:show-after="700"
>
<N8nTooltip placement="top" :visible="isTooltipVisible" :show-after="700">
<button :class="$style.button" data-test-id="canvas-plus-button" @click.stop="onClick">
<N8nIcon icon="plus" color="foreground-xdark" :size="40" />
</button>