fix(editor): Focus executions iframe when n8n is ready to delegate keyboard events (#12741)

This commit is contained in:
Alex Grozav 2025-01-28 17:22:41 +02:00 committed by GitHub
parent e85fe4abec
commit d5062189db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 35 additions and 4 deletions

View file

@ -12,6 +12,7 @@ const renderComponent = createComponentRenderer(WorkflowPreview);
let pinia: ReturnType<typeof createPinia>; let pinia: ReturnType<typeof createPinia>;
let executionsStore: ReturnType<typeof useExecutionsStore>; let executionsStore: ReturnType<typeof useExecutionsStore>;
let postMessageSpy: Mock; let postMessageSpy: Mock;
let focusSpy: Mock;
let consoleErrorSpy: MockInstance; let consoleErrorSpy: MockInstance;
const sendPostMessageCommand = (command: string) => { const sendPostMessageCommand = (command: string) => {
@ -26,10 +27,12 @@ describe('WorkflowPreview', () => {
consoleErrorSpy = vi.spyOn(console, 'error'); consoleErrorSpy = vi.spyOn(console, 'error');
postMessageSpy = vi.fn(); postMessageSpy = vi.fn();
focusSpy = vi.fn();
Object.defineProperty(HTMLIFrameElement.prototype, 'contentWindow', { Object.defineProperty(HTMLIFrameElement.prototype, 'contentWindow', {
writable: true, writable: true,
value: { value: {
postMessage: postMessageSpy, postMessage: postMessageSpy,
focus: focusSpy,
}, },
}); });
}); });
@ -105,6 +108,7 @@ describe('WorkflowPreview', () => {
}), }),
'*', '*',
); );
expect(focusSpy).toHaveBeenCalled();
}); });
}); });

View file

@ -15,6 +15,7 @@ const props = withDefaults(
loaderType?: 'image' | 'spinner'; loaderType?: 'image' | 'spinner';
canOpenNDV?: boolean; canOpenNDV?: boolean;
hideNodeIssues?: boolean; hideNodeIssues?: boolean;
focusOnLoad?: boolean;
}>(), }>(),
{ {
loading: false, loading: false,
@ -25,6 +26,7 @@ const props = withDefaults(
loaderType: 'image', loaderType: 'image',
canOpenNDV: true, canOpenNDV: true,
hideNodeIssues: false, hideNodeIssues: false,
focusOnLoad: true,
}, },
); );
@ -120,6 +122,7 @@ const onMouseEnter = () => {
scrollX.value = window.scrollX; scrollX.value = window.scrollX;
scrollY.value = window.scrollY; scrollY.value = window.scrollY;
}; };
const onMouseLeave = () => { const onMouseLeave = () => {
insideIframe.value = false; insideIframe.value = false;
}; };
@ -131,18 +134,41 @@ const receiveMessage = ({ data }: MessageEvent) => {
try { try {
const json = JSON.parse(data); const json = JSON.parse(data);
if (json.command === 'n8nReady') { if (json.command === 'n8nReady') {
ready.value = true; onReady();
} else if (json.command === 'openNDV') { } else if (json.command === 'openNDV') {
nodeViewDetailsOpened.value = true; onOpenNDV();
} else if (json.command === 'closeNDV') { } else if (json.command === 'closeNDV') {
nodeViewDetailsOpened.value = false; onCloseNDV();
} else if (json.command === 'error') { } else if (json.command === 'error') {
emit('close'); onError();
} }
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
}; };
const onReady = () => {
ready.value = true;
if (props.focusOnLoad) {
setTimeout(() => {
iframeRef.value?.contentWindow?.focus();
});
}
};
const onOpenNDV = () => {
nodeViewDetailsOpened.value = true;
};
const onCloseNDV = () => {
nodeViewDetailsOpened.value = false;
};
const onError = () => {
emit('close');
};
const onDocumentScroll = () => { const onDocumentScroll = () => {
if (insideIframe.value) { if (insideIframe.value) {
window.scrollTo(scrollX.value, scrollY.value); window.scrollTo(scrollX.value, scrollY.value);

View file

@ -266,6 +266,7 @@ function onRetryButtonBlur(event: FocusEvent) {
</div> </div>
</div> </div>
<WorkflowPreview <WorkflowPreview
:key="executionId"
mode="execution" mode="execution"
loader-type="spinner" loader-type="spinner"
:execution-id="executionId" :execution-id="executionId"