diff --git a/src/components/color/index.js b/src/components/color/index.js index fe22aca72aa..35ac2a0552e 100644 --- a/src/components/color/index.js +++ b/src/components/color/index.js @@ -57,6 +57,16 @@ color.combine = function(front, back) { return tinycolor(fcflat).toRgbString(); }; +color.contrast = function(cstr, lightAmount, darkAmount) { + var tc = tinycolor(cstr); + + var newColor = tc.isLight() ? + tc.darken(darkAmount) : + tc.lighten(lightAmount); + + return newColor.toString(); +}; + color.stroke = function(s, c) { var tc = tinycolor(c); s.style({'stroke': color.tinyRGB(tc), 'stroke-opacity': tc.getAlpha()}); diff --git a/src/components/rangeselector/attributes.js b/src/components/rangeselector/attributes.js index c0c58429bd9..4c4bb88128f 100644 --- a/src/components/rangeselector/attributes.js +++ b/src/components/rangeselector/attributes.js @@ -81,6 +81,11 @@ module.exports = { role: 'style', description: 'Sets the background color of the range selector buttons.' }, + activecolor: { + valType: 'color', + role: 'style', + description: 'Sets the background color of the active range selector button.' + }, bordercolor: { valType: 'color', dflt: colorAttrs.defaultLine, diff --git a/src/components/rangeselector/constants.js b/src/components/rangeselector/constants.js index e598ab45d69..6e2818a9c3c 100644 --- a/src/components/rangeselector/constants.js +++ b/src/components/rangeselector/constants.js @@ -21,6 +21,7 @@ module.exports = { rx: 3, ry: 3, - // color given to active and hovered buttons - activeColor: '#d3d3d3' + // light fraction used to compute the 'activecolor' default + lightAmount: 25, + darkAmount: 10 }; diff --git a/src/components/rangeselector/defaults.js b/src/components/rangeselector/defaults.js index 108b888c8eb..c616d41d261 100644 --- a/src/components/rangeselector/defaults.js +++ b/src/components/rangeselector/defaults.js @@ -9,6 +9,7 @@ 'use strict'; var Lib = require('../../lib'); +var Color = require('../color'); var attributes = require('./attributes'); var buttonAttrs = require('./button_attributes'); @@ -38,7 +39,8 @@ module.exports = function rangeSelectorDefaults(containerIn, containerOut, layou Lib.coerceFont(coerce, 'font', layout.font); - coerce('bgcolor'); + var bgColor = coerce('bgcolor'); + coerce('activecolor', Color.contrast(bgColor, constants.lightAmount, constants.darkAmount)); coerce('bordercolor'); coerce('borderwidth'); }; diff --git a/src/components/rangeselector/draw.js b/src/components/rangeselector/draw.js index 2eef35a05c6..53c499781cd 100644 --- a/src/components/rangeselector/draw.js +++ b/src/components/rangeselector/draw.js @@ -142,7 +142,7 @@ function drawButtonRect(button, selectorLayout, d) { function getFillColor(selectorLayout, d) { return (d.isActive || d.isHovered) ? - constants.activeColor : + selectorLayout.activecolor : selectorLayout.bgcolor; } diff --git a/test/image/mocks/range_selector_style.json b/test/image/mocks/range_selector_style.json index cbb3bc50a91..101620a4ed0 100644 --- a/test/image/mocks/range_selector_style.json +++ b/test/image/mocks/range_selector_style.json @@ -1025,6 +1025,7 @@ "y": 0.2, "yanchor": "bottom", "bgcolor": "#d3d3d3", + "activecolor": "#d3d3d3", "borderwidth": 2, "bordercolor": "#ff7f0e" }, diff --git a/test/jasmine/tests/color_test.js b/test/jasmine/tests/color_test.js index aef74e24a78..3f776337bcb 100644 --- a/test/jasmine/tests/color_test.js +++ b/test/jasmine/tests/color_test.js @@ -175,4 +175,25 @@ describe('Test color:', function() { }); }); + + describe('contrast', function() { + + it('should darken light colors', function() { + var out = Color.contrast('#eee', 10, 20); + + expect(out).toEqual('#bbbbbb'); + }); + + it('should darken light colors (2)', function() { + var out = Color.contrast('#fdae61', 10, 20); + + expect(out).toEqual('#f57a03'); + }); + + it('should lighten dark colors', function() { + var out = Color.contrast('#2b83ba', 10, 20); + + expect(out).toEqual('#449dd4'); + }); + }); }); diff --git a/test/jasmine/tests/range_selector_test.js b/test/jasmine/tests/range_selector_test.js index c48a3e36f0f..a88b05738a2 100644 --- a/test/jasmine/tests/range_selector_test.js +++ b/test/jasmine/tests/range_selector_test.js @@ -1,6 +1,5 @@ var RangeSelector = require('@src/components/rangeselector'); var getUpdateObject = require('@src/components/rangeselector/get_update_object'); -var constants = require('@src/components/rangeselector/constants'); var d3 = require('d3'); var Plotly = require('@lib'); @@ -427,6 +426,16 @@ describe('range selector interactions:', function() { }); } + function checkButtonColor(bgColor, activeColor) { + d3.selectAll('.button').each(function(d) { + var rect = d3.select(this).select('rect'); + + expect(rect.style('fill')).toEqual( + d.isActive ? activeColor : bgColor + ); + }); + } + it('should display the correct nodes', function() { assertNodeCount('.rangeselector', 1); assertNodeCount('.button', mockCopy.layout.xaxis.rangeselector.buttons.length); @@ -457,6 +466,22 @@ describe('range selector interactions:', function() { }); }); + it('should be able to change its style on `relayout`', function(done) { + var prefix = 'xaxis.rangeselector.'; + + checkButtonColor('rgb(238, 238, 238)', 'rgb(212, 212, 212)'); + + Plotly.relayout(gd, prefix + 'bgcolor', 'red').then(function() { + checkButtonColor('rgb(255, 0, 0)', 'rgb(255, 128, 128)'); + + return Plotly.relayout(gd, prefix + 'activecolor', 'blue'); + }).then(function() { + checkButtonColor('rgb(255, 0, 0)', 'rgb(0, 0, 255)'); + + done(); + }); + }); + it('should update range and active button when clicked', function() { var range0 = gd.layout.xaxis.range[0]; var buttons = d3.selectAll('.button').select('rect'); @@ -482,7 +507,7 @@ describe('range selector interactions:', function() { var pos = getRectCenter(button.node()); var fillColor = Color.rgb(gd._fullLayout.xaxis.rangeselector.bgcolor); - var activeColor = Color.rgb(constants.activeColor); + var activeColor = 'rgb(212, 212, 212)'; expect(button.style('fill')).toEqual(fillColor);