diff --git a/superset/assets/javascripts/explore/components/controls/ColorPickerControl.jsx b/superset/assets/javascripts/explore/components/controls/ColorPickerControl.jsx
new file mode 100644
index 000000000000..ecccc8e33ccc
--- /dev/null
+++ b/superset/assets/javascripts/explore/components/controls/ColorPickerControl.jsx
@@ -0,0 +1,92 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { OverlayTrigger, Popover } from 'react-bootstrap';
+import { SketchPicker } from 'react-color';
+
+import ControlHeader from '../ControlHeader';
+import { bnbColors } from '../../../modules/colors';
+
+const propTypes = {
+ onChange: PropTypes.func,
+ value: PropTypes.object,
+};
+
+const defaultProps = {
+ onChange: () => {},
+};
+
+const swatchCommon = {
+ position: 'absolute',
+ width: '50px',
+ height: '20px',
+ top: '0px',
+ left: '0px',
+ right: '0px',
+ bottom: '0px',
+};
+
+const styles = {
+ swatch: {
+ width: '50px',
+ height: '20px',
+ position: 'relative',
+ padding: '5px',
+ borderRadius: '1px',
+ display: 'inline-block',
+ cursor: 'pointer',
+ boxShadow: 'rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset, rgba(0, 0, 0, 0.25) 0px 0px 4px inset',
+ },
+ color: {
+ ...swatchCommon,
+ borderRadius: '2px',
+ },
+ checkboard: {
+ ...swatchCommon,
+ background: 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMUlEQVQ4T2NkYGAQYcAP3uCTZhw1gGGYhAGBZIA/nYDCgBDAm9BGDWAAJyRCgLaBCAAgXwixzAS0pgAAAABJRU5ErkJggg==") left center',
+ },
+};
+export default class ColorPickerControl extends React.Component {
+ constructor(props) {
+ super(props);
+ this.onChange = this.onChange.bind(this);
+ }
+ onChange(col) {
+ this.props.onChange(col.rgb);
+ }
+ renderPopover() {
+ return (
+
+ i < 7)}
+ />
+ );
+ }
+ render() {
+ const c = this.props.value || { r: 0, g: 0, b: 0, a: 0 };
+ const colStyle = Object.assign(
+ {}, styles.color, { background: `rgba(${c.r}, ${c.g}, ${c.b}, ${c.a})` });
+ return (
+
+ );
+ }
+}
+
+ColorPickerControl.propTypes = propTypes;
+ColorPickerControl.defaultProps = defaultProps;
diff --git a/superset/assets/javascripts/explore/components/controls/index.js b/superset/assets/javascripts/explore/components/controls/index.js
index e2840fbc52bb..876bc4a1c631 100644
--- a/superset/assets/javascripts/explore/components/controls/index.js
+++ b/superset/assets/javascripts/explore/components/controls/index.js
@@ -1,6 +1,7 @@
import BoundsControl from './BoundsControl';
import CheckboxControl from './CheckboxControl';
import CollectionControl from './CollectionControl';
+import ColorPickerControl from './ColorPickerControl';
import ColorSchemeControl from './ColorSchemeControl';
import DatasourceControl from './DatasourceControl';
import DateFilterControl from './DateFilterControl';
@@ -17,6 +18,7 @@ const controlMap = {
BoundsControl,
CheckboxControl,
CollectionControl,
+ ColorPickerControl,
ColorSchemeControl,
DatasourceControl,
DateFilterControl,
diff --git a/superset/assets/javascripts/explore/main.css b/superset/assets/javascripts/explore/main.css
index bc67249e75bd..a6afe5eba935 100644
--- a/superset/assets/javascripts/explore/main.css
+++ b/superset/assets/javascripts/explore/main.css
@@ -113,3 +113,11 @@
.list-group {
margin-bottom: 10px;
}
+.color-popover.popover {
+ border: none;
+ background-color: transparent;
+}
+.color-popover .popover-content {
+ padding: 0;
+ background-color: transparent;
+}
diff --git a/superset/assets/javascripts/explore/stores/controls.jsx b/superset/assets/javascripts/explore/stores/controls.jsx
index d2851ee7247e..5107360475ea 100644
--- a/superset/assets/javascripts/explore/stores/controls.jsx
+++ b/superset/assets/javascripts/explore/stores/controls.jsx
@@ -1322,6 +1322,12 @@ export const controls = {
description: t('The color for points and clusters in RGB'),
},
+ color: {
+ type: 'ColorPickerControl',
+ label: t('Color'),
+ description: t('Pick a color'),
+ },
+
ranges: {
type: 'TextControl',
label: t('Ranges'),
diff --git a/superset/assets/javascripts/modules/colors.js b/superset/assets/javascripts/modules/colors.js
index 803b6836d6b2..663fd6e4947c 100644
--- a/superset/assets/javascripts/modules/colors.js
+++ b/superset/assets/javascripts/modules/colors.js
@@ -3,7 +3,7 @@ import d3 from 'd3';
export const brandColor = '#00A699';
// Color related utility functions go in this object
-const bnbColors = [
+export const bnbColors = [
'#ff5a5f', // rausch
'#7b0051', // hackb
'#007A87', // kazan
diff --git a/superset/assets/package.json b/superset/assets/package.json
index 3dfdb78240f9..9ae69dccb491 100644
--- a/superset/assets/package.json
+++ b/superset/assets/package.json
@@ -71,8 +71,9 @@
"react-alert": "^2.3.0",
"react-bootstrap": "^0.31.2",
"react-bootstrap-table": "^4.0.2",
- "react-dom": "^15.6.2",
+ "react-color": "^2.13.8",
"react-datetime": "2.9.0",
+ "react-dom": "^15.6.2",
"react-gravatar": "^2.6.1",
"react-grid-layout": "^0.14.4",
"react-map-gl": "^3.0.4",
diff --git a/superset/assets/spec/javascripts/explore/components/ColorPickerControl_spec.jsx b/superset/assets/spec/javascripts/explore/components/ColorPickerControl_spec.jsx
new file mode 100644
index 000000000000..9ce2f478645f
--- /dev/null
+++ b/superset/assets/spec/javascripts/explore/components/ColorPickerControl_spec.jsx
@@ -0,0 +1,41 @@
+/* eslint-disable no-unused-expressions */
+import React from 'react';
+import { expect } from 'chai';
+import { describe, it, beforeEach } from 'mocha';
+import { shallow } from 'enzyme';
+import { OverlayTrigger } from 'react-bootstrap';
+import { SketchPicker } from 'react-color';
+
+import ColorPickerControl from
+ '../../../../javascripts/explore/components/controls/ColorPickerControl';
+import ControlHeader from '../../../../javascripts/explore/components/ControlHeader';
+
+const defaultProps = {
+ value: { },
+};
+
+describe('ColorPickerControl', () => {
+ let wrapper;
+ let inst;
+ beforeEach(() => {
+ wrapper = shallow();
+ inst = wrapper.instance();
+ });
+
+ it('renders a OverlayTrigger', () => {
+ const controlHeader = wrapper.find(ControlHeader);
+ expect(controlHeader).to.have.lengthOf(1);
+ expect(wrapper.find(OverlayTrigger)).to.have.length(1);
+ });
+
+ it('renders a OverlayTrigger', () => {
+ const controlHeader = wrapper.find(ControlHeader);
+ expect(controlHeader).to.have.lengthOf(1);
+ expect(wrapper.find(OverlayTrigger)).to.have.length(1);
+ });
+
+ it('renders a Popover with a SketchPicker', () => {
+ const popOver = shallow(inst.renderPopover());
+ expect(popOver.find(SketchPicker)).to.have.lengthOf(1);
+ });
+});
diff --git a/superset/assets/spec/javascripts/explore/components/ColorScheme_spec.jsx b/superset/assets/spec/javascripts/explore/components/ColorScheme_spec.jsx
new file mode 100644
index 000000000000..1f5ee69f6c96
--- /dev/null
+++ b/superset/assets/spec/javascripts/explore/components/ColorScheme_spec.jsx
@@ -0,0 +1,25 @@
+/* eslint-disable no-unused-expressions */
+import React from 'react';
+import { expect } from 'chai';
+import { describe, it, beforeEach } from 'mocha';
+import { mount } from 'enzyme';
+import { Creatable } from 'react-select';
+
+import ColorSchemeControl from
+ '../../../../javascripts/explore/components/controls/ColorSchemeControl';
+import { ALL_COLOR_SCHEMES } from '../../../../javascripts/modules/colors';
+
+const defaultProps = {
+ options: Object.keys(ALL_COLOR_SCHEMES).map(s => ([s, s])),
+};
+
+describe('ColorSchemeControl', () => {
+ let wrapper;
+ beforeEach(() => {
+ wrapper = mount();
+ });
+
+ it('renders a Creatable', () => {
+ expect(wrapper.find(Creatable)).to.have.length(1);
+ });
+});