mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-13 16:14:07 -08:00
feat(editor): Add executing state and disable node run button while executing on new canvas (no-changelog) (#11079)
This commit is contained in:
parent
9ed8040ec0
commit
6d716494a4
|
@ -1,11 +1,13 @@
|
|||
import { CanvasNodeHandleKey, CanvasNodeKey } from '@/constants';
|
||||
import { CanvasKey, CanvasNodeHandleKey, CanvasNodeKey } from '@/constants';
|
||||
import { computed, ref } from 'vue';
|
||||
import type {
|
||||
CanvasInjectionData,
|
||||
CanvasNode,
|
||||
CanvasNodeData,
|
||||
CanvasNodeEventBusEvents,
|
||||
CanvasNodeHandleInjectionData,
|
||||
CanvasNodeInjectionData,
|
||||
ConnectStartEvent,
|
||||
ExecutionOutputMapData,
|
||||
} from '@/types';
|
||||
import { CanvasConnectionMode, CanvasNodeRenderType } from '@/types';
|
||||
|
@ -88,6 +90,21 @@ export function createCanvasNodeProps({
|
|||
};
|
||||
}
|
||||
|
||||
export function createCanvasProvide({
|
||||
isExecuting = false,
|
||||
connectingHandle = undefined,
|
||||
}: {
|
||||
isExecuting?: boolean;
|
||||
connectingHandle?: ConnectStartEvent;
|
||||
} = {}) {
|
||||
return {
|
||||
[String(CanvasKey)]: {
|
||||
isExecuting: ref(isExecuting),
|
||||
connectingHandle: ref(connectingHandle),
|
||||
} satisfies CanvasInjectionData,
|
||||
};
|
||||
}
|
||||
|
||||
export function createCanvasNodeProvide({
|
||||
id = 'node',
|
||||
label = 'Test Node',
|
||||
|
|
|
@ -18,7 +18,7 @@ import { Background } from '@vue-flow/background';
|
|||
import { MiniMap } from '@vue-flow/minimap';
|
||||
import Node from './elements/nodes/CanvasNode.vue';
|
||||
import Edge from './elements/edges/CanvasEdge.vue';
|
||||
import { computed, onMounted, onUnmounted, provide, ref, useCssModule, watch } from 'vue';
|
||||
import { computed, onMounted, onUnmounted, provide, ref, toRef, useCssModule, watch } from 'vue';
|
||||
import type { EventBus } from 'n8n-design-system';
|
||||
import { createEventBus } from 'n8n-design-system';
|
||||
import { useContextMenu, type ContextMenuAction } from '@/composables/useContextMenu';
|
||||
|
@ -78,6 +78,7 @@ const props = withDefaults(
|
|||
controlsPosition?: PanelPosition;
|
||||
eventBus?: EventBus<CanvasEventBusEvents>;
|
||||
readOnly?: boolean;
|
||||
executing?: boolean;
|
||||
keyBindings?: boolean;
|
||||
}>(),
|
||||
{
|
||||
|
@ -87,6 +88,7 @@ const props = withDefaults(
|
|||
controlsPosition: PanelPosition.BottomLeft,
|
||||
eventBus: () => createEventBus(),
|
||||
readOnly: false,
|
||||
executing: false,
|
||||
keyBindings: true,
|
||||
},
|
||||
);
|
||||
|
@ -480,8 +482,11 @@ watch(() => props.readOnly, setReadonly, {
|
|||
* Provide
|
||||
*/
|
||||
|
||||
const isExecuting = toRef(props, 'executing');
|
||||
|
||||
provide(CanvasKey, {
|
||||
connectingHandle,
|
||||
isExecuting,
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ const props = withDefaults(
|
|||
fallbackNodes?: IWorkflowDb['nodes'];
|
||||
eventBus?: EventBus<CanvasEventBusEvents>;
|
||||
readOnly?: boolean;
|
||||
executing?: boolean;
|
||||
}>(),
|
||||
{
|
||||
id: 'canvas',
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { fireEvent } from '@testing-library/vue';
|
||||
import CanvasNodeToolbar from '@/components/canvas/elements/nodes/CanvasNodeToolbar.vue';
|
||||
import { createComponentRenderer } from '@/__tests__/render';
|
||||
import { createCanvasNodeProvide } from '@/__tests__/data';
|
||||
import { createCanvasNodeProvide, createCanvasProvide } from '@/__tests__/data';
|
||||
import { CanvasNodeRenderType } from '@/types';
|
||||
|
||||
const renderComponent = createComponentRenderer(CanvasNodeToolbar);
|
||||
|
@ -12,6 +12,7 @@ describe('CanvasNodeToolbar', () => {
|
|||
global: {
|
||||
provide: {
|
||||
...createCanvasNodeProvide(),
|
||||
...createCanvasProvide(),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -19,6 +20,21 @@ describe('CanvasNodeToolbar', () => {
|
|||
expect(getByTestId('execute-node-button')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render disabled execute node button when canvas is executing', () => {
|
||||
const { getByTestId } = renderComponent({
|
||||
global: {
|
||||
provide: {
|
||||
...createCanvasNodeProvide(),
|
||||
...createCanvasProvide({
|
||||
isExecuting: true,
|
||||
}),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(getByTestId('execute-node-button')).toBeDisabled();
|
||||
});
|
||||
|
||||
it('should not render execute node button when renderType is configuration', async () => {
|
||||
const { queryByTestId } = renderComponent({
|
||||
global: {
|
||||
|
@ -31,6 +47,7 @@ describe('CanvasNodeToolbar', () => {
|
|||
},
|
||||
},
|
||||
}),
|
||||
...createCanvasProvide(),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -43,6 +60,7 @@ describe('CanvasNodeToolbar', () => {
|
|||
global: {
|
||||
provide: {
|
||||
...createCanvasNodeProvide(),
|
||||
...createCanvasProvide(),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -57,6 +75,7 @@ describe('CanvasNodeToolbar', () => {
|
|||
global: {
|
||||
provide: {
|
||||
...createCanvasNodeProvide(),
|
||||
...createCanvasProvide(),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -71,6 +90,7 @@ describe('CanvasNodeToolbar', () => {
|
|||
global: {
|
||||
provide: {
|
||||
...createCanvasNodeProvide(),
|
||||
...createCanvasProvide(),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -85,6 +105,7 @@ describe('CanvasNodeToolbar', () => {
|
|||
global: {
|
||||
provide: {
|
||||
...createCanvasNodeProvide(),
|
||||
...createCanvasProvide(),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -106,6 +127,7 @@ describe('CanvasNodeToolbar', () => {
|
|||
},
|
||||
},
|
||||
}),
|
||||
...createCanvasProvide(),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -3,6 +3,7 @@ import { computed, useCssModule } from 'vue';
|
|||
import { useI18n } from '@/composables/useI18n';
|
||||
import { useCanvasNode } from '@/composables/useCanvasNode';
|
||||
import { CanvasNodeRenderType } from '@/types';
|
||||
import { useCanvas } from '@/composables/useCanvas';
|
||||
|
||||
const emit = defineEmits<{
|
||||
delete: [];
|
||||
|
@ -19,13 +20,12 @@ const props = defineProps<{
|
|||
const $style = useCssModule();
|
||||
const i18n = useI18n();
|
||||
|
||||
const { render } = useCanvasNode();
|
||||
const { isExecuting } = useCanvas();
|
||||
const { isDisabled, render } = useCanvasNode();
|
||||
|
||||
// @TODO
|
||||
const workflowRunning = false;
|
||||
|
||||
// @TODO
|
||||
const nodeDisabledTitle = 'Test';
|
||||
const nodeDisabledTitle = computed(() => {
|
||||
return isDisabled.value ? i18n.baseText('node.disable') : i18n.baseText('node.enable');
|
||||
});
|
||||
|
||||
const classes = computed(() => ({
|
||||
[$style.canvasNodeToolbar]: true,
|
||||
|
@ -84,7 +84,7 @@ function onOpenContextMenu(event: MouseEvent) {
|
|||
text
|
||||
size="small"
|
||||
icon="play"
|
||||
:disabled="workflowRunning"
|
||||
:disabled="isExecuting"
|
||||
:title="i18n.baseText('node.testStep')"
|
||||
@click="executeNode"
|
||||
/>
|
||||
|
|
|
@ -5,8 +5,10 @@ export function useCanvas() {
|
|||
const canvas = inject(CanvasKey);
|
||||
|
||||
const connectingHandle = computed(() => canvas?.connectingHandle.value);
|
||||
const isExecuting = computed(() => canvas?.isExecuting.value);
|
||||
|
||||
return {
|
||||
isExecuting,
|
||||
connectingHandle,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -132,6 +132,7 @@ export type CanvasConnectionCreateData = {
|
|||
};
|
||||
|
||||
export interface CanvasInjectionData {
|
||||
isExecuting: Ref<boolean | undefined>;
|
||||
connectingHandle: Ref<ConnectStartEvent | undefined>;
|
||||
}
|
||||
|
||||
|
|
|
@ -1568,6 +1568,7 @@ onBeforeUnmount(() => {
|
|||
:fallback-nodes="fallbackNodes"
|
||||
:event-bus="canvasEventBus"
|
||||
:read-only="isCanvasReadOnly"
|
||||
:executing="isWorkflowRunning"
|
||||
:key-bindings="keyBindingsEnabled"
|
||||
@update:nodes:position="onUpdateNodesPosition"
|
||||
@update:node:position="onUpdateNodePosition"
|
||||
|
|
Loading…
Reference in a new issue