diff --git a/cypress/integration/rendering/flowchart.spec.js b/cypress/integration/rendering/flowchart.spec.js index 0bae442e1..9e510c112 100644 --- a/cypress/integration/rendering/flowchart.spec.js +++ b/cypress/integration/rendering/flowchart.spec.js @@ -529,17 +529,19 @@ describe('Flowchart', () => { imgSnapshotTest( `graph TB TITLE["Link Click Events
(click the nodes below)"] - A[link test] - B[anchor test] - C[mailto test] - D[other protocol test] - E[script test] - TITLE --> A & B & C & D & E - click A "https://mermaid-js.github.io/mermaid/#/" "link test" - click B "#link-clicked" "anchor test" - click C "mailto:user@user.user" "mailto test" - click D "notes://do-your-thing/id" "other protocol test" - click E "javascript:alert('test')" "script test" + A["link test (open in same tab)"] + B["link test (open in new tab)"] + C[anchor test] + D[mailto test] + E[other protocol test] + F[script test] + TITLE --> A & B & C & D & E & F + click A "https://mermaid-js.github.io/mermaid/#/" "link test (open in same tab)" + click B "https://mermaid-js.github.io/mermaid/#/" "link test (open in new tab)" _blank + click C "#link-clicked" + click D "mailto:user@user.user" "mailto test" + click E "notes://do-your-thing/id" "other protocol test" + click F "javascript:alert('test')" "script test" `, { securityLevel: 'loose' } ); diff --git a/dist/index.html b/dist/index.html index f33151662..78b3ed8cb 100644 --- a/dist/index.html +++ b/dist/index.html @@ -393,17 +393,19 @@ graph TB
graph TB TITLE["Link Click Events
(click the nodes below)"] - A[link test] - B[anchor test] - C[mailto test] - D[other protocol test] - E[script test] - TITLE --> A & B & C & D & E - click A "https://mermaid-js.github.io/mermaid/#/" "link test" - click B "#link-clicked" "anchor test" - click C "mailto:user@user.user" "mailto test" - click D "notes://do-your-thing/id" "other protocol test" - click E "javascript:alert('test')" "script test" + A["link test (open in same tab)"] + B["link test (open in new tab)"] + C[anchor test] + D[mailto test] + E[other protocol test] + F[script test] + TITLE --> A & B & C & D & E & F + click A "https://mermaid-js.github.io/mermaid/#/" "link test (open in same tab)" + click B "https://mermaid-js.github.io/mermaid/#/" "link test (open in new tab)" _blank + click C "#link-clicked" + click D "mailto:user@user.user" "mailto test" + click E "notes://do-your-thing/id" "other protocol test" + click F "javascript:alert('test')" "script test"

diff --git a/docs/flowchart.md b/docs/flowchart.md index e00fbe1ff..115f2a24b 100644 --- a/docs/flowchart.md +++ b/docs/flowchart.md @@ -562,6 +562,23 @@ graph LR ?> Due to limitations with how Docsify handles JavaScript callback functions, an alternate working demo for the above code can be viewed at [this jsfiddle](https://jsfiddle.net/s37cjoau/3/). +Links are opened in the same browser tab/window by default. It is possible to change this by adding a link target to the click definition (`_self`, `_blank`, `_parent` and `_top` are supported): +``` +graph LR; + A-->B; + B-->C; + click A "http://www.github.com" _blank + click B "http://www.github.com" "Open this in a new tab" _blank +``` + +```mermaid +graph LR; + A-->B; + B-->C; + click A "http://www.github.com" _blank + click B "http://www.github.com" "Open this in a new tab" _blank +``` + Beginners tip, a full example using interactive links in a html context: ``` diff --git a/src/diagrams/flowchart/flowDb.js b/src/diagrams/flowchart/flowDb.js index c99325145..b18fedc6f 100644 --- a/src/diagrams/flowchart/flowDb.js +++ b/src/diagrams/flowchart/flowDb.js @@ -248,12 +248,13 @@ const setClickFun = function(_id, functionName) { * @param linkStr URL to create a link for * @param tooltip Tooltip for the clickable element */ -export const setLink = function(ids, linkStr, tooltip) { +export const setLink = function(ids, linkStr, tooltip, target) { ids.split(',').forEach(function(_id) { let id = _id; if (_id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id; if (typeof vertices[id] !== 'undefined') { vertices[id].link = utils.formatUrl(linkStr, config); + vertices[id].linkTarget = target; } }); setTooltip(ids, tooltip); diff --git a/src/diagrams/flowchart/flowRenderer-v2.js b/src/diagrams/flowchart/flowRenderer-v2.js index 1bd2db9de..a6a6bdb8e 100644 --- a/src/diagrams/flowchart/flowRenderer-v2.js +++ b/src/diagrams/flowchart/flowRenderer-v2.js @@ -486,6 +486,9 @@ export const draw = function(text, id) { link.setAttributeNS('http://www.w3.org/2000/svg', 'class', vertex.classes.join(' ')); link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link); link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener'); + if (vertex.linkTarget) { + link.setAttributeNS('http://www.w3.org/2000/svg', 'target', vertex.linkTarget); + } const linkNode = node.insert(function() { return link; diff --git a/src/diagrams/flowchart/flowRenderer.js b/src/diagrams/flowchart/flowRenderer.js index 51ef4edd5..390809d6c 100644 --- a/src/diagrams/flowchart/flowRenderer.js +++ b/src/diagrams/flowchart/flowRenderer.js @@ -469,6 +469,9 @@ export const draw = function(text, id) { link.setAttributeNS('http://www.w3.org/2000/svg', 'class', vertex.classes.join(' ')); link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link); link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener'); + if (vertex.linkTarget) { + link.setAttributeNS('http://www.w3.org/2000/svg', 'target', vertex.linkTarget); + } const linkNode = node.insert(function() { return link; diff --git a/src/diagrams/flowchart/parser/flow-interactions.spec.js b/src/diagrams/flowchart/parser/flow-interactions.spec.js index 1ddb710a8..9255b9ce9 100644 --- a/src/diagrams/flowchart/parser/flow-interactions.spec.js +++ b/src/diagrams/flowchart/parser/flow-interactions.spec.js @@ -39,7 +39,7 @@ describe('[Interactions] when parsing', () => { const vert = flow.parser.yy.getVertices(); const edges = flow.parser.yy.getEdges(); - expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html', undefined); + expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html', undefined, undefined); }); it('should handle interaction - click to a link with tooltip', function() { @@ -49,6 +49,26 @@ describe('[Interactions] when parsing', () => { const vert = flow.parser.yy.getVertices(); const edges = flow.parser.yy.getEdges(); - expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html', 'tooltip'); + expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html', 'tooltip', undefined); + }); + + it('should handle interaction - click to a link with target', function() { + spyOn(flowDb, 'setLink'); + const res = flow.parser.parse('graph TD\nA-->B\nclick A "click.html" _blank'); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + + expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html', undefined, '_blank'); + }); + + it('should handle interaction - click to a link with tooltip and target', function() { + spyOn(flowDb, 'setLink'); + const res = flow.parser.parse('graph TD\nA-->B\nclick A "click.html" "tooltip" _blank'); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + + expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html', 'tooltip', '_blank'); }); }); diff --git a/src/diagrams/flowchart/parser/flow.jison b/src/diagrams/flowchart/parser/flow.jison index 6cd7c6af8..0f553d34f 100644 --- a/src/diagrams/flowchart/parser/flow.jison +++ b/src/diagrams/flowchart/parser/flow.jison @@ -25,6 +25,10 @@ "flowchart" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';} "subgraph" return 'subgraph'; "end"\b\s* return 'end'; +"_self" return 'LINK_TARGET'; +"_blank" return 'LINK_TARGET'; +"_parent" return 'LINK_TARGET'; +"_top" return 'LINK_TARGET'; \s*"LR" { this.popState(); return 'DIR'; } \s*"RL" { this.popState(); return 'DIR'; } \s*"TB" { this.popState(); return 'DIR'; } @@ -397,10 +401,12 @@ classStatement:CLASS SPACE alphaNum SPACE alphaNum ; clickStatement - : CLICK SPACE alphaNum SPACE alphaNum {$$ = $1;yy.setClickEvent($3, $5, undefined);} - | CLICK SPACE alphaNum SPACE alphaNum SPACE STR {$$ = $1;yy.setClickEvent($3, $5, $7) ;} - | CLICK SPACE alphaNum SPACE STR {$$ = $1;yy.setLink($3, $5, undefined);} - | CLICK SPACE alphaNum SPACE STR SPACE STR {$$ = $1;yy.setLink($3, $5, $7 );} + : CLICK SPACE alphaNum SPACE alphaNum {$$ = $1;yy.setClickEvent($3, $5, undefined);} + | CLICK SPACE alphaNum SPACE alphaNum SPACE STR {$$ = $1;yy.setClickEvent($3, $5, $7) ;} + | CLICK SPACE alphaNum SPACE STR {$$ = $1;yy.setLink($3, $5, undefined, undefined);} + | CLICK SPACE alphaNum SPACE STR SPACE STR {$$ = $1;yy.setLink($3, $5, $7, undefined );} + | CLICK SPACE alphaNum SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setLink($3, $5, undefined, $7 );} + | CLICK SPACE alphaNum SPACE STR SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setLink($3, $5, $7, $9 );} ; styleStatement:STYLE SPACE alphaNum SPACE stylesOpt