Skip to content

Commit

Permalink
Merge branch 'airbnb-master' into john-bodley-cherry-pick-5684
Browse files Browse the repository at this point in the history
  • Loading branch information
john-bodley authored Aug 22, 2018
2 parents 918f3a2 + a82bae0 commit ed8f1cf
Show file tree
Hide file tree
Showing 9 changed files with 292 additions and 185 deletions.
2 changes: 1 addition & 1 deletion superset/assets/src/SqlLab/components/SqlEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ class SqlEditor extends React.PureComponent {
{ctasControls}
<span className="m-l-5">
<Hotkeys
header="Hotkeys"
header="Keyboard shortcuts"
hotkeys={hotkeys}
/>
</span>
Expand Down
9 changes: 8 additions & 1 deletion superset/assets/src/explore/controls.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,7 @@ export const controls = {
...metric,
label: t('Color Metric'),
default: null,
validators: [],
description: t('A metric to use for color'),
},
select_country: {
Expand Down Expand Up @@ -787,6 +788,7 @@ export const controls = {

link_length: {
type: 'SelectControl',
renderTrigger: true,
freeForm: true,
label: t('Link Length'),
default: '200',
Expand All @@ -796,6 +798,7 @@ export const controls = {

charge: {
type: 'SelectControl',
renderTrigger: true,
freeForm: true,
label: t('Charge'),
default: '-500',
Expand Down Expand Up @@ -1249,7 +1252,8 @@ export const controls = {
type: 'SelectControl',
label: t('Rotation'),
choices: formatSelectOptions(['random', 'flat', 'square']),
default: 'random',
renderTrigger: true,
default: 'square',
description: t('Rotation to apply to words in the cloud'),
},

Expand Down Expand Up @@ -1310,6 +1314,7 @@ export const controls = {
type: 'TextControl',
isInt: true,
label: t('Font Size From'),
renderTrigger: true,
default: '20',
description: t('Font size for the smallest value in the list'),
},
Expand All @@ -1318,6 +1323,7 @@ export const controls = {
type: 'TextControl',
isInt: true,
label: t('Font Size To'),
renderTrigger: true,
default: '150',
description: t('Font size for the biggest value in the list'),
},
Expand Down Expand Up @@ -1394,6 +1400,7 @@ export const controls = {
type: 'CheckboxControl',
label: t('Data Table'),
default: false,
renderTrigger: true,
description: t('Whether to display the interactive data table'),
},

Expand Down
9 changes: 6 additions & 3 deletions superset/assets/src/explore/visTypes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -1037,9 +1037,9 @@ export const visTypes = {
label: t('Query'),
expanded: true,
controlSetRows: [
['series'],
['metric'],
['adhoc_filters'],
['series'],
['row_limit', null],
],
},
Expand Down Expand Up @@ -1393,7 +1393,7 @@ export const visTypes = {
},

directed_force: {
label: t('Directed Force Layout'),
label: t('Force-directed Graph'),
controlPanelSections: [
{
label: t('Query'),
Expand Down Expand Up @@ -1508,6 +1508,7 @@ export const visTypes = {
['country_fieldtype'],
['metric'],
['adhoc_filters'],
['row_limit'],
],
},
{
Expand Down Expand Up @@ -1587,13 +1588,15 @@ export const visTypes = {
['metrics'],
['secondary_metric'],
['adhoc_filters'],
['limit'],
['limit', 'row_limit'],
],
},
{
label: t('Options'),
expanded: true,
controlSetRows: [
['show_datatable', 'include_series'],
['linear_color_scheme'],
],
},
],
Expand Down
126 changes: 80 additions & 46 deletions superset/assets/src/visualizations/chord.jsx
Original file line number Diff line number Diff line change
@@ -1,75 +1,93 @@
/* eslint-disable no-param-reassign */
import d3 from 'd3';
import PropTypes from 'prop-types';
import { getColorFromScheme } from '../modules/colors';
import './chord.css';

function chordViz(slice, json) {
slice.container.html('');

const div = d3.select(slice.selector);
const nodes = json.data.nodes;
const fd = slice.formData;
const f = d3.format(fd.y_axis_format);

const width = slice.width();
const height = slice.height();
const propTypes = {
data: PropTypes.shape({
nodes: PropTypes.arrayOf(PropTypes.string),
matrix: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number)),
}),
width: PropTypes.number,
height: PropTypes.number,
numberFormat: PropTypes.string,
colorScheme: PropTypes.string,
};

function chordVis(element, props) {
PropTypes.checkPropTypes(propTypes, props, 'prop', 'ChordVis');

const {
data,
width,
height,
numberFormat,
colorScheme,
} = props;

element.innerHTML = '';

const div = d3.select(element);
const { nodes, matrix } = data;
const f = d3.format(numberFormat);

const outerRadius = Math.min(width, height) / 2 - 10;
const innerRadius = outerRadius - 24;

let chord;

const arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
.innerRadius(innerRadius)
.outerRadius(outerRadius);

const layout = d3.layout.chord()
.padding(0.04)
.sortSubgroups(d3.descending)
.sortChords(d3.descending);
.padding(0.04)
.sortSubgroups(d3.descending)
.sortChords(d3.descending);

const path = d3.svg.chord()
.radius(innerRadius);
.radius(innerRadius);

const svg = div.append('svg')
.attr('width', width)
.attr('height', height)
.on('mouseout', () => chord.classed('fade', false))
.append('g')
.attr('id', 'circle')
.attr('transform', `translate(${width / 2}, ${height / 2})`);
.attr('width', width)
.attr('height', height)
.on('mouseout', () => chord.classed('fade', false))
.append('g')
.attr('id', 'circle')
.attr('transform', `translate(${width / 2}, ${height / 2})`);

svg.append('circle')
.attr('r', outerRadius);
.attr('r', outerRadius);

// Compute the chord layout.
layout.matrix(json.data.matrix);
layout.matrix(matrix);

const group = svg.selectAll('.group')
.data(layout.groups)
.enter().append('g')
.attr('class', 'group')
.on('mouseover', (d, i) => {
chord.classed('fade', p => p.source.index !== i && p.target.index !== i);
});
.data(layout.groups)
.enter().append('g')
.attr('class', 'group')
.on('mouseover', (d, i) => {
chord.classed('fade', p => p.source.index !== i && p.target.index !== i);
});

// Add a mouseover title.
group.append('title').text((d, i) => `${nodes[i]}: ${f(d.value)}`);

// Add the group arc.
const groupPath = group.append('path')
.attr('id', (d, i) => 'group' + i)
.attr('d', arc)
.style('fill', (d, i) => getColorFromScheme(nodes[i], slice.formData.color_scheme));
.attr('id', (d, i) => 'group' + i)
.attr('d', arc)
.style('fill', (d, i) => getColorFromScheme(nodes[i], colorScheme));

// Add a text label.
const groupText = group.append('text')
.attr('x', 6)
.attr('dy', 15);
.attr('x', 6)
.attr('dy', 15);

groupText.append('textPath')
.attr('xlink:href', (d, i) => `#group${i}`)
.text((d, i) => nodes[i]);
.attr('xlink:href', (d, i) => `#group${i}`)
.text((d, i) => nodes[i]);
// Remove the labels that don't fit. :(
groupText.filter(function (d, i) {
return groupPath[0][i].getTotalLength() / 2 - 16 < this.getComputedTextLength();
Expand All @@ -78,14 +96,14 @@ function chordViz(slice, json) {

// Add the chords.
chord = svg.selectAll('.chord')
.data(layout.chords)
.enter().append('path')
.attr('class', 'chord')
.on('mouseover', (d) => {
chord.classed('fade', p => p !== d);
})
.style('fill', d => getColorFromScheme(nodes[d.source.index], slice.formData.color_scheme))
.attr('d', path);
.data(layout.chords)
.enter().append('path')
.attr('class', 'chord')
.on('mouseover', (d) => {
chord.classed('fade', p => p !== d);
})
.style('fill', d => getColorFromScheme(nodes[d.source.index], colorScheme))
.attr('d', path);

// Add an elaborate mouseover title for each chord.
chord.append('title').text(function (d) {
Expand All @@ -98,4 +116,20 @@ function chordViz(slice, json) {
});
}

module.exports = chordViz;
chordVis.propTypes = propTypes;

function adaptor(slice, payload) {
const { selector, formData } = slice;
const { y_axis_format: numberFormat, color_scheme: colorScheme } = formData;
const element = document.querySelector(selector);

return chordVis(element, {
data: payload.data,
width: slice.width(),
height: slice.height(),
numberFormat,
colorScheme,
});
}

export default adaptor;
Loading

0 comments on commit ed8f1cf

Please sign in to comment.