From b55f9c0bd0ea9f912cf141e4ec05f72c2e533727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Tusz?= Date: Wed, 27 Apr 2016 19:11:26 -0400 Subject: [PATCH 1/4] Legend: fix drag movement when `editable: true` The legend container is now a `` and position must be set using `translate` instead of `x` and `y` coordinates. The cursor is set to always be `move`. --- src/components/legend/draw.js | 42 ++++++++++++++++------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index f39935acf26..9c1a9ae5d9c 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -13,7 +13,6 @@ var d3 = require('d3'); var Plotly = require('../../plotly'); var Lib = require('../../lib'); -var setCursor = require('../../lib/setcursor'); var Plots = require('../../plots/plots'); var dragElement = require('../dragelement'); var Drawing = require('../drawing'); @@ -326,38 +325,35 @@ module.exports = function draw(gd) { } if(gd._context.editable) { - var xf, - yf, - x0, - y0, - lw, - lh; + var xf, yf, x0, y0; + + legend.classed('cursor-move', true); dragElement.init({ element: legend.node(), prepFn: function() { - x0 = Number(legend.attr('x')); - y0 = Number(legend.attr('y')); - lw = Number(legend.attr('width')); - lh = Number(legend.attr('height')); - setCursor(legend); + // regex pattern for 'translate(123.45px, 543.21px)' + var re = /(.*\()(\d*\.?\d*)([^\d]*)(\d*\.?\d*)([^\d]*)/, + transform = legend.attr('transform') + .replace(re, function(match, p1, p2, p3, p4) { + return [p2, p4].join(' '); + }) + .split(' '); + + x0 = +transform[0] || 0; + y0 = +transform[1] || 0; }, moveFn: function(dx, dy) { - var gs = gd._fullLayout._size; - - legend.call(Drawing.setPosition, x0+dx, y0+dy); + var newX = x0 + dx, + newY = y0 + dy; - xf = dragElement.align(x0+dx, lw, gs.l, gs.l+gs.w, - opts.xanchor); - yf = dragElement.align(y0+dy+lh, -lh, gs.t+gs.h, gs.t, - opts.yanchor); + var transform = 'translate(' + newX + ', ' + newY + ')'; + legend.attr('transform', transform); - var csr = dragElement.getCursor(xf, yf, - opts.xanchor, opts.yanchor); - setCursor(legend, csr); + xf = dragElement.align(newX, 0, gs.l, gs.l+gs.w, opts.xanchor); + yf = dragElement.align(newY, 0, gs.t+gs.h, gs.t, opts.yanchor); }, doneFn: function(dragged) { - setCursor(legend); if(dragged && xf !== undefined && yf !== undefined) { Plotly.relayout(gd, {'legend.x': xf, 'legend.y': yf}); } From 0cf82fed02a0786d62a13e318eeaed5ff7a35a73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Tusz?= Date: Thu, 28 Apr 2016 17:52:23 -0400 Subject: [PATCH 2/4] Lib: Add getTranslate and setTranslate methods and tests --- src/lib/index.js | 36 ++++++++++++++ test/jasmine/tests/lib_test.js | 87 ++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/src/lib/index.js b/src/lib/index.js index 8f2235928cd..0a6b8f839bc 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -430,6 +430,42 @@ lib.addStyleRule = function(selector, styleString) { else console.warn('addStyleRule failed'); }; +lib.getTranslate = function(element) { + + var re = /(\btranslate\()(\d*\.?\d*)([^\d]*)(\d*\.?\d*)([^\d]*)(.*)/, + getter = element.attr ? 'attr' : 'getAttribute', + transform = element[getter]('transform') || ''; + + var translate = transform.replace(re, function(match, p1, p2, p3, p4) { + return [p2, p4].join(' '); + }) + .split(' '); + + return { + x: +translate[0] || 0, + y: +translate[1] || 0 + }; +}; + +lib.setTranslate = function(element, x, y) { + + var re = /(\btranslate\(.*?\);?)/, + getter = element.attr ? 'attr' : 'getAttribute', + setter = element.attr ? 'attr' : 'setAttribute', + transform = element[getter]('transform') || ''; + + x = x || 0; + y = y || 0; + + transform = transform.replace(re, '').trim(); + transform += ' translate(' + x + ', ' + y + ')'; + transform = transform.trim(); + + element[setter]('transform', transform); + + return transform; +}; + lib.isIE = function() { return typeof window.navigator.msSaveBlob !== 'undefined'; }; diff --git a/test/jasmine/tests/lib_test.js b/test/jasmine/tests/lib_test.js index 3b11c1868d9..065c7da8f31 100644 --- a/test/jasmine/tests/lib_test.js +++ b/test/jasmine/tests/lib_test.js @@ -770,4 +770,91 @@ describe('Test lib.js:', function() { }); }); + fdescribe('getTranslate', function() { + + it('should work with regular DOM elements', function() { + var el = document.createElement('div'); + + expect(Lib.getTranslate(el)).toEqual({ x: 0, y: 0 }); + + el.setAttribute('transform', 'translate(123.45px, 67)'); + expect(Lib.getTranslate(el)).toEqual({ x: 123.45, y: 67 }); + + el.setAttribute('transform', 'translate(123.45)'); + expect(Lib.getTranslate(el)).toEqual({ x: 123.45, y: 0 }); + + el.setAttribute('transform', 'translate(1 2)'); + expect(Lib.getTranslate(el)).toEqual({ x: 1, y: 2 }); + + el.setAttribute('transform', 'translate(1 2); rotate(20deg)'); + expect(Lib.getTranslate(el)).toEqual({ x: 1, y: 2 }); + + el.setAttribute('transform', 'rotate(20deg)'); + expect(Lib.getTranslate(el)).toEqual({ x: 0, y: 0 }); + }); + + it('should work with d3 elements', function() { + var el = d3.select(document.createElement('div')); + + el.attr('transform', 'translate(123.45px, 67)'); + expect(Lib.getTranslate(el)).toEqual({ x: 123.45, y: 67 }); + + el.attr('transform', 'translate(123.45)'); + expect(Lib.getTranslate(el)).toEqual({ x: 123.45, y: 0 }); + + el.attr('transform', 'translate(1 2)'); + expect(Lib.getTranslate(el)).toEqual({ x: 1, y: 2 }); + + el.attr('transform', 'translate(1 2); rotate(20)'); + expect(Lib.getTranslate(el)).toEqual({ x: 1, y: 2 }); + + el.attr('transform', 'rotate(20)'); + expect(Lib.getTranslate(el)).toEqual({ x: 0, y: 0 }); + }); + + }); + + fdescribe('setTranslate', function() { + + it('should work with regular DOM elements', function() { + var el = document.createElement('div'); + + Lib.setTranslate(el, 5); + expect(el.getAttribute('transform')).toBe('translate(5, 0)'); + + Lib.setTranslate(el, 10, 20); + expect(el.getAttribute('transform')).toBe('translate(10, 20)'); + + Lib.setTranslate(el, 30, 40); + expect(el.getAttribute('transform')).toBe('translate(30, 40)'); + + Lib.setTranslate(el); + expect(el.getAttribute('transform')).toBe('translate(0, 0)'); + + el.setAttribute('transform', 'translate(0, 0); rotate(30)'); + Lib.setTranslate(el, 30, 40); + expect(el.getAttribute('transform')).toBe('rotate(30) translate(30, 40)'); + }); + + it('should work with d3 elements', function() { + var el = d3.select(document.createElement('div')); + + Lib.setTranslate(el, 5); + expect(el.attr('transform')).toBe('translate(5, 0)'); + + Lib.setTranslate(el, 10, 20); + expect(el.attr('transform')).toBe('translate(10, 20)'); + + Lib.setTranslate(el, 30, 40); + expect(el.attr('transform')).toBe('translate(30, 40)'); + + Lib.setTranslate(el); + expect(el.attr('transform')).toBe('translate(0, 0)'); + + el.attr('transform', 'translate(0, 0); rotate(30)'); + Lib.setTranslate(el, 30, 40); + expect(el.attr('transform')).toBe('rotate(30) translate(30, 40)'); + }); + }); + }); From 7269c339c8a0fc638cf7e19c8a021be5fdf00ee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Tusz?= Date: Thu, 28 Apr 2016 17:52:52 -0400 Subject: [PATCH 3/4] Legend: Switch to using Lib.getTranslate --- src/components/legend/draw.js | 14 ++++---------- test/jasmine/tests/lib_test.js | 4 ++-- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index 9c1a9ae5d9c..26641cff7bd 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -332,16 +332,10 @@ module.exports = function draw(gd) { dragElement.init({ element: legend.node(), prepFn: function() { - // regex pattern for 'translate(123.45px, 543.21px)' - var re = /(.*\()(\d*\.?\d*)([^\d]*)(\d*\.?\d*)([^\d]*)/, - transform = legend.attr('transform') - .replace(re, function(match, p1, p2, p3, p4) { - return [p2, p4].join(' '); - }) - .split(' '); - - x0 = +transform[0] || 0; - y0 = +transform[1] || 0; + var transform = Lib.getTranslate(legend); + + x0 = transform.x; + y0 = transform.y; }, moveFn: function(dx, dy) { var newX = x0 + dx, diff --git a/test/jasmine/tests/lib_test.js b/test/jasmine/tests/lib_test.js index 065c7da8f31..110cc948b91 100644 --- a/test/jasmine/tests/lib_test.js +++ b/test/jasmine/tests/lib_test.js @@ -770,7 +770,7 @@ describe('Test lib.js:', function() { }); }); - fdescribe('getTranslate', function() { + describe('getTranslate', function() { it('should work with regular DOM elements', function() { var el = document.createElement('div'); @@ -814,7 +814,7 @@ describe('Test lib.js:', function() { }); - fdescribe('setTranslate', function() { + describe('setTranslate', function() { it('should work with regular DOM elements', function() { var el = document.createElement('div'); From ee0b45273c240421243f9ba16c7aa7113d64e462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Tusz?= Date: Fri, 29 Apr 2016 14:34:27 -0400 Subject: [PATCH 4/4] Tests: add tests for when editable --- test/jasmine/tests/config_test.js | 87 ++++++++++++++++++++++++++++--- 1 file changed, 80 insertions(+), 7 deletions(-) diff --git a/test/jasmine/tests/config_test.js b/test/jasmine/tests/config_test.js index b2d3e9d8d1b..a72fcb28e71 100644 --- a/test/jasmine/tests/config_test.js +++ b/test/jasmine/tests/config_test.js @@ -1,19 +1,20 @@ var Plotly = require('@lib/index'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); +var mouseEvent = require('../assets/mouse_event'); describe('config argument', function() { - var gd; + describe('showLink attribute', function() { - beforeEach(function(done) { - gd = createGraphDiv(); - done(); - }); + var gd; - afterEach(destroyGraphDiv); + beforeEach(function(done) { + gd = createGraphDiv(); + done(); + }); - describe('showLink attribute', function() { + afterEach(destroyGraphDiv); it('should not display the edit link by default', function() { Plotly.plot(gd, [], {}); @@ -39,4 +40,76 @@ describe('config argument', function() { expect(bBox.height).toBeGreaterThan(0); }); }); + + + describe('editable attribute', function() { + + var gd; + + beforeEach(function(done) { + gd = createGraphDiv(); + + Plotly.plot(gd, [ + { x: [1,2,3], y: [1,2,3] }, + { x: [1,2,3], y: [3,2,1] } + ], { + width: 600, + height: 400 + }, { editable: true }) + .then(done); + }); + + afterEach(destroyGraphDiv); + + function checkIfEditable(elClass, text) { + var label = document.getElementsByClassName(elClass)[0]; + + expect(label.textContent).toBe(text); + + var labelBox = label.getBoundingClientRect(), + labelX = labelBox.left + labelBox.width / 2, + labelY = labelBox.top + labelBox.height / 2; + + mouseEvent('click', labelX, labelY); + + var editBox = document.getElementsByClassName('plugin-editable editable')[0]; + expect(editBox).toBeDefined(); + expect(editBox.textContent).toBe(text); + expect(editBox.getAttribute('contenteditable')).toBe('true'); + } + + it('should make titles editable', function() { + checkIfEditable('gtitle', 'Click to enter Plot title'); + }); + + it('should make x axes labels editable', function() { + checkIfEditable('g-xtitle', 'Click to enter X axis title'); + }); + + it('should make y axes labels editable', function() { + checkIfEditable('g-ytitle', 'Click to enter Y axis title'); + }); + + it('should make legend labels editable', function() { + checkIfEditable('legendtext', 'trace 0'); + }); + + it('should make legends draggable', function() { + + var legend = document.getElementsByClassName('legend')[0], + legendBox = legend.getBoundingClientRect(), + legendX = legendBox.left + legendBox.width / 2, + legendY = legendBox.top + legendBox.height / 2; + + mouseEvent('mousedown', legendX, legendY); + mouseEvent('mousemove', legendX - 20, legendY + 20); + mouseEvent('mouseup', legendX - 20, legendY + 20); + + var movedlegendBox = legend.getBoundingClientRect(); + + expect(movedlegendBox.left).not.toBe(legendBox.left); + expect(movedlegendBox.top).not.toBe(legendBox.top); + + }); + }); });