diff --git a/src/components/rangeslider/range_plot.js b/src/components/rangeslider/range_plot.js
index 39dc6e1aa5d..b70f5dbab5f 100644
--- a/src/components/rangeslider/range_plot.js
+++ b/src/components/rangeslider/range_plot.js
@@ -8,6 +8,8 @@
'use strict';
+var d3 = require('d3');
+
var Symbols = require('../drawing/symbol_defs');
var Drawing = require('../drawing');
@@ -38,7 +40,7 @@ module.exports = function rangePlot(gd, w, h) {
clipDefs.appendChild(clip);
var rangePlot = document.createElementNS(svgNS, 'g');
- rangePlot.setAttribute('clip-path', 'url(#range-clip-path)');
+ d3.select(rangePlot).call(Drawing.setClipUrl, 'range-clip-path');
rangePlot.appendChild(clipDefs);
diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js
index dc6940341e0..6ed76ab6270 100644
--- a/src/plot_api/plot_api.js
+++ b/src/plot_api/plot_api.js
@@ -2913,10 +2913,8 @@ function lsInner(gd) {
});
- plotinfo.plot.attr({
- 'transform': 'translate(' + xa._offset + ', ' + ya._offset + ')',
- 'clip-path': 'url(#' + clipId + ')'
- });
+ plotinfo.plot.call(Lib.setTranslate, xa._offset, ya._offset);
+ plotinfo.plot.call(Drawing.setClipUrl, clipId);
var xlw = Drawing.crispRound(gd, xa.linewidth, 1),
ylw = Drawing.crispRound(gd, ya.linewidth, 1),
diff --git a/test/jasmine/tests/drawing_test.js b/test/jasmine/tests/drawing_test.js
new file mode 100644
index 00000000000..0394715de5e
--- /dev/null
+++ b/test/jasmine/tests/drawing_test.js
@@ -0,0 +1,52 @@
+var Drawing = require('@src/components/drawing');
+
+var d3 = require('d3');
+
+
+describe('Drawing.setClipUrl', function() {
+ 'use strict';
+
+ beforeEach(function() {
+ this.svg = d3.select('body').append('svg');
+ this.g = this.svg.append('g');
+ });
+
+ afterEach(function() {
+ this.svg.remove();
+ this.g.remove();
+ });
+
+ it('should set the clip-path attribute', function() {
+ expect(this.g.attr('clip-path')).toBe(null);
+
+ Drawing.setClipUrl(this.g, 'id1');
+
+ expect(this.g.attr('clip-path')).toEqual('url(#id1)');
+ });
+
+ it('should unset the clip-path if arg is falsy', function() {
+ this.g.attr('clip-path', 'url(#id2)');
+
+ Drawing.setClipUrl(this.g, false);
+
+ expect(this.g.attr('clip-path')).toBe(null);
+ });
+
+ it('should append window URL to clip-path if is present', function() {
+
+ // append with href
+ var base = d3.select('body')
+ .append('base')
+ .attr('href', 'https://plot.ly');
+
+ // grab window URL
+ var href = window.location.href;
+
+ Drawing.setClipUrl(this.g, 'id3');
+
+ expect(this.g.attr('clip-path'))
+ .toEqual('url(' + href + '#id3)');
+
+ base.remove();
+ });
+});
diff --git a/test/jasmine/tests/plot_interact_test.js b/test/jasmine/tests/plot_interact_test.js
index 8748b9d9bb2..9ca7f2b3f2f 100644
--- a/test/jasmine/tests/plot_interact_test.js
+++ b/test/jasmine/tests/plot_interact_test.js
@@ -436,3 +436,73 @@ describe('Test plot structure', function() {
});
});
});
+
+describe('plot svg clip paths', function() {
+
+ // plot with all features that rely on clip paths
+ function plot() {
+ return Plotly.plot(createGraphDiv(), [{
+ type: 'contour',
+ z: [[1,2,3], [2,3,1]]
+ }, {
+ type: 'scatter',
+ y: [2, 1, 2]
+ }], {
+ showlegend: true,
+ xaxis: {
+ rangeslider: {}
+ },
+ shapes: [{
+ xref: 'x',
+ yref: 'y',
+ x0: 0,
+ y0: 0,
+ x1: 3,
+ y1: 3
+ }]
+ });
+ }
+
+ afterEach(destroyGraphDiv);
+
+ it('should set clip path url to ids (base case)', function(done) {
+ plot().then(function() {
+
+ d3.selectAll('[clip-path]').each(function() {
+ var cp = d3.select(this).attr('clip-path');
+
+ expect(cp.substring(0, 5)).toEqual('url(#');
+ expect(cp.substring(cp.length - 1)).toEqual(')');
+ });
+
+ done();
+ });
+ });
+
+ it('should set clip path url to ids appended to window url', function(done) {
+
+ // this case occurs in some past versions of AngularJS
+ // https://github.com/angular/angular.js/issues/8934
+
+ // append with href
+ var base = d3.select('body')
+ .append('base')
+ .attr('href', 'https://plot.ly');
+
+ // grab window URL
+ var href = window.location.href;
+
+ plot().then(function() {
+
+ d3.selectAll('[clip-path]').each(function() {
+ var cp = d3.select(this).attr('clip-path');
+
+ expect(cp.substring(0, 5 + href.length)).toEqual('url(' + href + '#');
+ expect(cp.substring(cp.length - 1)).toEqual(')');
+ });
+
+ base.remove();
+ done();
+ });
+ });
+});