From 13c4c0a1a034b86595cc32f3404d577a44de1a41 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 8 May 2026 09:05:13 +0200 Subject: [PATCH] destroy learning hub row editors --- public/js/learningHub.js | 35 ++++++++++++++++++++++++++------ test/learning-hub-editor.test.js | 16 +++++++++++++++ 2 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 test/learning-hub-editor.test.js diff --git a/public/js/learningHub.js b/public/js/learningHub.js index d011f00..f0a468c 100644 --- a/public/js/learningHub.js +++ b/public/js/learningHub.js @@ -117,7 +117,7 @@ // Remove question block var rmQ = e.target.closest('.lh-rm-question'); - if (rmQ) { var qb = rmQ.closest('.lh-question-block'); if (qb) qb.remove(); return; } + if (rmQ) { var qb = rmQ.closest('.lh-question-block'); if (qb) { destroyQuestionBlockEditors(qb); qb.remove(); } return; } // Add option to question var addOpt = e.target.closest('.lh-add-option'); @@ -129,7 +129,7 @@ // Remove option var rmOpt = e.target.closest('.lh-rm-option'); - if (rmOpt) { var row = rmOpt.closest('.lh-option-row'); if (row) row.remove(); return; } + if (rmOpt) { var row = rmOpt.closest('.lh-option-row'); if (row) { destroyOptionEditor(row); row.remove(); } return; } }); // wireSearch() called after component is in DOM @@ -876,7 +876,7 @@ // Fill questions if any were generated alongside slides var qContainerP = document.getElementById('lh-cms-questions'); if (qContainerP) { - qContainerP.innerHTML = ''; + clearQuestionBlocks(qContainerP); if (content.questions && Array.isArray(content.questions)) { content.questions.forEach(function(q) { addQuestionBlock(q); }); } @@ -898,7 +898,7 @@ // Replace questions var qContainer = document.getElementById('lh-cms-questions'); if (qContainer) { - qContainer.innerHTML = ''; + clearQuestionBlocks(qContainer); if (content.questions && Array.isArray(content.questions)) { content.questions.forEach(function(q) { addQuestionBlock(q); }); } @@ -1240,6 +1240,29 @@ return (html === '

' || html === '') ? '' : html; } + function destroyTpEditor(editor) { + if (editor && typeof editor.destroy === 'function') editor.destroy(); + } + + function destroyOptionEditor(row) { + if (!row || !row._optEditor) return; + destroyTpEditor(row._optEditor); + row._optEditor = null; + } + + function destroyQuestionBlockEditors(block) { + if (!block) return; + destroyTpEditor(block._questionEditor); + block._questionEditor = null; + block.querySelectorAll('.lh-option-row').forEach(destroyOptionEditor); + } + + function clearQuestionBlocks(container) { + if (!container) return; + container.querySelectorAll('.lh-question-block').forEach(destroyQuestionBlockEditors); + container.innerHTML = ''; + } + // ── Body editor ──────────────────────────────────────────── function initBodyEditor() { var wrap = document.getElementById('lh-body-editor'); @@ -1335,7 +1358,7 @@ document.getElementById('lh-cms-edit-category').value = ''; document.getElementById('lh-cms-edit-type').value = contentType || 'article'; document.getElementById('lh-cms-edit-published').value = 'false'; - document.getElementById('lh-cms-questions').innerHTML = ''; + clearQuestionBlocks(document.getElementById('lh-cms-questions')); document.getElementById('btn-lh-delete-content').classList.add('hidden'); hideDeleteConfirm(); toggleEditorMode(contentType || 'article'); @@ -1375,7 +1398,7 @@ // Load questions var qContainer = document.getElementById('lh-cms-questions'); - qContainer.innerHTML = ''; + clearQuestionBlocks(qContainer); if (c.questions && c.questions.length > 0) { c.questions.forEach(function(q) { addQuestionBlock(q); }); } diff --git a/test/learning-hub-editor.test.js b/test/learning-hub-editor.test.js new file mode 100644 index 0000000..703415d --- /dev/null +++ b/test/learning-hub-editor.test.js @@ -0,0 +1,16 @@ +const { test } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); + +const source = fs.readFileSync(path.join(__dirname, '..', 'public', 'js', 'learningHub.js'), 'utf8'); + +test('Learning Hub destroys embedded Tiptap editors before removing CMS rows', () => { + assert.match(source, /function destroyTpEditor\(editor\)/); + assert.match(source, /function destroyQuestionBlockEditors\(block\)/); + assert.match(source, /function clearQuestionBlocks\(container\)/); + assert.match(source, /destroyQuestionBlockEditors\(qb\); qb\.remove\(\)/); + assert.match(source, /destroyOptionEditor\(row\); row\.remove\(\)/); + assert.doesNotMatch(source, /document\.getElementById\('lh-cms-questions'\)\.innerHTML = ''/); + assert.doesNotMatch(source, /qContainer\.innerHTML = ''/); +});