From 296bfa0148bfa1b844d49becade6b25b4734b494 Mon Sep 17 00:00:00 2001 From: Myllaume Date: Sun, 15 Sep 2024 09:46:43 +0200 Subject: [PATCH 1/6] doc: nodemon watch css files --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 076a37a..2e9c234 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ npm i # install dependences + build JS files ```bash npm run watch:front # build web browser script npm run watch:back # build NodeJs executable file -nodemon --ext js,cjs --watch dist/ --exec "sh e2e/exec-modelize.sh" # make cosmoscope files for dev or E2E testing +nodemon --ext css,js,cjs --watch dist/ --watch static/ --exec "sh e2e/exec-modelize.sh" # make cosmoscope files for dev or E2E testing ``` ## E2E testing From 8b79081992f5855ba156f0789c6c64e8fffb59d1 Mon Sep 17 00:00:00 2001 From: Myllaume Date: Sun, 15 Sep 2024 10:07:21 +0200 Subject: [PATCH 2/6] refa: remove useless highlight labels functions --- core/frontend/graph.js | 51 ------------------------------------------ 1 file changed, 51 deletions(-) diff --git a/core/frontend/graph.js b/core/frontend/graph.js index edbad7d..dfe032e 100644 --- a/core/frontend/graph.js +++ b/core/frontend/graph.js @@ -552,25 +552,6 @@ window.labelDisplayToggle = function (isChecked) { } }; -/** - * Add 'highlight' class to texts linked to nodes ids - * @param {string[]|number[]} nodeIds - List of node ids - */ - -window.labelHighlight = function (nodeIds) { - elts.nodes.data().map((node) => { - if (nodeIds.includes(node.id)) { - node.highlighted = true; - } - return node; - }); - - elts.nodes - .filter((node) => nodeIds.includes(node.id)) - .select('text') - .style('fill', 'var(--highlight)'); -}; - /** * Change the font size of graph labels */ @@ -579,38 +560,6 @@ window.updateFontsize = function () { elts.labels.attr('font-size', graphProperties.text_size); }; -/** - * Remove 'highlight' class from texts linked to nodes ids - * @param {string[]|number[]} nodeIds - List of node ids - */ - -window.labelUnlight = function (nodeIds) { - elts.nodes.data().map((node) => { - if (nodeIds.includes(node.id)) { - node.highlighted = false; - } - return node; - }); - - elts.nodes - .filter((node) => nodeIds.includes(node.id)) - .select('text') - .style('fill', null); -}; - -/** - * Remove 'highlight' class from all texts - */ - -window.labelUnlightAll = function () { - data.nodes = data.nodes.map(function (node) { - node.highlighted = false; - return node; - }); - - elts.labels.style('fill', null); -}; - function translate() { const minX = d3.min(data.nodes, (d) => d.x); const maxX = d3.max(data.nodes, (d) => d.x); From 07b79a89c7cbad7fdd5eeb068cdd3f357a215cc8 Mon Sep 17 00:00:00 2001 From: Myllaume Date: Sun, 15 Sep 2024 10:12:09 +0200 Subject: [PATCH 3/6] refa: graph elements effect by class --- core/frontend/graph.js | 45 +++++++++++++++++++++------------------- static/styles/styles.css | 22 ++++++++++++++++++-- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/core/frontend/graph.js b/core/frontend/graph.js index dfe032e..2561862 100644 --- a/core/frontend/graph.js +++ b/core/frontend/graph.js @@ -108,6 +108,7 @@ elts.links = svgSub .append('line') .attr('stroke', (d) => d.color) .attr('title', (d) => d.title) + .attr('data-link', (d) => d.id) .attr('data-source', (d) => d.source.id) .attr('data-target', (d) => d.target.id) .attr('stroke-dasharray', function (d) { @@ -193,10 +194,10 @@ elts.nodes = svgSub return true; }); - nodesHovered.selectAll('.border').style('fill', 'var(--highlight)'); - linksHovered.style('stroke', 'var(--highlight)'); - nodesToModif.attr('opacity', 0.5); - linksToModif.attr('stroke-opacity', 0.5); + nodesHovered.nodes().forEach((elt) => elt.classList.add('highlight')); + linksHovered.nodes().forEach((elt) => elt.classList.add('highlight')); + nodesToModif.nodes().forEach((elt) => elt.classList.add('translucent')); + linksToModif.nodes().forEach((elt) => elt.classList.add('translucent')); }) .on('mouseout', () => { if (!graphProperties.graph_highlight_on_hover) { @@ -212,8 +213,8 @@ elts.nodes = svgSub } return true; }) - .selectAll('.border') - .style('fill', null); + .nodes() + .forEach((elt) => elt.classList.remove('highlight')); elts.links .filter((link) => { if (selectedNodeId) { @@ -221,9 +222,10 @@ elts.nodes = svgSub } return true; }) - .style('stroke', null); - elts.nodes.attr('opacity', null); - elts.links.attr('stroke-opacity', null); + .nodes() + .forEach((elt) => elt.classList.remove('highlight')); + elts.nodes.nodes().forEach((elt) => elt.classList.remove('translucent')); + elts.links.nodes().forEach((elt) => elt.classList.remove('translucent')); }); /** Draw node content */ @@ -297,6 +299,7 @@ elts.nodes.each(function (d) { /** @type {d3.Selection} */ elts.labels = elts.nodes .append('text') + .attr('class', 'label') .each(function (d) { const words = d.label.split(' '), max = 25, @@ -479,8 +482,8 @@ function hideNodesAll(priority = filterPriority.notFiltered) { window.hideNodeNetwork = function (nodeIds) { const { nodes, links } = getNodeNetwork(nodeIds); - nodes.style('display', 'none'); - links.style('display', 'none'); + nodes.nodes().forEach((elt) => elt.classList.add('hide')); + links.nodes().forEach((elt) => elt.classList.add('hide')); }; /** @@ -491,8 +494,8 @@ window.hideNodeNetwork = function (nodeIds) { window.displayNodeNetwork = function (nodeIds) { const { nodes, links } = getNodeNetwork(nodeIds); - nodes.style('display', null); - links.style('display', null); + nodes.nodes().forEach((elt) => elt.classList.remove('hide')); + links.nodes().forEach((elt) => elt.classList.remove('hide')); }; /** @@ -503,8 +506,8 @@ window.displayNodeNetwork = function (nodeIds) { function highlightNodes(nodeIds) { const { nodes, links } = getNodeNetwork(nodeIds); - nodes.selectAll('.border').style('fill', 'var(--highlight)'); - links.style('stroke', 'var(--highlight)'); + nodes.nodes().forEach((elt) => elt.classList.add('highlight')); + links.nodes().forEach((elt) => elt.classList.add('highlight')); View.highlightedNodes = View.highlightedNodes.concat(nodeIds); } @@ -520,8 +523,8 @@ function unlightNodes() { const { nodes, links } = getNodeNetwork(View.highlightedNodes); - nodes.selectAll('.border').style('fill', null); - links.style('stroke', null); + nodes.nodes().forEach((elt) => elt.classList.remove('highlight')); + links.nodes().forEach((elt) => elt.classList.remove('highlight')); View.highlightedNodes = []; } @@ -533,9 +536,9 @@ function unlightNodes() { window.linksDisplayToggle = function (isChecked) { if (isChecked) { - elts.links.style('display', null); + elts.links.nodes().forEach((elt) => elt.classList.remove('hide')); } else { - elts.links.style('display', 'none'); + elts.links.nodes().forEach((elt) => elt.classList.add('hide')); } }; @@ -546,9 +549,9 @@ window.linksDisplayToggle = function (isChecked) { window.labelDisplayToggle = function (isChecked) { if (isChecked) { - elts.labels.style('display', null); + elts.labels.nodes().forEach((elt) => elt.classList.remove('hide')); } else { - elts.labels.style('display', 'none'); + elts.labels.nodes().forEach((elt) => elt.classList.add('hide')); } }; diff --git a/static/styles/styles.css b/static/styles/styles.css index e6328bf..7292c5f 100644 --- a/static/styles/styles.css +++ b/static/styles/styles.css @@ -670,9 +670,27 @@ a.record-link.highlight { - Graph elements --- */ +[data-node] a.highlight .border { + fill: var(--highlight); +} + +[data-link].highlight { + stroke: var(--highlight); +} + +[data-node] a.translucent, +[data-link].translucent { + opacity: 0.5; +} + +[data-node] a.hide, +[data-link].hide, +[data-node] .label.hide { + visibility: hidden; +} + /* nodes */ -circle, -text { +[data-node] { cursor: pointer; } From d602305f51a99ed4680b110d69fe85cf1a85389c Mon Sep 17 00:00:00 2001 From: Myllaume Date: Sun, 15 Sep 2024 11:07:04 +0200 Subject: [PATCH 4/6] doc: nodemon watch nkj files --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e9c234..5384105 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ npm i # install dependences + build JS files ```bash npm run watch:front # build web browser script npm run watch:back # build NodeJs executable file -nodemon --ext css,js,cjs --watch dist/ --watch static/ --exec "sh e2e/exec-modelize.sh" # make cosmoscope files for dev or E2E testing +nodemon --ext css,njk,js,cjs --watch dist/ --watch static/ --exec "sh e2e/exec-modelize.sh" # make cosmoscope files for dev or E2E testing ``` ## E2E testing From b538639a90cdc694053268adaabbd5de6440d05b Mon Sep 17 00:00:00 2001 From: Myllaume Date: Sun, 15 Sep 2024 11:33:31 +0200 Subject: [PATCH 5/6] fix: node element is group, not anchor --- core/frontend/graph.js | 20 ++++++++++---------- static/styles/styles.css | 6 +++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/frontend/graph.js b/core/frontend/graph.js index 2561862..4b4cbca 100644 --- a/core/frontend/graph.js +++ b/core/frontend/graph.js @@ -138,8 +138,6 @@ elts.nodes = svgSub .enter() .append('g') .attr('data-node', (d) => d.id) - .append('a') - .attr('href', (d) => '#' + d.id) .call( d3 .drag() @@ -232,6 +230,7 @@ elts.nodes = svgSub elts.nodes.each(function (d) { const node = d3.select(this); + const link = node.append('a').attr('href', (d) => '#' + d.id); const getFill = (fill) => { if (imageFileValidExtnames.has(fill.split('.').at(-1))) { @@ -249,13 +248,13 @@ elts.nodes.each(function (d) { const drawSimpleCircle = (stroke, fill) => { /** Background: color of type */ - node + link .append('circle') .attr('class', 'border') .attr('r', d.size + 2) .attr('fill', stroke); /** Foreground: circle contains color or image */ - node.append('circle').attr('r', d.size).attr('fill', fill); + link.append('circle').attr('r', d.size).attr('fill', fill); }; if (d.thumbnail) { @@ -267,13 +266,13 @@ elts.nodes.each(function (d) { ({ border }, i) => { const type = graphProperties['record_types'][d.types[i]]; /** Background: borders with one color per type */ - node.append('path').attr('d', border).attr('fill', type.stroke).attr('class', 'border'); + link.append('path').attr('d', border).attr('fill', type.stroke).attr('class', 'border'); }, ); /** Background: neutral white color */ - node.append('circle').attr('r', d.size).attr('fill', `var(--background-gray)`); + link.append('circle').attr('r', d.size).attr('fill', `var(--background-gray)`); /** Foreground: circle contains thumbnail */ - node.append('circle').attr('r', d.size).attr('fill', `url(#${d.thumbnail})`); + link.append('circle').attr('r', d.size).attr('fill', `url(#${d.thumbnail})`); } return; } @@ -286,11 +285,11 @@ elts.nodes.each(function (d) { ({ segment, border }, i) => { const type = graphProperties['record_types'][d.types[i]]; /** Background: borders with one color per type */ - node.append('path').attr('d', border).attr('fill', type.stroke).attr('class', 'border'); + link.append('path').attr('d', border).attr('fill', type.stroke).attr('class', 'border'); /** Background: neutral white color */ - node.append('path').attr('d', segment).attr('fill', 'var(--background-gray)'); + link.append('path').attr('d', segment).attr('fill', 'var(--background-gray)'); /** Foreground: circle fragment per type with color or image */ - node.append('path').attr('d', segment).attr('fill', getFill(type.fill)); + link.append('path').attr('d', segment).attr('fill', getFill(type.fill)); }, ); } @@ -298,6 +297,7 @@ elts.nodes.each(function (d) { /** @type {d3.Selection} */ elts.labels = elts.nodes + .select('a') .append('text') .attr('class', 'label') .each(function (d) { diff --git a/static/styles/styles.css b/static/styles/styles.css index 7292c5f..0f08692 100644 --- a/static/styles/styles.css +++ b/static/styles/styles.css @@ -670,7 +670,7 @@ a.record-link.highlight { - Graph elements --- */ -[data-node] a.highlight .border { +[data-node].highlight .border { fill: var(--highlight); } @@ -678,12 +678,12 @@ a.record-link.highlight { stroke: var(--highlight); } -[data-node] a.translucent, +[data-node].translucent, [data-link].translucent { opacity: 0.5; } -[data-node] a.hide, +[data-node].hide, [data-link].hide, [data-node] .label.hide { visibility: hidden; From 25d01d05507f2d469096d9374645cfc3975cdd0e Mon Sep 17 00:00:00 2001 From: Myllaume Date: Sun, 15 Sep 2024 11:34:04 +0200 Subject: [PATCH 6/6] test: graph element highlight/translucent --- e2e/graph.cy.js | 64 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/e2e/graph.cy.js b/e2e/graph.cy.js index afc2165..7f66e9e 100644 --- a/e2e/graph.cy.js +++ b/e2e/graph.cy.js @@ -51,10 +51,10 @@ describe('graph', () => { cy.get('@node').click(); cy.get('[data-source="evergreen notes"]') .should('have.length', 1) - .each((elt) => expect(elt.attr('style')).to.contain('highlight')); + .each((elt) => expect(elt.attr('class')).to.contain('highlight')); cy.get('[data-target="evergreen notes"]') .should('have.length', 3) - .each((elt) => expect(elt.attr('style')).to.contain('highlight')); + .each((elt) => expect(elt.attr('class')).to.contain('highlight')); }); it('should apply link shape', () => { @@ -81,13 +81,61 @@ describe('graph', () => { cy.get('text:visible').should('have.length', 0); }); + it('should change label size', () => { + cy.contains('Paramètres du graphe').click(); + cy.get('form:contains("taille du texte")').as('form'); + + cy.get('[data-node] text').should('have.attr', 'font-size', 7); + + cy.get('@form').find('input[type="number"]').invoke('val', 8).trigger('input'); + cy.get('[data-node] text').should('have.attr', 'font-size', 8); + + cy.get('@form').find('input[type="range"]').invoke('val', 10).trigger('input'); + cy.get('[data-node] text').should('have.attr', 'font-size', 10); + }); + + it('should highlight selected node, hovered node and connected nodes, links', () => { + cy.get('[data-node], [data-link]').should('not.have.class', 'highlight'); + + cy.get('[data-node="evergreen notes should be densely linked"]').click(); + + cy.get('[data-node="evergreen notes"]').trigger('mouseover'); + + const highlightNodes = [ + 'evergreen notes', + 'tools for thought', + 'evergreen notes should be atomic', + 'evergreen note titles are like apis', + 'evergreen notes should be concept-oriented', + 'evergreen notes should be densely linked', + ]; + + cy.get('[data-node].highlight').should('have.length', highlightNodes.length); + highlightNodes.forEach((name) => + cy.get(`[data-node="${name}"]`).should('have.class', 'highlight'), + ); + + cy.get('[data-link].highlight').should('have.length', 5); + }); + describe('change node opacity', () => { - it('should on no connected nodes to hovered node', () => { + it('should for no connected nodes to hovered node', () => { + cy.get('[data-node], [data-link]').should('not.have.class', 'translucent'); + cy.get('[data-node="evergreen notes"]').trigger('mouseover'); - ['matuschak2019', 'engelbart1962', 'evergreen notes should be densely linked'].forEach( - (name) => cy.get(`[data-node="${name}"]`).find('a').should('have.attr', 'opacity', '0.5'), + const translucentNodes = [ + 'matuschak2019', + 'engelbart1962', + 'evergreen notes should be densely linked', + ]; + + cy.get('[data-node].translucent').should('have.length', translucentNodes.length); + translucentNodes.forEach((name) => + cy.get(`[data-node="${name}"]`).should('have.class', 'translucent'), ); + + cy.get('[data-link].translucent').should('have.length', 3); }); it('should not if option is unactivated', () => { @@ -98,11 +146,11 @@ describe('graph', () => { cy.get('@option').click(); cy.get('@option').find('input').should('not.have.checked'); + cy.get('[data-node], [data-link]').should('not.have.class', 'translucent'); + cy.get('[data-node="evergreen notes"]').trigger('mouseover'); - ['matuschak2019', 'engelbart1962', 'evergreen notes should be densely linked'].forEach( - (name) => cy.get(`[data-node="${name}"]`).find('a').should('not.have.attr', 'opacity'), - ); + cy.get('[data-node], [data-link]').should('not.have.class', 'translucent'); }); }); });