mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(editor): Support pasting values that start with =
(#13699)
This commit is contained in:
parent
906770a06a
commit
9e83ff51da
|
@ -82,7 +82,25 @@ class Worker {
|
|||
terminate = vi.fn();
|
||||
}
|
||||
|
||||
class DataTransfer {
|
||||
private data: Record<string, unknown> = {};
|
||||
|
||||
setData = vi.fn((type: string, data) => {
|
||||
this.data[type] = data;
|
||||
});
|
||||
|
||||
getData = vi.fn((type) => {
|
||||
if (type.startsWith('text')) type = 'text';
|
||||
return this.data[type] ?? null;
|
||||
});
|
||||
}
|
||||
|
||||
Object.defineProperty(window, 'Worker', {
|
||||
writable: true,
|
||||
value: Worker,
|
||||
});
|
||||
|
||||
Object.defineProperty(window, 'DataTransfer', {
|
||||
writable: true,
|
||||
value: DataTransfer,
|
||||
});
|
||||
|
|
|
@ -164,6 +164,42 @@ describe('ParameterInput.vue', () => {
|
|||
expect(emitted('update')).toContainEqual([expect.objectContaining({ value: 'foo' })]);
|
||||
});
|
||||
|
||||
test('should correctly handle paste events', async () => {
|
||||
const { container, emitted } = renderComponent(ParameterInput, {
|
||||
pinia: createTestingPinia(),
|
||||
props: {
|
||||
path: 'tag',
|
||||
parameter: {
|
||||
displayName: 'Tag',
|
||||
name: 'tag',
|
||||
type: 'string',
|
||||
},
|
||||
modelValue: '',
|
||||
},
|
||||
});
|
||||
const input = container.querySelector('input') as HTMLInputElement;
|
||||
expect(input).toBeInTheDocument();
|
||||
await userEvent.click(input);
|
||||
|
||||
async function paste(text: string) {
|
||||
const expression = new DataTransfer();
|
||||
expression.setData('text', text);
|
||||
await userEvent.clear(input);
|
||||
await userEvent.paste(expression);
|
||||
}
|
||||
|
||||
await paste('foo');
|
||||
expect(emitted('update')).toContainEqual([expect.objectContaining({ value: 'foo' })]);
|
||||
|
||||
await paste('={{ $json.foo }}');
|
||||
expect(emitted('update')).toContainEqual([
|
||||
expect.objectContaining({ value: '={{ $json.foo }}' }),
|
||||
]);
|
||||
|
||||
await paste('=flDvzj%y1nP');
|
||||
expect(emitted('update')).toContainEqual([expect.objectContaining({ value: '==flDvzj%y1nP' })]);
|
||||
});
|
||||
|
||||
test('should not reset the value of a multi-select with loadOptionsMethod on load', async () => {
|
||||
mockNodeTypesState.getNodeParameterOptions = vi.fn(async () => [
|
||||
{ name: 'ID', value: 'id' },
|
||||
|
|
|
@ -741,6 +741,30 @@ function onBlur() {
|
|||
isFocused.value = false;
|
||||
}
|
||||
|
||||
function onPaste(event: ClipboardEvent) {
|
||||
const pastedText = event.clipboardData?.getData('text');
|
||||
const input = event.target;
|
||||
|
||||
if (!(input instanceof HTMLInputElement)) return;
|
||||
|
||||
const start = input.selectionStart ?? 0;
|
||||
|
||||
// When a value starting with `=` is pasted that does not contain expression syntax ({{}})
|
||||
// Add an extra `=` to go into expression mode and preserve the original pasted text
|
||||
if (pastedText && pastedText.startsWith('=') && !pastedText.match(/{{.*?}}/g) && start === 0) {
|
||||
event.preventDefault();
|
||||
|
||||
const end = input.selectionEnd ?? start;
|
||||
const text = input.value;
|
||||
const withExpressionPrefix = '=' + pastedText;
|
||||
|
||||
input.value = text.substring(0, start) + withExpressionPrefix + text.substring(end);
|
||||
input.selectionStart = input.selectionEnd = start + withExpressionPrefix.length;
|
||||
|
||||
valueChanged(input.value);
|
||||
}
|
||||
}
|
||||
|
||||
function onResourceLocatorDrop(data: string) {
|
||||
emit('drop', data);
|
||||
}
|
||||
|
@ -951,7 +975,9 @@ async function optionSelected(command: string) {
|
|||
|
||||
if (props.parameter.type === 'string') {
|
||||
// Strip the '=' from the beginning
|
||||
newValue = modelValueString.value ? modelValueString.value.toString().substring(1) : null;
|
||||
newValue = modelValueString.value
|
||||
? modelValueString.value.toString().replace(/^=+/, '')
|
||||
: null;
|
||||
} else if (newValue === null) {
|
||||
// Invalid expressions land here
|
||||
if (['number', 'boolean'].includes(props.parameter.type)) {
|
||||
|
@ -1460,6 +1486,7 @@ onUpdated(async () => {
|
|||
@keydown.stop
|
||||
@focus="setFocus"
|
||||
@blur="onBlur"
|
||||
@paste="onPaste"
|
||||
>
|
||||
<template #suffix>
|
||||
<N8nIcon
|
||||
|
|
Loading…
Reference in a new issue