From c486ffcf2d0a7789acd27ba5a4299c0ac60e0ed7 Mon Sep 17 00:00:00 2001 From: OlegIvaniv Date: Wed, 30 Nov 2022 17:27:41 +0100 Subject: [PATCH] refactor(editor): Patch ElementUI tooltip memory leak (#4769) * refactor(editor): Fix ElementUI tooltip memory leak * fix(editor): Remove patch console.logs and revert node-creator test --- cypress/e2e/4-node-creator.cy.ts | 19 +- cypress/pages/features/node-creator.ts | 1 + package.json | 5 +- .../components/Node/NodeCreator/NodeItem.vue | 2 +- patches/element-ui@2.15.12.patch | 263 ++++++++++++++++++ pnpm-lock.yaml | 3 + 6 files changed, 284 insertions(+), 9 deletions(-) create mode 100644 patches/element-ui@2.15.12.patch diff --git a/cypress/e2e/4-node-creator.cy.ts b/cypress/e2e/4-node-creator.cy.ts index 7c2c3170b1..e11dc9b3f2 100644 --- a/cypress/e2e/4-node-creator.cy.ts +++ b/cypress/e2e/4-node-creator.cy.ts @@ -97,9 +97,7 @@ describe('Node Creator', () => { // TODO: Replace once we have canvas feature utils cy.get('div').contains("On clicking 'execute'").should('exist'); }) - // TODO: This tests has to be skipped due to a memory leak in the node creator - // we should unskip it once we have fixed the memory leak - it.skip('check if non-core nodes are rendered', () => { + it('check if non-core nodes are rendered', () => { cy.wait('@nodesIntercept').then((interception) => { const nodes = interception.response?.body as INodeTypeDescription[]; @@ -115,9 +113,18 @@ describe('Node Creator', () => { nodeCreatorFeature.actions.toggleCategory(category) // Check if all nodes are present - categorizedNodes[category].forEach((node: INodeTypeDescription) => { - if(node.hidden) return; - nodeCreatorFeature.getters.categorizedItems().contains(node.displayName).should('exist'); + nodeCreatorFeature.getters.nodeItemName().then($elements => { + const visibleNodes: string[] = []; + $elements.each((_, element) => { + visibleNodes.push(element.textContent?.trim() || ''); + }) + const visibleCategoryNodes = (categorizedNodes[category] as INodeTypeDescription[]) + .filter(node => !node.hidden) + .map(node => node.displayName?.trim()); + + cy.wrap(visibleCategoryNodes).each((categoryNode: string) => { + expect(visibleNodes).to.include(categoryNode); + }); }) nodeCreatorFeature.actions.toggleCategory(category) diff --git a/cypress/pages/features/node-creator.ts b/cypress/pages/features/node-creator.ts index 129277a753..54dc9790ba 100644 --- a/cypress/pages/features/node-creator.ts +++ b/cypress/pages/features/node-creator.ts @@ -16,6 +16,7 @@ export class NodeCreator extends BasePage { creatorItem: () => cy.getByTestId('item-iterator-item'), communityNodeTooltip: () => cy.getByTestId('node-item-community-tooltip'), noResults: () => cy.getByTestId('categorized-no-results'), + nodeItemName: () => cy.getByTestId('node-item-name'), activeSubcategory: () => cy.getByTestId('categorized-items-subcategory'), expandedCategories: () => this.getters.creatorItem().find('>div').filter('.active').invoke('text'), }; diff --git a/package.json b/package.json index 4b57d284ef..4d122df74c 100644 --- a/package.json +++ b/package.json @@ -42,10 +42,10 @@ "@types/node": "^16.11.22", "cross-env": "^7.0.3", "cypress": "^10.0.3", - "node-fetch": "^2.6.7", "jest": "^29.3.1", "jest-environment-jsdom": "^29.3.1", "jest-mock": "^29.3.1", + "node-fetch": "^2.6.7", "prettier": "^2.3.2", "rimraf": "^3.0.2", "run-script-os": "^1.0.7", @@ -58,7 +58,8 @@ }, "pnpm": { "patchedDependencies": { - "quill@2.0.0-dev.4": "patches/quill@2.0.0-dev.4.patch" + "quill@2.0.0-dev.4": "patches/quill@2.0.0-dev.4.patch", + "element-ui@2.15.12": "patches/element-ui@2.15.12.patch" }, "onlyBuiltDependencies": [ "sqlite3", diff --git a/packages/editor-ui/src/components/Node/NodeCreator/NodeItem.vue b/packages/editor-ui/src/components/Node/NodeCreator/NodeItem.vue index f48bfe25bd..b90547e48d 100644 --- a/packages/editor-ui/src/components/Node/NodeCreator/NodeItem.vue +++ b/packages/editor-ui/src/components/Node/NodeCreator/NodeItem.vue @@ -8,7 +8,7 @@
- + {{ $locale.headerText({ key: `headers.${shortNodeType}.displayName`, fallback: nodeType.displayName, diff --git a/patches/element-ui@2.15.12.patch b/patches/element-ui@2.15.12.patch new file mode 100644 index 0000000000..1b598f92bc --- /dev/null +++ b/patches/element-ui@2.15.12.patch @@ -0,0 +1,263 @@ +diff --git a/lib/element-ui.common.js b/lib/element-ui.common.js +index 6209b8b7f21c6eea447dd4671b7c4cfc1ef5e9c2..a26772af8dcdcbc0e3a6058bda30c7f9f143cd5d 100644 +--- a/lib/element-ui.common.js ++++ b/lib/element-ui.common.js +@@ -21471,14 +21471,8 @@ mainvue_type_template_id_52060272_render._withStripped = true + popper.setAttribute('tabindex', 0); + + if (this.trigger !== 'click') { +- Object(dom_["on"])(reference, 'focusin', function () { +- _this.handleFocus(); +- var instance = reference.__vue__; +- if (instance && typeof instance.focus === 'function') { +- instance.focus(); +- } +- }); +- Object(dom_["on"])(popper, 'focusin', this.handleFocus); ++ Object(dom_["on"])(reference, 'focusin', this.handleRefrenceFocus); ++ Object(dom_["on"])(popper, 'focusin', this.handlePopperFocus); + Object(dom_["on"])(reference, 'focusout', this.handleBlur); + Object(dom_["on"])(popper, 'focusout', this.handleBlur); + } +@@ -21524,7 +21518,17 @@ mainvue_type_template_id_52060272_render._withStripped = true + doClose: function doClose() { + this.showPopper = false; + }, +- handleFocus: function handleFocus() { ++ handleRefrenceFocus: function handleRefrenceFocus() { ++ this.handlePopperFocus(); ++ if (!this.referenceElm) { ++ return; ++ } ++ const instance = this.referenceElm.__vue__; ++ if (instance && typeof instance.focus === 'function') { ++ instance.focus(); ++ } ++ }, ++ handlePopperFocus: function handlePopperFocus() { + Object(dom_["addClass"])(this.referenceElm, 'focusing'); + if (this.trigger === 'click' || this.trigger === 'focus') this.showPopper = true; + }, +@@ -21570,7 +21574,7 @@ mainvue_type_template_id_52060272_render._withStripped = true + var popper = this.popper || this.$refs.popper; + + if (!reference && this.$refs.wrapper.children) { +- reference = this.referenceElm = this.$refs.wrapper.children[0]; ++ reference = this.$refs.wrapper.children[0]; + } + if (!this.$el || !reference || this.$el.contains(e.target) || reference.contains(e.target) || !popper || popper.contains(e.target)) return; + this.showPopper = false; +@@ -21590,18 +21594,28 @@ mainvue_type_template_id_52060272_render._withStripped = true + }, + + destroyed: function destroyed() { +- var reference = this.reference; ++ var reference = this.referenceElm; ++ var popper = this.popper || this.$refs.popper; + ++ Object(dom_["off"])(reference, 'focusin', this.handleRefrenceFocus); ++ Object(dom_["off"])(popper, 'focusin', this.handlePopperFocus); ++ Object(dom_["off"])(reference, 'focusout', this.handleBlur); ++ Object(dom_["off"])(popper, 'focusout', this.handleBlur); ++ Object(dom_["off"])(reference, 'keydown', this.handleKeydown); ++ Object(dom_["off"])(reference, 'click', this.handleClick); + Object(dom_["off"])(reference, 'click', this.doToggle); ++ Object(dom_["off"])(document, 'click', this.handleDocumentClick); ++ Object(dom_["off"])(reference, 'mouseenter', this.handleMouseEnter); ++ Object(dom_["off"])(popper, 'mouseenter', this.handleMouseEnter); ++ Object(dom_["off"])(reference, 'mouseleave', this.handleMouseLeave); ++ Object(dom_["off"])(popper, 'mouseleave', this.handleMouseLeave); + Object(dom_["off"])(reference, 'mouseup', this.doClose); + Object(dom_["off"])(reference, 'mousedown', this.doShow); + Object(dom_["off"])(reference, 'focusin', this.doShow); + Object(dom_["off"])(reference, 'focusout', this.doClose); + Object(dom_["off"])(reference, 'mousedown', this.doShow); + Object(dom_["off"])(reference, 'mouseup', this.doClose); +- Object(dom_["off"])(reference, 'mouseleave', this.handleMouseLeave); +- Object(dom_["off"])(reference, 'mouseenter', this.handleMouseEnter); +- Object(dom_["off"])(document, 'click', this.handleDocumentClick); ++ this.referenceElm = undefined; + } + }); + // CONCATENATED MODULE: ./packages/popover/src/main.vue?vue&type=script&lang=js& +@@ -21805,18 +21819,7 @@ main.directive = directive; + this.$el.setAttribute('tabindex', this.tabindex); + Object(dom_["on"])(this.referenceElm, 'mouseenter', this.show); + Object(dom_["on"])(this.referenceElm, 'mouseleave', this.hide); +- Object(dom_["on"])(this.referenceElm, 'focus', function () { +- if (!_this3.$slots.default || !_this3.$slots.default.length) { +- _this3.handleFocus(); +- return; +- } +- var instance = _this3.$slots.default[0].componentInstance; +- if (instance && instance.focus) { +- instance.focus(); +- } else { +- _this3.handleFocus(); +- } +- }); ++ Object(dom_["on"])(this.referenceElm, 'focus', this.handleFocus); + Object(dom_["on"])(this.referenceElm, 'blur', this.handleBlur); + Object(dom_["on"])(this.referenceElm, 'click', this.removeFocusing); + } +@@ -21849,8 +21852,18 @@ main.directive = directive; + this.debounceClose(); + }, + handleFocus: function handleFocus() { +- this.focusing = true; +- this.show(); ++ if (!this.$slots.default || !this.$slots.default.length) { ++ this.focusing = true; ++ this.show(); ++ return; ++ } ++ var instance = this.$slots.default[0].componentInstance; ++ if (instance && instance.focus) { ++ instance.focus(); ++ } else { ++ this.focusing = true; ++ this.show(); ++ } + }, + handleBlur: function handleBlur() { + this.focusing = false; +@@ -21918,6 +21931,7 @@ main.directive = directive; + this.popperVM && this.popperVM.$destroy(); + }, + destroyed: function destroyed() { ++ this.popperVM && this.popperVM.$destroy(); + var reference = this.referenceElm; + if (reference.nodeType === 1) { + Object(dom_["off"])(reference, 'mouseenter', this.show); +diff --git a/packages/popover/src/main.vue b/packages/popover/src/main.vue +index ab5d060182c2e671989f1aba190c85a074d2c754..d87f1b592d7d408d185229b9f9e29070ec2c6fe8 100644 +--- a/packages/popover/src/main.vue ++++ b/packages/popover/src/main.vue +@@ -98,16 +98,9 @@ export default { + reference.setAttribute('aria-describedby', this.tooltipId); + reference.setAttribute('tabindex', this.tabindex); // tab序列 + popper.setAttribute('tabindex', 0); +- + if (this.trigger !== 'click') { +- on(reference, 'focusin', () => { +- this.handleFocus(); +- const instance = reference.__vue__; +- if (instance && typeof instance.focus === 'function') { +- instance.focus(); +- } +- }); +- on(popper, 'focusin', this.handleFocus); ++ on(reference, 'focusin', this.handleRefrenceFocus); ++ on(popper, 'focusin', this.handlePopperFocus); + on(reference, 'focusout', this.handleBlur); + on(popper, 'focusout', this.handleBlur); + } +@@ -154,7 +147,17 @@ export default { + doClose() { + this.showPopper = false; + }, +- handleFocus() { ++ handleRefrenceFocus() { ++ this.handlePopperFocus(); ++ if (!this.referenceElm) { ++ return; ++ } ++ const instance = this.referenceElm.__vue__; ++ if (instance && typeof instance.focus === 'function') { ++ instance.focus(); ++ } ++ }, ++ handlePopperFocus() { + addClass(this.referenceElm, 'focusing'); + if (this.trigger === 'click' || this.trigger === 'focus') this.showPopper = true; + }, +@@ -193,9 +196,8 @@ export default { + handleDocumentClick(e) { + let reference = this.reference || this.$refs.reference; + const popper = this.popper || this.$refs.popper; +- + if (!reference && this.$refs.wrapper.children) { +- reference = this.referenceElm = this.$refs.wrapper.children[0]; ++ reference = this.$refs.wrapper.children[0]; + } + if (!this.$el || + !reference || +@@ -218,20 +220,26 @@ export default { + } + } + }, +- + destroyed() { +- const reference = this.reference; +- ++ const reference = this.referenceElm; ++ const popper = this.popper || this.$refs.popper; ++ off(reference, 'focusin', this.handleRefrenceFocus); ++ off(popper, 'focusin', this.handlePopperFocus); ++ off(reference, 'focusout', this.handleBlur); ++ off(popper, 'focusout', this.handleBlur); ++ off(reference, 'keydown', this.handleKeydown); ++ off(reference, 'click', this.handleClick); + off(reference, 'click', this.doToggle); +- off(reference, 'mouseup', this.doClose); +- off(reference, 'mousedown', this.doShow); ++ off(document, 'click', this.handleDocumentClick); ++ off(reference, 'mouseenter', this.handleMouseEnter); ++ off(popper, 'mouseenter', this.handleMouseEnter); ++ off(reference, 'mouseleave', this.handleMouseLeave); ++ off(popper, 'mouseleave', this.handleMouseLeave); + off(reference, 'focusin', this.doShow); + off(reference, 'focusout', this.doClose); + off(reference, 'mousedown', this.doShow); + off(reference, 'mouseup', this.doClose); +- off(reference, 'mouseleave', this.handleMouseLeave); +- off(reference, 'mouseenter', this.handleMouseEnter); +- off(document, 'click', this.handleDocumentClick); ++ this.referenceElm = undefined; + } + }; + +\ No newline at end of file +diff --git a/packages/tooltip/src/main.js b/packages/tooltip/src/main.js +index dc930ec58d42328a4d62cddb22fd513db31793cf..d9a6afc80d27ea89b838a47f395e249131c1370c 100644 +--- a/packages/tooltip/src/main.js ++++ b/packages/tooltip/src/main.js +@@ -113,18 +113,7 @@ export default { + this.$el.setAttribute('tabindex', this.tabindex); + on(this.referenceElm, 'mouseenter', this.show); + on(this.referenceElm, 'mouseleave', this.hide); +- on(this.referenceElm, 'focus', () => { +- if (!this.$slots.default || !this.$slots.default.length) { +- this.handleFocus(); +- return; +- } +- const instance = this.$slots.default[0].componentInstance; +- if (instance && instance.focus) { +- instance.focus(); +- } else { +- this.handleFocus(); +- } +- }); ++ on(this.referenceElm, 'focus', this.handleFocus); + on(this.referenceElm, 'blur', this.handleBlur); + on(this.referenceElm, 'click', this.removeFocusing); + } +@@ -157,6 +146,18 @@ export default { + this.debounceClose(); + }, + handleFocus() { ++ if (!this.$slots.default || !this.$slots.default.length) { ++ this.doFocus(); ++ return; ++ } ++ const instance = this.$slots.default[0].componentInstance; ++ if (instance && instance.focus) { ++ instance.focus(); ++ } else { ++ this.doFocus(); ++ } ++ }, ++ doFocus() { + this.focusing = true; + this.show(); + }, \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 500a58fd7f..ad7e4de991 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ patchedDependencies: quill@2.0.0-dev.4: hash: wzpqp2wkmsfkgdbvlakyt3wndy path: patches/quill@2.0.0-dev.4.patch + element-ui@2.15.12: + hash: aaa3sc7bmwb4jwg35ga5npx4he + path: patches/element-ui@2.15.12.patch importers: