From f79757ce7eec16829c146bfc41d5cfa34e88afbc Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Mon, 20 Apr 2026 09:32:12 +0300 Subject: [PATCH 1/5] add grandchild node to seed data --- data/seedDb.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/data/seedDb.php b/data/seedDb.php index b9f2b31..d695258 100644 --- a/data/seedDb.php +++ b/data/seedDb.php @@ -20,6 +20,12 @@ $nodes = [ 'textId' => 0, 'parentNodeId' => 0, ], + [ + 'id' => 2, + 'title' => 'Page 1.1.1', + 'textId' => 0, + 'parentNodeId' => 1, + ], ]; $fileDataMap = [ From d26734facf09164b57e83f44ec52cc50ad25d031 Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Mon, 20 Apr 2026 09:32:28 +0300 Subject: [PATCH 2/5] test toggle children button visibility --- cypress/e2e/adminTextToggle.cy.js | 67 +++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 cypress/e2e/adminTextToggle.cy.js diff --git a/cypress/e2e/adminTextToggle.cy.js b/cypress/e2e/adminTextToggle.cy.js new file mode 100644 index 0000000..c75132b --- /dev/null +++ b/cypress/e2e/adminTextToggle.cy.js @@ -0,0 +1,67 @@ +describe('Toggle display of child nodes', () => { + beforeEach(() => { + cy.exec('npm run db:seed') + cy.intercept('GET', '/api/texts/0').as('getText') + cy.intercept('GET', '/api/nodes/0').as('getNodes') + cy.visit('/admin/texts/0') + cy.wait('@getText') + cy.wait('@getNodes') + }) + + afterEach(() => { + cy.exec('npm run db:wipe') + }) + + it('shows a toggle button on nodes that have children', () => { + cy.get('#text-detail > ul > li') + .first() + .find('button.toggle-children') + .should('exist') + }) + + it('does not show a toggle button on leaf nodes', () => { + cy.get('#text-detail > ul > li > ul > li > ul > li') + .first() + .find('button.toggle-children') + .should('not.exist') + }) + + it('root node children are visible on initial load', () => { + cy.get('#text-detail > ul > li > ul > li') + .first() + .should('be.visible') + }) + + it('grandchildren are hidden on initial load', () => { + cy.get('#text-detail > ul > li > ul > li > ul') + .first() + .should('not.be.visible') + }) + + it('clicking toggle button on a node shows its children', () => { + cy.get('#text-detail > ul > li > ul > li') + .first() + .find('button.toggle-children') + .click() + cy.get('#text-detail > ul > li > ul > li > ul') + .first() + .should('be.visible') + }) + + it('clicking toggle button again hides children', () => { + cy.get('#text-detail > ul > li > ul > li') + .first() + .find('button.toggle-children') + .click() + cy.get('#text-detail > ul > li > ul > li > ul') + .first() + .should('be.visible') + cy.get('#text-detail > ul > li > ul > li') + .first() + .find('button.toggle-children') + .click() + cy.get('#text-detail > ul > li > ul > li > ul') + .first() + .should('not.be.visible') + }) +}) From 12fd5a88b68f05197d0606a9bbadf2b033fb8499 Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Mon, 20 Apr 2026 09:35:53 +0300 Subject: [PATCH 3/5] add toggle children button to nodes --- public/js/text.js | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/public/js/text.js b/public/js/text.js index f017bd4..0388bf6 100644 --- a/public/js/text.js +++ b/public/js/text.js @@ -43,7 +43,7 @@ function buildTree(nodes) { return roots; } -function renderTree(nodes, textId) { +function renderTree(nodes, textId, depth = 0) { const ul = document.createElement('ul'); nodes.forEach(node => { const li = document.createElement('li'); @@ -61,11 +61,28 @@ function renderTree(nodes, textId) { const bulkBtn = document.createElement('button'); bulkBtn.textContent = 'Bulk add children'; bulkBtn.className = 'bulk-add-children'; - bulkBtn.addEventListener('click', () => toggleBulkAddForm(li, node.id, textId)); + bulkBtn.addEventListener( + 'click', + () => toggleBulkAddForm(li, node.id, textId) + ); li.appendChild(bulkBtn); if (node.children.length > 0) { - li.appendChild(renderTree(node.children, textId)); + const childUl = renderTree(node.children, textId, depth + 1); + const childrenVisible = depth === 0; + childUl.style.display = childrenVisible ? '' : 'none'; + + const toggleBtn = document.createElement('button'); + toggleBtn.className = 'toggle-children'; + toggleBtn.textContent = childrenVisible ? '▼' : '▶'; + toggleBtn.addEventListener('click', () => { + const isHidden = childUl.style.display === 'none'; + childUl.style.display = isHidden ? '' : 'none'; + toggleBtn.textContent = isHidden ? '▼' : '▶'; + }); + + li.appendChild(toggleBtn); + li.appendChild(childUl); } ul.appendChild(li); From 0d44d2c0901110b89c3a63e0d602364da11088c7 Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Mon, 20 Apr 2026 09:40:31 +0300 Subject: [PATCH 4/5] change seed data for better user comprehension --- data/seedDb.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/seedDb.php b/data/seedDb.php index d695258..3206372 100644 --- a/data/seedDb.php +++ b/data/seedDb.php @@ -3,26 +3,26 @@ $texts = [ [ 'id' => 0, - 'name' => 'test text', + 'name' => 'Tanach', ], ]; $nodes = [ [ 'id' => 0, - 'title' => 'Chapter 1', + 'title' => 'Tanach', 'textId' => 0, 'parentNodeId' => null, ], [ 'id' => 1, - 'title' => 'Section 1.1', + 'title' => 'Torah', 'textId' => 0, 'parentNodeId' => 0, ], [ 'id' => 2, - 'title' => 'Page 1.1.1', + 'title' => 'Bereishis', 'textId' => 0, 'parentNodeId' => 1, ], From 760f995fa0ec274ce02d8f9d4dd2ef1a1b03f97a Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Mon, 20 Apr 2026 09:41:14 +0300 Subject: [PATCH 5/5] add line break to echo of file name --- data/wipeDb.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/wipeDb.php b/data/wipeDb.php index c36b4af..4183d5b 100644 --- a/data/wipeDb.php +++ b/data/wipeDb.php @@ -6,7 +6,7 @@ $files = [ ]; foreach ($files as $file) { - echo __DIR__ . "/$file"; + echo __DIR__ . "/$file\n"; $path = __DIR__ . "/$file"; file_put_contents($path, json_encode([], JSON_PRETTY_PRINT)); }