From 0154a97773bccf1f00bf5606ddcabdb11f561be9 Mon Sep 17 00:00:00 2001 From: Alex Grozav Date: Tue, 20 Jun 2023 13:00:53 +0300 Subject: [PATCH] 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 --- cypress/e2e/5-ndv.cy.ts | 126 +++++++++++------- .../src/components/IntersectionObserved.vue | 20 ++- .../src/components/IntersectionObserver.vue | 24 +++- .../components/MainHeader/WorkflowDetails.vue | 5 +- .../src/components/ParameterInput.vue | 18 ++- .../src/components/ParameterInputExpanded.vue | 7 +- .../src/components/ParameterInputFull.vue | 12 +- .../src/components/ParameterInputWrapper.vue | 23 +++- .../ResourceLocator/ResourceLocator.vue | 20 ++- .../ResourceLocatorDropdown.vue | 11 +- .../src/components/TagsContainer.vue | 4 + .../src/composables/useGlobalLinkActions.ts | 9 +- packages/editor-ui/src/event-bus/index.ts | 1 + .../editor-ui/src/event-bus/link-actions.ts | 3 + packages/editor-ui/src/mixins/emitter.ts | 50 ------- .../editor-ui/src/mixins/pushConnection.ts | 11 +- packages/editor-ui/src/views/NodeView.vue | 29 ++-- 17 files changed, 215 insertions(+), 158 deletions(-) create mode 100644 packages/editor-ui/src/event-bus/link-actions.ts delete mode 100644 packages/editor-ui/src/mixins/emitter.ts diff --git a/cypress/e2e/5-ndv.cy.ts b/cypress/e2e/5-ndv.cy.ts index a375330cf7..93198a2391 100644 --- a/cypress/e2e/5-ndv.cy.ts +++ b/cypress/e2e/5-ndv.cy.ts @@ -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: '
' + input: '
', }, ].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(); + }); }); }); diff --git a/packages/editor-ui/src/components/IntersectionObserved.vue b/packages/editor-ui/src/components/IntersectionObserved.vue index e171fb5b29..041e70f0c7 100644 --- a/packages/editor-ui/src/components/IntersectionObserved.vue +++ b/packages/editor-ui/src/components/IntersectionObserved.vue @@ -5,25 +5,35 @@ diff --git a/packages/editor-ui/src/components/ParameterInputExpanded.vue b/packages/editor-ui/src/components/ParameterInputExpanded.vue index 334fc6b171..3e2faac1c5 100644 --- a/packages/editor-ui/src/components/ParameterInputExpanded.vue +++ b/packages/editor-ui/src/components/ParameterInputExpanded.vue @@ -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; @@ -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); diff --git a/packages/editor-ui/src/components/ParameterInputFull.vue b/packages/editor-ui/src/components/ParameterInputFull.vue index 7653d3ff65..3ff7e88355 100644 --- a/packages/editor-ui/src/components/ParameterInputFull.vue +++ b/packages/editor-ui/src/components/ParameterInputFull.vue @@ -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; @@ -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) { diff --git a/packages/editor-ui/src/components/ParameterInputWrapper.vue b/packages/editor-ui/src/components/ParameterInputWrapper.vue index 5f5b221fe6..a8d794eb98 100644 --- a/packages/editor-ui/src/components/ParameterInputWrapper.vue +++ b/packages/editor-ui/src/components/ParameterInputWrapper.vue @@ -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; +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, + 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); diff --git a/packages/editor-ui/src/components/ResourceLocator/ResourceLocator.vue b/packages/editor-ui/src/components/ResourceLocator/ResourceLocator.vue index d4362c3c27..69af9376dc 100644 --- a/packages/editor-ui/src/components/ResourceLocator/ResourceLocator.vue +++ b/packages/editor-ui/src/components/ResourceLocator/ResourceLocator.vue @@ -15,11 +15,11 @@ :hasMore="currentQueryHasMore" :errorView="currentQueryError" :width="width" + :event-bus="eventBus" @input="onListItemSelected" @hide="onDropdownHide" @filter="onSearchFilter" @loadMore="loadResourcesDebounced" - ref="dropdown" >