mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 04:34:06 -08:00
feat(editor): Replace root events with event bus events (no-changelog) (#6454)
* feat: replace root events with event bus events * fix: prevent cypress from replacing global with globalThis in import path * feat: remove emitter mixin * fix: replace component events with event bus * fix: fix linting issue * fix: fix breaking expression switch * chore: prettify ndv e2e suite code
This commit is contained in:
parent
18f588444f
commit
0154a97773
|
@ -50,7 +50,7 @@ describe('NDV', () => {
|
|||
workflowPage.getters.canvasNodes().last().dblclick();
|
||||
ndv.getters.inputSelect().click();
|
||||
ndv.getters.inputOption().last().click();
|
||||
ndv.getters.inputDataContainer().find('[class*=schema_]').should('exist')
|
||||
ndv.getters.inputDataContainer().find('[class*=schema_]').should('exist');
|
||||
ndv.getters.inputDataContainer().should('contain', 'start');
|
||||
});
|
||||
|
||||
|
@ -96,10 +96,20 @@ describe('NDV', () => {
|
|||
ndv.getters.container().should('be.visible');
|
||||
workflowPage.actions.saveWorkflowUsingKeyboardShortcut();
|
||||
workflowPage.getters.isWorkflowSaved();
|
||||
})
|
||||
});
|
||||
|
||||
describe('test output schema view', () => {
|
||||
const schemaKeys = ['id', 'name', 'email', 'notes', 'country', 'created', 'objectValue', 'prop1', 'prop2'];
|
||||
const schemaKeys = [
|
||||
'id',
|
||||
'name',
|
||||
'email',
|
||||
'notes',
|
||||
'country',
|
||||
'created',
|
||||
'objectValue',
|
||||
'prop1',
|
||||
'prop2',
|
||||
];
|
||||
function setupSchemaWorkflow() {
|
||||
cy.createFixtureWorkflow('Test_workflow_schema_test.json', `NDV test schema view ${uuid()}`);
|
||||
workflowPage.actions.zoomToFit();
|
||||
|
@ -108,41 +118,62 @@ describe('NDV', () => {
|
|||
}
|
||||
|
||||
it('should switch to output schema view and validate it', () => {
|
||||
setupSchemaWorkflow()
|
||||
setupSchemaWorkflow();
|
||||
ndv.getters.outputDisplayMode().children().should('have.length', 3);
|
||||
ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Table');
|
||||
ndv.getters.outputDisplayMode().contains('Schema').click();
|
||||
ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Schema');
|
||||
|
||||
schemaKeys.forEach((key) => {
|
||||
ndv.getters.outputPanel().find('[data-test-id=run-data-schema-item]').contains(key).should('exist');
|
||||
ndv.getters
|
||||
.outputPanel()
|
||||
.find('[data-test-id=run-data-schema-item]')
|
||||
.contains(key)
|
||||
.should('exist');
|
||||
});
|
||||
});
|
||||
it('should preserve schema view after execution', () => {
|
||||
setupSchemaWorkflow()
|
||||
setupSchemaWorkflow();
|
||||
ndv.getters.outputDisplayMode().contains('Schema').click();
|
||||
ndv.actions.execute();
|
||||
ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Schema');
|
||||
})
|
||||
});
|
||||
it('should collapse and expand nested schema object', () => {
|
||||
setupSchemaWorkflow()
|
||||
const expandedObjectProps = ['prop1', 'prop2'];;
|
||||
const getObjectValueItem = () => ndv.getters.outputPanel().find('[data-test-id=run-data-schema-item]').filter(':contains("objectValue")');
|
||||
setupSchemaWorkflow();
|
||||
const expandedObjectProps = ['prop1', 'prop2'];
|
||||
const getObjectValueItem = () =>
|
||||
ndv.getters
|
||||
.outputPanel()
|
||||
.find('[data-test-id=run-data-schema-item]')
|
||||
.filter(':contains("objectValue")');
|
||||
ndv.getters.outputDisplayMode().contains('Schema').click();
|
||||
|
||||
expandedObjectProps.forEach((key) => {
|
||||
ndv.getters.outputPanel().find('[data-test-id=run-data-schema-item]').contains(key).should('be.visible');
|
||||
ndv.getters
|
||||
.outputPanel()
|
||||
.find('[data-test-id=run-data-schema-item]')
|
||||
.contains(key)
|
||||
.should('be.visible');
|
||||
});
|
||||
getObjectValueItem().find('label').click();
|
||||
expandedObjectProps.forEach((key) => {
|
||||
ndv.getters.outputPanel().find('[data-test-id=run-data-schema-item]').contains(key).should('not.be.visible');
|
||||
ndv.getters
|
||||
.outputPanel()
|
||||
.find('[data-test-id=run-data-schema-item]')
|
||||
.contains(key)
|
||||
.should('not.be.visible');
|
||||
});
|
||||
})
|
||||
});
|
||||
it('should not display pagination for schema', () => {
|
||||
setupSchemaWorkflow()
|
||||
setupSchemaWorkflow();
|
||||
ndv.getters.backToCanvas().click();
|
||||
workflowPage.getters.canvasNodeByName('Set').click();
|
||||
workflowPage.actions.addNodeToCanvas('Customer Datastore (n8n training)', true, true, 'Get All People');
|
||||
workflowPage.actions.addNodeToCanvas(
|
||||
'Customer Datastore (n8n training)',
|
||||
true,
|
||||
true,
|
||||
'Get All People',
|
||||
);
|
||||
ndv.actions.execute();
|
||||
ndv.getters.outputPanel().contains('25 items').should('exist');
|
||||
ndv.getters.outputPanel().find('[class*=_pagination]').should('exist');
|
||||
|
@ -150,9 +181,12 @@ describe('NDV', () => {
|
|||
ndv.getters.outputPanel().find('[class*=_pagination]').should('not.exist');
|
||||
ndv.getters.outputDisplayMode().contains('JSON').click();
|
||||
ndv.getters.outputPanel().find('[class*=_pagination]').should('exist');
|
||||
})
|
||||
});
|
||||
it('should display large schema', () => {
|
||||
cy.createFixtureWorkflow('Test_workflow_schema_test_pinned_data.json', `NDV test schema view ${uuid()}`);
|
||||
cy.createFixtureWorkflow(
|
||||
'Test_workflow_schema_test_pinned_data.json',
|
||||
`NDV test schema view ${uuid()}`,
|
||||
);
|
||||
workflowPage.actions.zoomToFit();
|
||||
workflowPage.actions.openNode('Set');
|
||||
|
||||
|
@ -160,8 +194,11 @@ describe('NDV', () => {
|
|||
ndv.getters.outputPanel().find('[class*=_pagination]').should('exist');
|
||||
ndv.getters.outputDisplayMode().contains('Schema').click();
|
||||
ndv.getters.outputPanel().find('[class*=_pagination]').should('not.exist');
|
||||
ndv.getters.outputPanel().find('[data-test-id=run-data-schema-item] [data-test-id=run-data-schema-item]').should('have.length', 20);
|
||||
})
|
||||
ndv.getters
|
||||
.outputPanel()
|
||||
.find('[data-test-id=run-data-schema-item] [data-test-id=run-data-schema-item]')
|
||||
.should('have.length', 20);
|
||||
});
|
||||
});
|
||||
|
||||
it('can link and unlink run selectors between input and output', () => {
|
||||
|
@ -170,11 +207,13 @@ describe('NDV', () => {
|
|||
workflowPage.actions.executeWorkflow();
|
||||
workflowPage.actions.openNode('Set3');
|
||||
|
||||
ndv.getters.inputRunSelector()
|
||||
ndv.getters
|
||||
.inputRunSelector()
|
||||
.should('exist')
|
||||
.find('input')
|
||||
.should('include.value', '2 of 2 (6 items)');
|
||||
ndv.getters.outputRunSelector()
|
||||
ndv.getters
|
||||
.outputRunSelector()
|
||||
.should('exist')
|
||||
.find('input')
|
||||
.should('include.value', '2 of 2 (6 items)');
|
||||
|
@ -183,23 +222,20 @@ describe('NDV', () => {
|
|||
ndv.actions.switchOutputMode('Table');
|
||||
|
||||
ndv.actions.changeOutputRunSelector('1 of 2 (6 items)');
|
||||
ndv.getters.inputRunSelector()
|
||||
.find('input')
|
||||
.should('include.value', '1 of 2 (6 items)');
|
||||
ndv.getters.inputRunSelector().find('input').should('include.value', '1 of 2 (6 items)');
|
||||
ndv.getters.inputTbodyCell(1, 0).should('have.text', '1111');
|
||||
ndv.getters.outputTbodyCell(1, 0).should('have.text', '1111');
|
||||
|
||||
ndv.getters.inputTbodyCell(1, 0).click(); // remove tooltip
|
||||
ndv.actions.changeInputRunSelector('2 of 2 (6 items)');
|
||||
ndv.getters.outputRunSelector()
|
||||
.find('input')
|
||||
.should('include.value', '2 of 2 (6 items)');
|
||||
ndv.getters.outputRunSelector().find('input').should('include.value', '2 of 2 (6 items)');
|
||||
|
||||
// unlink
|
||||
ndv.actions.toggleOutputRunLinking();
|
||||
ndv.getters.inputTbodyCell(1, 0).click(); // remove tooltip
|
||||
ndv.actions.changeOutputRunSelector('1 of 2 (6 items)');
|
||||
ndv.getters.inputRunSelector()
|
||||
ndv.getters
|
||||
.inputRunSelector()
|
||||
.should('exist')
|
||||
.find('input')
|
||||
.should('include.value', '2 of 2 (6 items)');
|
||||
|
@ -207,24 +243,18 @@ describe('NDV', () => {
|
|||
// link again
|
||||
ndv.actions.toggleOutputRunLinking();
|
||||
ndv.getters.inputTbodyCell(1, 0).click(); // remove tooltip
|
||||
ndv.getters.inputRunSelector()
|
||||
.find('input')
|
||||
.should('include.value', '1 of 2 (6 items)');
|
||||
|
||||
ndv.getters.inputRunSelector().find('input').should('include.value', '1 of 2 (6 items)');
|
||||
|
||||
// unlink again
|
||||
ndv.actions.toggleInputRunLinking();
|
||||
ndv.getters.inputTbodyCell(1, 0).click(); // remove tooltip
|
||||
ndv.actions.changeInputRunSelector('2 of 2 (6 items)');
|
||||
ndv.getters.outputRunSelector()
|
||||
.find('input')
|
||||
.should('include.value', '1 of 2 (6 items)');
|
||||
ndv.getters.outputRunSelector().find('input').should('include.value', '1 of 2 (6 items)');
|
||||
|
||||
// link again
|
||||
ndv.actions.toggleInputRunLinking();
|
||||
ndv.getters.inputTbodyCell(1, 0).click(); // remove tooltip
|
||||
ndv.getters.outputRunSelector()
|
||||
.find('input')
|
||||
.should('include.value', '2 of 2 (6 items)');
|
||||
ndv.getters.outputRunSelector().find('input').should('include.value', '2 of 2 (6 items)');
|
||||
});
|
||||
|
||||
it('should display parameter hints correctly', () => {
|
||||
|
@ -247,21 +277,19 @@ describe('NDV', () => {
|
|||
input: ' test',
|
||||
},
|
||||
{
|
||||
input: ' '
|
||||
input: ' ',
|
||||
},
|
||||
{
|
||||
input: '<div></div>'
|
||||
input: '<div></div>',
|
||||
},
|
||||
].forEach(({ input, output }) => {
|
||||
if (input) {
|
||||
ndv.actions.typeIntoParameterInput('value', input);
|
||||
}
|
||||
ndv.getters.parameterInput('name').click(); // remove focus from input, hide expression preview
|
||||
|
||||
|
||||
if (input) {
|
||||
ndv.actions.typeIntoParameterInput('value', input);
|
||||
}
|
||||
ndv.getters.parameterInput('name').click(); // remove focus from input, hide expression preview
|
||||
|
||||
ndv.actions.validateExpressionPreview('value', output || input);
|
||||
ndv.getters.parameterInput('value').clear();
|
||||
});
|
||||
ndv.actions.validateExpressionPreview('value', output || input);
|
||||
ndv.getters.parameterInput('value').clear();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,25 +5,35 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import emitter from '@/mixins/emitter';
|
||||
import type { EventBus } from 'n8n-design-system/utils';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'IntersectionObserved',
|
||||
mixins: [emitter],
|
||||
props: ['enabled'],
|
||||
props: {
|
||||
enabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
eventBus: {
|
||||
type: Object as PropType<EventBus>,
|
||||
default: () => createEventBus(),
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.$dispatch('IntersectionObserver', 'observe', this.$refs.observed);
|
||||
this.eventBus.emit('observe', this.$refs.observed);
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.enabled) {
|
||||
this.$dispatch('IntersectionObserver', 'unobserve', this.$refs.observed);
|
||||
this.eventBus.emit('unobserve', this.$refs.observed);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -5,11 +5,27 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import type { EventBus } from 'n8n-design-system/utils';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'IntersectionObserver',
|
||||
props: ['threshold', 'enabled'],
|
||||
props: {
|
||||
threshold: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
enabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
eventBus: {
|
||||
type: Object as PropType<EventBus>,
|
||||
default: () => createEventBus(),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
observer: null,
|
||||
|
@ -35,13 +51,13 @@ export default defineComponent({
|
|||
});
|
||||
}, options);
|
||||
|
||||
this.$data.observer = observer;
|
||||
this.observer = observer;
|
||||
|
||||
this.$on('observe', (observed: Element) => {
|
||||
this.eventBus.on('observe', (observed: Element) => {
|
||||
observer.observe(observed);
|
||||
});
|
||||
|
||||
this.$on('unobserve', (observed: Element) => {
|
||||
this.eventBus.on('unobserve', (observed: Element) => {
|
||||
observer.unobserve(observed);
|
||||
});
|
||||
},
|
||||
|
|
|
@ -166,6 +166,7 @@ import type { IPermissions } from '@/permissions';
|
|||
import { getWorkflowPermissions } from '@/permissions';
|
||||
import { createEventBus } from 'n8n-design-system';
|
||||
import { useCloudPlanStore } from '@/stores';
|
||||
import { nodeViewEventBus } from '@/event-bus';
|
||||
|
||||
const hasChanged = (prev: string[], curr: string[]) => {
|
||||
if (prev.length !== curr.length) {
|
||||
|
@ -445,7 +446,7 @@ export default defineComponent({
|
|||
return;
|
||||
}
|
||||
|
||||
this.$root.$emit('importWorkflowData', { data: workflowData });
|
||||
nodeViewEventBus.emit('importWorkflowData', { data: workflowData });
|
||||
};
|
||||
|
||||
const inputRef = this.$refs.importFile as HTMLInputElement | undefined;
|
||||
|
@ -505,7 +506,7 @@ export default defineComponent({
|
|||
},
|
||||
)) as MessageBoxInputData;
|
||||
|
||||
this.$root.$emit('importWorkflowUrl', { url: promptResponse.value });
|
||||
nodeViewEventBus.emit('importWorkflowUrl', { url: promptResponse.value });
|
||||
} catch (e) {}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
:droppable="droppable"
|
||||
:node="node"
|
||||
:path="path"
|
||||
:event-bus="eventBus"
|
||||
@input="valueChanged"
|
||||
@modalOpenerClick="openExpressionEditorModal"
|
||||
@focus="setFocus"
|
||||
|
@ -391,8 +392,8 @@ import { useCredentialsStore } from '@/stores/credentials.store';
|
|||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { htmlEditorEventBus } from '@/event-bus';
|
||||
import Vue from 'vue';
|
||||
|
||||
type ResourceLocatorRef = InstanceType<typeof ResourceLocator>;
|
||||
import type { EventBus } from 'n8n-design-system/utils';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'parameter-input',
|
||||
|
@ -463,6 +464,10 @@ export default defineComponent({
|
|||
size: 'small',
|
||||
}),
|
||||
},
|
||||
eventBus: {
|
||||
type: Object as PropType<EventBus>,
|
||||
default: () => createEventBus(),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -1112,9 +1117,7 @@ export default defineComponent({
|
|||
}
|
||||
} else if (command === 'refreshOptions') {
|
||||
if (this.isResourceLocatorParameter) {
|
||||
const resourceLocatorRef = this.$refs.resourceLocator as ResourceLocatorRef | undefined;
|
||||
|
||||
resourceLocatorRef?.$emit('refreshList');
|
||||
this.eventBus.emit('refreshList');
|
||||
}
|
||||
void this.loadRemoteParameterOptions();
|
||||
} else if (command === 'formatHtml') {
|
||||
|
@ -1146,7 +1149,7 @@ export default defineComponent({
|
|||
});
|
||||
},
|
||||
mounted() {
|
||||
this.$on('optionSelected', this.optionSelected);
|
||||
this.eventBus.on('optionSelected', this.optionSelected);
|
||||
|
||||
this.tempValue = this.displayValue as string;
|
||||
if (this.node !== null) {
|
||||
|
@ -1186,6 +1189,9 @@ export default defineComponent({
|
|||
inputFieldRef: this.$refs['inputField'],
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.eventBus.off('optionSelected', this.optionSelected);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
:isForCredential="true"
|
||||
:eventSource="eventSource"
|
||||
:hint="!showRequiredErrors ? hint : ''"
|
||||
:event-bus="eventBus"
|
||||
@focus="onFocus"
|
||||
@blur="onBlur"
|
||||
@textInput="valueChanged"
|
||||
|
@ -65,6 +66,7 @@ import { isValueExpression } from '@/utils';
|
|||
import type { INodeParameterResourceLocator, INodeProperties, IParameterLabel } from 'n8n-workflow';
|
||||
import { mapStores } from 'pinia';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
|
||||
type ParamRef = InstanceType<typeof ParameterInputWrapper>;
|
||||
|
||||
|
@ -100,6 +102,7 @@ export default defineComponent({
|
|||
focused: false,
|
||||
blurredEver: false,
|
||||
menuExpanded: false,
|
||||
eventBus: createEventBus(),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -147,9 +150,7 @@ export default defineComponent({
|
|||
this.menuExpanded = expanded;
|
||||
},
|
||||
optionSelected(command: string) {
|
||||
if (this.$refs.param) {
|
||||
(this.$refs.param as ParamRef).$emit('optionSelected', command);
|
||||
}
|
||||
this.eventBus.emit('optionSelected', command);
|
||||
},
|
||||
valueChanged(parameterData: IUpdateInformation) {
|
||||
this.$emit('change', parameterData);
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
:hint="hint"
|
||||
:hide-issues="hideIssues"
|
||||
:label="label"
|
||||
:event-bus="eventBus"
|
||||
@valueChanged="valueChanged"
|
||||
@textInput="onTextInput"
|
||||
@focus="onFocus"
|
||||
|
@ -98,6 +99,7 @@ import { useNDVStore } from '@/stores/ndv.store';
|
|||
import { useSegment } from '@/stores/segment.store';
|
||||
import { externalHooks } from '@/mixins/externalHooks';
|
||||
import { getMappedResult } from '@/utils/mappingUtils';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
|
||||
type ParameterInputWrapperRef = InstanceType<typeof ParameterInputWrapper>;
|
||||
|
||||
|
@ -112,7 +114,10 @@ export default defineComponent({
|
|||
ParameterInputWrapper,
|
||||
},
|
||||
setup() {
|
||||
const eventBus = createEventBus();
|
||||
|
||||
return {
|
||||
eventBus,
|
||||
...useToast(),
|
||||
};
|
||||
},
|
||||
|
@ -234,17 +239,14 @@ export default defineComponent({
|
|||
this.menuExpanded = expanded;
|
||||
},
|
||||
optionSelected(command: string) {
|
||||
const paramRef = this.$refs.param as ParameterInputWrapperRef | undefined;
|
||||
paramRef?.$emit('optionSelected', command);
|
||||
this.eventBus.emit('optionSelected', command);
|
||||
},
|
||||
valueChanged(parameterData: IUpdateInformation) {
|
||||
this.$emit('valueChanged', parameterData);
|
||||
},
|
||||
onTextInput(parameterData: IUpdateInformation) {
|
||||
const paramRef = this.$refs.param as ParameterInputWrapperRef | undefined;
|
||||
|
||||
if (isValueExpression(this.parameter, parameterData.value)) {
|
||||
paramRef?.$emit('optionSelected', 'addExpression');
|
||||
this.eventBus.emit('optionSelected', 'addExpression');
|
||||
}
|
||||
},
|
||||
onDrop(newParamValue: string) {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
:expressionEvaluated="expressionValueComputed"
|
||||
:label="label"
|
||||
:data-test-id="`parameter-input-${parameter.name}`"
|
||||
:event-bus="internalEventBus"
|
||||
@focus="onFocus"
|
||||
@blur="onBlur"
|
||||
@drop="onDrop"
|
||||
|
@ -61,8 +62,8 @@ import type { INodeUi, IUpdateInformation, TargetItem } from '@/Interface';
|
|||
import { workflowHelpers } from '@/mixins/workflowHelpers';
|
||||
import { isValueExpression } from '@/utils';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
|
||||
type ParamRef = InstanceType<typeof ParameterInput>;
|
||||
import type { EventBus } from 'n8n-design-system/utils';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'parameter-input-wrapper',
|
||||
|
@ -71,8 +72,16 @@ export default defineComponent({
|
|||
ParameterInput,
|
||||
InputHint,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
internalEventBus: createEventBus(),
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$on('optionSelected', this.optionSelected);
|
||||
this.eventBus.on('optionSelected', this.optionSelected);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.eventBus.off('optionSelected', this.optionSelected);
|
||||
},
|
||||
props: {
|
||||
isReadOnly: {
|
||||
|
@ -124,6 +133,10 @@ export default defineComponent({
|
|||
size: 'small',
|
||||
}),
|
||||
},
|
||||
eventBus: {
|
||||
type: Object as PropType<EventBus>,
|
||||
default: () => createEventBus(),
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useNDVStore),
|
||||
|
@ -217,9 +230,7 @@ export default defineComponent({
|
|||
this.$emit('drop', data);
|
||||
},
|
||||
optionSelected(command: string) {
|
||||
const paramRef = this.$refs.param as ParamRef | undefined;
|
||||
|
||||
paramRef?.$emit('optionSelected', command);
|
||||
this.internalEventBus.emit('optionSelected', command);
|
||||
},
|
||||
onValueChanged(parameterData: IUpdateInformation) {
|
||||
this.$emit('valueChanged', parameterData);
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
:hasMore="currentQueryHasMore"
|
||||
:errorView="currentQueryError"
|
||||
:width="width"
|
||||
:event-bus="eventBus"
|
||||
@input="onListItemSelected"
|
||||
@hide="onDropdownHide"
|
||||
@filter="onSearchFilter"
|
||||
@loadMore="loadResourcesDebounced"
|
||||
ref="dropdown"
|
||||
>
|
||||
<template #error>
|
||||
<div :class="$style.error" data-test-id="rlc-error-container">
|
||||
|
@ -176,8 +176,8 @@ import { useWorkflowsStore } from '@/stores/workflows.store';
|
|||
import { useRootStore } from '@/stores/n8nRoot.store';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
|
||||
type ResourceLocatorDropdownRef = InstanceType<typeof ResourceLocatorDropdown>;
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
import type { EventBus } from 'n8n-design-system/utils';
|
||||
|
||||
interface IResourceLocatorQuery {
|
||||
results: INodeListSearchItems[];
|
||||
|
@ -256,6 +256,10 @@ export default defineComponent({
|
|||
loadOptionsMethod: {
|
||||
type: String,
|
||||
},
|
||||
eventBus: {
|
||||
type: Object as PropType<EventBus>,
|
||||
default: () => createEventBus(),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -475,17 +479,20 @@ export default defineComponent({
|
|||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$on('refreshList', this.refreshList);
|
||||
this.eventBus.on('refreshList', this.refreshList);
|
||||
window.addEventListener('resize', this.setWidth);
|
||||
|
||||
useNDVStore().$subscribe((mutation, state) => {
|
||||
// Update the width when main panel dimension change
|
||||
this.setWidth();
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.setWidth();
|
||||
}, 0);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.eventBus.off('refreshList', this.refreshList);
|
||||
window.removeEventListener('resize', this.setWidth);
|
||||
},
|
||||
methods: {
|
||||
|
@ -510,9 +517,8 @@ export default defineComponent({
|
|||
this.trackEvent('User refreshed resource locator list');
|
||||
},
|
||||
onKeyDown(e: MouseEvent) {
|
||||
const dropdownRef = this.$refs.dropdown as ResourceLocatorDropdownRef | undefined;
|
||||
if (dropdownRef && this.showResourceDropdown && !this.isSearchable) {
|
||||
dropdownRef.$emit('keyDown', e);
|
||||
if (this.showResourceDropdown && !this.isSearchable) {
|
||||
this.eventBus.emit('keyDown', e);
|
||||
}
|
||||
},
|
||||
openResource(url: string) {
|
||||
|
|
|
@ -82,6 +82,8 @@
|
|||
import type { IResourceLocatorResultExpanded } from '@/Interface';
|
||||
import { defineComponent } from 'vue';
|
||||
import type { PropType } from 'vue';
|
||||
import type { EventBus } from 'n8n-design-system/utils';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
|
||||
const SEARCH_BAR_HEIGHT_PX = 40;
|
||||
const SCROLL_MARGIN_PX = 10;
|
||||
|
@ -120,6 +122,10 @@ export default defineComponent({
|
|||
width: {
|
||||
type: Number,
|
||||
},
|
||||
eventBus: {
|
||||
type: Object as PropType<EventBus>,
|
||||
default: () => createEventBus(),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -128,7 +134,10 @@ export default defineComponent({
|
|||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$on('keyDown', this.onKeyDown);
|
||||
this.eventBus.on('keyDown', this.onKeyDown);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.eventBus.off('keyDown', this.onKeyDown);
|
||||
},
|
||||
computed: {
|
||||
sortedResources(): IResourceLocatorResultExpanded[] {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
@observed="onObserved"
|
||||
class="tags-container"
|
||||
:enabled="responsive"
|
||||
:event-bus="intersectionEventBus"
|
||||
>
|
||||
<template>
|
||||
<span class="tags">
|
||||
|
@ -26,6 +27,7 @@
|
|||
:class="{ hidden: tag.hidden }"
|
||||
:data-id="tag.id"
|
||||
:enabled="responsive"
|
||||
:event-bus="intersectionEventBus"
|
||||
v-else
|
||||
>
|
||||
<el-tag :title="tag.name" type="info" size="small" :class="{ hoverable }">
|
||||
|
@ -46,6 +48,7 @@ import IntersectionObserver from './IntersectionObserver.vue';
|
|||
import IntersectionObserved from './IntersectionObserved.vue';
|
||||
import { mapStores } from 'pinia';
|
||||
import { useTagsStore } from '@/stores/tags.store';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
|
||||
// random upper limit if none is set to minimize performance impact of observers
|
||||
const DEFAULT_MAX_TAGS_LIMIT = 20;
|
||||
|
@ -62,6 +65,7 @@ export default defineComponent({
|
|||
props: ['tagIds', 'limit', 'clickable', 'responsive', 'hoverable'],
|
||||
data() {
|
||||
return {
|
||||
intersectionEventBus: createEventBus(),
|
||||
visibility: {} as { [id: string]: boolean },
|
||||
};
|
||||
},
|
||||
|
|
|
@ -3,13 +3,14 @@
|
|||
* unsafe onclick attribute
|
||||
*/
|
||||
import { reactive, del, computed, onMounted, onUnmounted, getCurrentInstance } from 'vue';
|
||||
import { globalLinkActionsEventBus } from '@/event-bus';
|
||||
|
||||
const state = reactive({
|
||||
customActions: {} as Record<string, Function>,
|
||||
});
|
||||
|
||||
export default () => {
|
||||
function registerCustomAction(key: string, action: Function) {
|
||||
function registerCustomAction({ key, action }: { key: string; action: Function }) {
|
||||
state.customActions[key] = action;
|
||||
}
|
||||
function unregisterCustomAction(key: string) {
|
||||
|
@ -42,13 +43,15 @@ export default () => {
|
|||
onMounted(() => {
|
||||
const instance = getCurrentInstance();
|
||||
window.addEventListener('click', delegateClick);
|
||||
instance?.proxy.$root.$on('registerGlobalLinkAction', registerCustomAction);
|
||||
|
||||
globalLinkActionsEventBus.on('registerGlobalLinkAction', registerCustomAction);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
const instance = getCurrentInstance();
|
||||
window.removeEventListener('click', delegateClick);
|
||||
instance?.proxy.$root.$off('registerGlobalLinkAction', registerCustomAction);
|
||||
|
||||
globalLinkActionsEventBus.off('registerGlobalLinkAction', registerCustomAction);
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export * from './code-node-editor';
|
||||
export * from './data-pinning';
|
||||
export * from './link-actions';
|
||||
export * from './html-editor';
|
||||
export * from './node-view';
|
||||
|
|
3
packages/editor-ui/src/event-bus/link-actions.ts
Normal file
3
packages/editor-ui/src/event-bus/link-actions.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
import { createEventBus } from 'n8n-design-system';
|
||||
|
||||
export const globalLinkActionsEventBus = createEventBus();
|
|
@ -1,50 +0,0 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
function broadcast(
|
||||
this: InstanceType<typeof EmitterMixin>,
|
||||
componentName: string,
|
||||
eventName: string,
|
||||
params: any,
|
||||
) {
|
||||
this.$children.forEach((child) => {
|
||||
const name = child.$options.name;
|
||||
|
||||
if (name === componentName) {
|
||||
// eslint-disable-next-line prefer-spread
|
||||
child.$emit.apply(child, [eventName].concat(params) as Parameters<typeof child.$emit>);
|
||||
} else {
|
||||
broadcast.apply(
|
||||
child as InstanceType<typeof EmitterMixin>,
|
||||
[componentName, eventName].concat([params]) as Parameters<typeof broadcast>,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const EmitterMixin = defineComponent({
|
||||
methods: {
|
||||
$dispatch(componentName: string, eventName: string, params: any) {
|
||||
let parent = this.$parent || this.$root;
|
||||
let name = parent.$options.name;
|
||||
|
||||
while (parent && (!name || name !== componentName)) {
|
||||
parent = parent.$parent as InstanceType<typeof EmitterMixin>;
|
||||
|
||||
if (parent) {
|
||||
name = parent.$options.name;
|
||||
}
|
||||
}
|
||||
if (parent) {
|
||||
// eslint-disable-next-line prefer-spread
|
||||
parent.$emit.apply(parent, [eventName].concat(params) as Parameters<typeof parent.$emit>);
|
||||
}
|
||||
},
|
||||
|
||||
$broadcast(componentName: string, eventName: string, params: any) {
|
||||
broadcast.call(this, componentName, eventName, params);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default EmitterMixin;
|
|
@ -24,7 +24,7 @@ import { TelemetryHelpers } from 'n8n-workflow';
|
|||
|
||||
import { WORKFLOW_SETTINGS_MODAL_KEY } from '@/constants';
|
||||
import { getTriggerNodeServiceName } from '@/utils';
|
||||
import { codeNodeEditorEventBus } from '@/event-bus';
|
||||
import { codeNodeEditorEventBus, globalLinkActionsEventBus } from '@/event-bus';
|
||||
import { mapStores } from 'pinia';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
|
@ -351,9 +351,12 @@ export const pushConnection = defineComponent({
|
|||
|
||||
let action;
|
||||
if (!isSavingExecutions) {
|
||||
this.$root.$emit('registerGlobalLinkAction', 'open-settings', async () => {
|
||||
if (this.workflowsStore.isNewWorkflow) await this.saveAsNewWorkflow();
|
||||
this.uiStore.openModal(WORKFLOW_SETTINGS_MODAL_KEY);
|
||||
globalLinkActionsEventBus.emit('registerGlobalLinkAction', {
|
||||
key: 'open-settings',
|
||||
action: async () => {
|
||||
if (this.workflowsStore.isNewWorkflow) await this.saveAsNewWorkflow();
|
||||
this.uiStore.openModal(WORKFLOW_SETTINGS_MODAL_KEY);
|
||||
},
|
||||
});
|
||||
|
||||
action =
|
||||
|
|
|
@ -672,9 +672,12 @@ export default defineComponent({
|
|||
? this.$locale.baseText('nodeView.addOrEnableTriggerNode')
|
||||
: this.$locale.baseText('nodeView.addATriggerNodeFirst');
|
||||
|
||||
this.registerCustomAction('showNodeCreator', () =>
|
||||
this.showTriggerCreator(NODE_CREATOR_OPEN_SOURCES.NO_TRIGGER_EXECUTION_TOOLTIP),
|
||||
);
|
||||
this.registerCustomAction({
|
||||
key: 'showNodeCreator',
|
||||
action: () =>
|
||||
this.showTriggerCreator(NODE_CREATOR_OPEN_SOURCES.NO_TRIGGER_EXECUTION_TOOLTIP),
|
||||
});
|
||||
|
||||
const notice = this.showMessage({
|
||||
type: 'info',
|
||||
title: this.$locale.baseText('nodeView.cantExecuteNoTrigger'),
|
||||
|
@ -1050,7 +1053,7 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
if (this.$router.currentRoute.name === VIEWS.NEW_WORKFLOW) {
|
||||
this.$root.$emit('newWorkflow');
|
||||
nodeViewEventBus.emit('newWorkflow');
|
||||
} else {
|
||||
void this.$router.push({ name: VIEWS.NEW_WORKFLOW });
|
||||
}
|
||||
|
@ -3913,9 +3916,9 @@ export default defineComponent({
|
|||
window.addEventListener('message', this.onPostMessageReceived);
|
||||
window.addEventListener('pageshow', this.onPageShow);
|
||||
|
||||
this.$root.$on('newWorkflow', this.newWorkflow);
|
||||
this.$root.$on('importWorkflowData', this.onImportWorkflowDataEvent);
|
||||
this.$root.$on('importWorkflowUrl', this.onImportWorkflowUrlEvent);
|
||||
nodeViewEventBus.on('newWorkflow', this.newWorkflow);
|
||||
nodeViewEventBus.on('importWorkflowData', this.onImportWorkflowDataEvent);
|
||||
nodeViewEventBus.on('importWorkflowUrl', this.onImportWorkflowUrlEvent);
|
||||
historyBus.on('nodeMove', this.onMoveNode);
|
||||
historyBus.on('revertAddNode', this.onRevertAddNode);
|
||||
historyBus.on('revertRemoveNode', this.onRevertRemoveNode);
|
||||
|
@ -3938,9 +3941,9 @@ export default defineComponent({
|
|||
window.removeEventListener('beforeunload', this.onBeforeUnload);
|
||||
window.removeEventListener('pageshow', this.onPageShow);
|
||||
|
||||
this.$root.$off('newWorkflow', this.newWorkflow);
|
||||
this.$root.$off('importWorkflowData', this.onImportWorkflowDataEvent);
|
||||
this.$root.$off('importWorkflowUrl', this.onImportWorkflowUrlEvent);
|
||||
nodeViewEventBus.off('newWorkflow', this.newWorkflow);
|
||||
nodeViewEventBus.off('importWorkflowData', this.onImportWorkflowDataEvent);
|
||||
nodeViewEventBus.off('importWorkflowUrl', this.onImportWorkflowUrlEvent);
|
||||
historyBus.off('nodeMove', this.onMoveNode);
|
||||
historyBus.off('revertAddNode', this.onRevertAddNode);
|
||||
historyBus.off('revertRemoveNode', this.onRevertRemoveNode);
|
||||
|
@ -3959,9 +3962,9 @@ export default defineComponent({
|
|||
this.instance.destroy();
|
||||
this.uiStore.stateIsDirty = false;
|
||||
window.removeEventListener('message', this.onPostMessageReceived);
|
||||
this.$root.$off('newWorkflow', this.newWorkflow);
|
||||
this.$root.$off('importWorkflowData', this.onImportWorkflowDataEvent);
|
||||
this.$root.$off('importWorkflowUrl', this.onImportWorkflowUrlEvent);
|
||||
nodeViewEventBus.off('newWorkflow', this.newWorkflow);
|
||||
nodeViewEventBus.off('importWorkflowData', this.onImportWorkflowDataEvent);
|
||||
nodeViewEventBus.off('importWorkflowUrl', this.onImportWorkflowUrlEvent);
|
||||
this.workflowsStore.setWorkflowId(PLACEHOLDER_EMPTY_WORKFLOW_ID);
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue